web-mojo 2.2.101 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +603 -0
- package/README.md +2 -2
- package/dist/admin-models.cjs.js +2 -0
- package/dist/admin-models.cjs.js.map +1 -0
- package/dist/admin-models.es.js +2 -0
- package/dist/admin-models.es.js.map +1 -0
- package/dist/admin.cjs.js +1 -1
- package/dist/admin.css +42 -0
- package/dist/admin.es.js +1 -1
- package/dist/auth.cjs.js +1 -1
- package/dist/auth.cjs.js.map +1 -1
- package/dist/auth.es.js +1 -1
- package/dist/auth.es.js.map +1 -1
- package/dist/charts.cjs.js +1 -1
- package/dist/charts.cjs.js.map +1 -1
- package/dist/charts.css +897 -1
- package/dist/charts.es.js +1 -1
- package/dist/charts.es.js.map +1 -1
- package/dist/chat.css +96 -0
- package/dist/chunks/AssistantPanelView-BG34Qbfj.js +2 -0
- package/dist/chunks/AssistantPanelView-BG34Qbfj.js.map +1 -0
- package/dist/chunks/AssistantPanelView-DCEV6VeI.js +2 -0
- package/dist/chunks/AssistantPanelView-DCEV6VeI.js.map +1 -0
- package/dist/chunks/ChatView-B73uox2v.js +2 -0
- package/dist/chunks/ChatView-B73uox2v.js.map +1 -0
- package/dist/chunks/ChatView-W8daOwIo.js +2 -0
- package/dist/chunks/ChatView-W8daOwIo.js.map +1 -0
- package/dist/chunks/Collection-BZlmtcuL.js +2 -0
- package/dist/chunks/Collection-BZlmtcuL.js.map +1 -0
- package/dist/chunks/Collection-Bwoq6muu.js +2 -0
- package/dist/chunks/Collection-Bwoq6muu.js.map +1 -0
- package/dist/chunks/ContextMenu-BPPtuqKk.js +2 -0
- package/dist/chunks/ContextMenu-BPPtuqKk.js.map +1 -0
- package/dist/chunks/ContextMenu-q76hjQb6.js +2 -0
- package/dist/chunks/ContextMenu-q76hjQb6.js.map +1 -0
- package/dist/chunks/DataView-BbrwHMV4.js +2 -0
- package/dist/chunks/{DataView-BFx2glFg.js.map → DataView-BbrwHMV4.js.map} +1 -1
- package/dist/chunks/DataView-k-7wmk5_.js +2 -0
- package/dist/chunks/{DataView-D5C_lDdg.js.map → DataView-k-7wmk5_.js.map} +1 -1
- package/dist/chunks/FormView-DPSuwWMq.js +3 -0
- package/dist/chunks/{FormView-e-PeRx1s.js.map → FormView-DPSuwWMq.js.map} +1 -1
- package/dist/chunks/FormView-Dcy7XOtC.js +3 -0
- package/dist/chunks/{FormView-Cu4iPfvU.js.map → FormView-Dcy7XOtC.js.map} +1 -1
- package/dist/chunks/ListView-DHC-yBIw.js +2 -0
- package/dist/chunks/ListView-DHC-yBIw.js.map +1 -0
- package/dist/chunks/ListView-iGBsD4a7.js +2 -0
- package/dist/chunks/ListView-iGBsD4a7.js.map +1 -0
- package/dist/chunks/MetricsCountryMapView-CAD9wR_T.js +2 -0
- package/dist/chunks/MetricsCountryMapView-CAD9wR_T.js.map +1 -0
- package/dist/chunks/MetricsCountryMapView-Dzk3Yrzx.js +2 -0
- package/dist/chunks/MetricsCountryMapView-Dzk3Yrzx.js.map +1 -0
- package/dist/chunks/Modal-DBJU16cc.js +3 -0
- package/dist/chunks/Modal-DBJU16cc.js.map +1 -0
- package/dist/chunks/Modal-DuULCMFZ.js +3 -0
- package/dist/chunks/Modal-DuULCMFZ.js.map +1 -0
- package/dist/chunks/Passkeys-CGRZ8ZMv.js +2 -0
- package/dist/chunks/{Passkeys-DfVHrRPY.js.map → Passkeys-CGRZ8ZMv.js.map} +1 -1
- package/dist/chunks/Passkeys-Dr8-oSm9.js +2 -0
- package/dist/chunks/{Passkeys-Bj-ufmei.js.map → Passkeys-Dr8-oSm9.js.map} +1 -1
- package/dist/chunks/TokenManager-CcQFvaFD.js +2 -0
- package/dist/chunks/TokenManager-CcQFvaFD.js.map +1 -0
- package/dist/chunks/TokenManager-DEWZqbuo.js +2 -0
- package/dist/chunks/TokenManager-DEWZqbuo.js.map +1 -0
- package/dist/chunks/User-DNQhdBtI.js +2 -0
- package/dist/chunks/User-DNQhdBtI.js.map +1 -0
- package/dist/chunks/User-Dg7xpYEI.js +2 -0
- package/dist/chunks/User-Dg7xpYEI.js.map +1 -0
- package/dist/chunks/UserProfileView-B5nczdfw.js +2 -0
- package/dist/chunks/UserProfileView-B5nczdfw.js.map +1 -0
- package/dist/chunks/UserProfileView-Bpz3VZmP.js +2 -0
- package/dist/chunks/UserProfileView-Bpz3VZmP.js.map +1 -0
- package/dist/chunks/View-C5n3sIFi.js +2 -0
- package/dist/chunks/View-C5n3sIFi.js.map +1 -0
- package/dist/chunks/View-Yazho7OL.js +2 -0
- package/dist/chunks/View-Yazho7OL.js.map +1 -0
- package/dist/chunks/WebApp-CeiDNV6L.js +2 -0
- package/dist/chunks/WebApp-CeiDNV6L.js.map +1 -0
- package/dist/chunks/WebApp-irKlhuFX.js +2 -0
- package/dist/chunks/WebApp-irKlhuFX.js.map +1 -0
- package/dist/chunks/admin-B5tf0zOO.js +2 -0
- package/dist/chunks/admin-B5tf0zOO.js.map +1 -0
- package/dist/chunks/admin-CGoTpXfs.js +2 -0
- package/dist/chunks/admin-CGoTpXfs.js.map +1 -0
- package/dist/chunks/exportChart-BTJBYkOz.js +2 -0
- package/dist/chunks/exportChart-BTJBYkOz.js.map +1 -0
- package/dist/chunks/exportChart-kQ-we4Cp.js +2 -0
- package/dist/chunks/exportChart-kQ-we4Cp.js.map +1 -0
- package/dist/chunks/index-BNjCQA7q.js +2 -0
- package/dist/chunks/{index-BY54viKF.js.map → index-BNjCQA7q.js.map} +1 -1
- package/dist/chunks/index-DBsIDOAa.js +2 -0
- package/dist/chunks/{index-Df5lx5TH.js.map → index-DBsIDOAa.js.map} +1 -1
- package/dist/chunks/version-BURwX10Q.js +2 -0
- package/dist/chunks/version-BURwX10Q.js.map +1 -0
- package/dist/chunks/version-CYGIXntv.js +2 -0
- package/dist/chunks/version-CYGIXntv.js.map +1 -0
- package/dist/core.css +306 -0
- package/dist/css/web-mojo.css +1 -1
- package/dist/docit.cjs.js +1 -1
- package/dist/docit.cjs.js.map +1 -1
- package/dist/docit.es.js +1 -1
- package/dist/docit.es.js.map +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +1 -1
- package/dist/index.es.js.map +1 -1
- package/dist/lightbox.cjs.js +1 -1
- package/dist/lightbox.cjs.js.map +1 -1
- package/dist/lightbox.es.js +1 -1
- package/dist/lightbox.es.js.map +1 -1
- package/dist/map.cjs.js +1 -1
- package/dist/map.cjs.js.map +1 -1
- package/dist/map.es.js +1 -1
- package/dist/map.es.js.map +1 -1
- package/dist/mojo-auth.es.js +94 -65
- package/dist/mojo-auth.umd.js +1 -1
- package/dist/portal.css +86 -0
- package/dist/timeline.cjs.js +1 -1
- package/dist/timeline.cjs.js.map +1 -1
- package/dist/timeline.es.js +1 -1
- package/dist/timeline.es.js.map +1 -1
- package/dist/user-profile.cjs.js +1 -1
- package/dist/user-profile.es.js +1 -1
- package/dist/web-mojo.lite.iife.js +4183 -3916
- package/dist/web-mojo.lite.iife.js.map +1 -1
- package/dist/web-mojo.lite.iife.min.js +289 -311
- package/dist/web-mojo.lite.iife.min.js.map +1 -1
- package/package.json +7 -2
- package/dist/chunks/AssistantPanelView-Bdpmd4z7.js +0 -2
- package/dist/chunks/AssistantPanelView-Bdpmd4z7.js.map +0 -1
- package/dist/chunks/AssistantPanelView-C0bdbEWr.js +0 -2
- package/dist/chunks/AssistantPanelView-C0bdbEWr.js.map +0 -1
- package/dist/chunks/ChatView-B1MZNPGy.js +0 -2
- package/dist/chunks/ChatView-B1MZNPGy.js.map +0 -1
- package/dist/chunks/ChatView-DXl9nVVV.js +0 -2
- package/dist/chunks/ChatView-DXl9nVVV.js.map +0 -1
- package/dist/chunks/Collection-C39Oy2q0.js +0 -2
- package/dist/chunks/Collection-C39Oy2q0.js.map +0 -1
- package/dist/chunks/Collection-CyK0u557.js +0 -2
- package/dist/chunks/Collection-CyK0u557.js.map +0 -1
- package/dist/chunks/ContextMenu-D-C7V6J6.js +0 -2
- package/dist/chunks/ContextMenu-D-C7V6J6.js.map +0 -1
- package/dist/chunks/ContextMenu-hjqR1pGW.js +0 -2
- package/dist/chunks/ContextMenu-hjqR1pGW.js.map +0 -1
- package/dist/chunks/DataView-BFx2glFg.js +0 -2
- package/dist/chunks/DataView-D5C_lDdg.js +0 -2
- package/dist/chunks/Dialog-1umNJi4B.js +0 -3
- package/dist/chunks/Dialog-1umNJi4B.js.map +0 -1
- package/dist/chunks/Dialog-BnxWLMfr.js +0 -3
- package/dist/chunks/Dialog-BnxWLMfr.js.map +0 -1
- package/dist/chunks/FormView-Cu4iPfvU.js +0 -3
- package/dist/chunks/FormView-e-PeRx1s.js +0 -3
- package/dist/chunks/ListView-D_hOtfWZ.js +0 -2
- package/dist/chunks/ListView-D_hOtfWZ.js.map +0 -1
- package/dist/chunks/ListView-Jkke6pU1.js +0 -2
- package/dist/chunks/ListView-Jkke6pU1.js.map +0 -1
- package/dist/chunks/MetricsCountryMapView-UdvJWArx.js +0 -2
- package/dist/chunks/MetricsCountryMapView-UdvJWArx.js.map +0 -1
- package/dist/chunks/MetricsCountryMapView-jLWCL6Hm.js +0 -2
- package/dist/chunks/MetricsCountryMapView-jLWCL6Hm.js.map +0 -1
- package/dist/chunks/MetricsMiniChartWidget-BaXxR9C6.js +0 -2
- package/dist/chunks/MetricsMiniChartWidget-BaXxR9C6.js.map +0 -1
- package/dist/chunks/MetricsMiniChartWidget-DSKmKY2Z.js +0 -2
- package/dist/chunks/MetricsMiniChartWidget-DSKmKY2Z.js.map +0 -1
- package/dist/chunks/MiniPieChart-B3sM4Bjv.js +0 -2
- package/dist/chunks/MiniPieChart-B3sM4Bjv.js.map +0 -1
- package/dist/chunks/MiniPieChart-DL5Rynvm.js +0 -2
- package/dist/chunks/MiniPieChart-DL5Rynvm.js.map +0 -1
- package/dist/chunks/MiniSeriesChart-C4DPVbU_.js +0 -2
- package/dist/chunks/MiniSeriesChart-C4DPVbU_.js.map +0 -1
- package/dist/chunks/MiniSeriesChart-DdNMLwfh.js +0 -2
- package/dist/chunks/MiniSeriesChart-DdNMLwfh.js.map +0 -1
- package/dist/chunks/Modal-COT4zRh3.js +0 -2
- package/dist/chunks/Modal-COT4zRh3.js.map +0 -1
- package/dist/chunks/Modal-DgMMvDnw.js +0 -2
- package/dist/chunks/Modal-DgMMvDnw.js.map +0 -1
- package/dist/chunks/Passkeys-Bj-ufmei.js +0 -2
- package/dist/chunks/Passkeys-DfVHrRPY.js +0 -2
- package/dist/chunks/TokenManager-DIEaBGJh.js +0 -2
- package/dist/chunks/TokenManager-DIEaBGJh.js.map +0 -1
- package/dist/chunks/TokenManager-DeXkJy0u.js +0 -2
- package/dist/chunks/TokenManager-DeXkJy0u.js.map +0 -1
- package/dist/chunks/UserProfileView-B_jyMUp0.js +0 -2
- package/dist/chunks/UserProfileView-B_jyMUp0.js.map +0 -1
- package/dist/chunks/UserProfileView-DcpvvGQS.js +0 -2
- package/dist/chunks/UserProfileView-DcpvvGQS.js.map +0 -1
- package/dist/chunks/WebApp-BvFnKj-I.js +0 -2
- package/dist/chunks/WebApp-BvFnKj-I.js.map +0 -1
- package/dist/chunks/WebApp-CfDWKmwH.js +0 -2
- package/dist/chunks/WebApp-CfDWKmwH.js.map +0 -1
- package/dist/chunks/WebSocketClient-BQAZr8C4.js +0 -2
- package/dist/chunks/WebSocketClient-BQAZr8C4.js.map +0 -1
- package/dist/chunks/WebSocketClient-YR5d82h8.js +0 -2
- package/dist/chunks/WebSocketClient-YR5d82h8.js.map +0 -1
- package/dist/chunks/admin-BgvcCWQp.js +0 -2
- package/dist/chunks/admin-BgvcCWQp.js.map +0 -1
- package/dist/chunks/admin-DzhyZ_Tn.js +0 -2
- package/dist/chunks/admin-DzhyZ_Tn.js.map +0 -1
- package/dist/chunks/index-BY54viKF.js +0 -2
- package/dist/chunks/index-Df5lx5TH.js +0 -2
- package/dist/chunks/version-8mBBYRDe.js +0 -2
- package/dist/chunks/version-8mBBYRDe.js.map +0 -1
- package/dist/chunks/version-CoLHxZIU.js +0 -2
- package/dist/chunks/version-CoLHxZIU.js.map +0 -1
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{V as e}from"./Collection-C39Oy2q0.js";class Page extends e{constructor(e={}){e.tagName=e.tagName||"main",e.className=e.className||"mojo-page";const t=e.pageName||"";t&&!e.id&&(e.id="page_"+t.toLowerCase().replace(/\s+/g,"_")),super(e),this.pageName=e.pageName||this.constructor.pageName||"",this.route=e.route||this.constructor.route||"",this.title=e.title||this.pageName||"",this.id||!this.constructor.pageName||e.pageName||(this.id="page_"+this.constructor.pageName.toLowerCase().replace(/\s+/g,"_")),this.pageIcon=e.icon||e.pageIcon||this.constructor.pageIcon||"bi bi-file-text",this.displayName=e.displayName||this.constructor.displayName||this.pageName||"",this.pageDescription=e.pageDescription||this.constructor.pageDescription||"",this.params={},this.query={},this.matched=!1,this.isActive=!1,this.pageOptions={title:e.title||this.pageName||"Untitled Page",description:e.description||"",requiresAuth:e.requiresAuth||!1,...e.pageOptions},this.savedState=null,this.pageName,this.route}async onParams(e={},t={}){this.params=e,this.query=t}canEnter(){if(this.options.permissions){const e=this.getApp().activeUser;if(!e||!e.hasPermission(this.options.permissions))return!1}return!(this.options.requiresGroup&&!this.getApp().activeGroup)}async onEnter(){this.isActive=!0,await this.onInitView(),this.savedState&&(this.restoreState(this.savedState),this.savedState=null),this.pageOptions&&this.pageOptions.title&&"undefined"!=typeof document&&(document.title=this.pageOptions.title),this.emit("activated",{page:this.getMetadata()}),this.pageName}async onExit(){this.savedState=this.captureState(),this.isActive=!1,this.emit("deactivated",{page:this.getMetadata()}),this.pageName}async onGroupChange(e){}getMetadata(){return{name:this.pageName,displayName:this.displayName||this.pageName,icon:this.pageIcon,description:this.pageDescription,route:this.route,isActive:this.isActive}}async onActionDefault(e){this.pageName}async makeActive(){this.getApp().showPage(this)}async onActionNavigate(e,t){e.preventDefault();const s=t.dataset.page;this.getApp().showPage(s)}captureState(){return this.element?{scrollTop:this.element.scrollTop,formData:this.captureFormData(),custom:this.captureCustomState()}:null}restoreState(e){e&&this.element&&(this.element.scrollTop=e.scrollTop||0,this.restoreFormData(e.formData),e.custom&&this.restoreCustomState(e.custom))}captureFormData(){const e={};return this.element?(this.element.querySelectorAll("input, select, textarea").forEach(t=>{t.name&&("checkbox"===t.type?e[t.name]=t.checked:"radio"===t.type?t.checked&&(e[t.name]=t.value):e[t.name]=t.value)}),e):e}restoreFormData(e){e&&this.element&&Object.entries(e).forEach(([e,t])=>{const s=this.element.querySelector(`[name="${e}"]`);if(s)if("checkbox"===s.type)s.checked=t;else if("radio"===s.type){const s=this.element.querySelector(`[name="${e}"][value="${t}"]`);s&&(s.checked=!0)}else s.value=t})}captureCustomState(){return{}}restoreCustomState(e){}setMeta(e={}){if("undefined"!=typeof document){if(e.title&&(document.title=e.title,this.pageOptions.title=e.title),e.description){let t=document.querySelector('meta[name="description"]');t||(t=document.createElement("meta"),t.name="description",document.head.appendChild(t)),t.content=e.description,this.pageOptions.description=e.description}Object.entries(e).forEach(([e,t])=>{if("title"!==e&&"description"!==e){let s=document.querySelector(`meta[name="${e}"]`);s||(s=document.createElement("meta"),s.name=e,document.head.appendChild(s)),s.content=t}})}}showError(e){if(super.showError(e),this.element){const t=document.createElement("div");t.className="alert alert-danger alert-dismissible fade show",t.innerHTML=`\n ${e}\n <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>\n `,this.element.insertBefore(t,this.element.firstChild),setTimeout(()=>{t.parentNode&&t.parentNode.removeChild(t)},5e3)}}showSuccess(e){if(super.showSuccess(e),this.element){const t=document.createElement("div");t.className="alert alert-success alert-dismissible fade show",t.innerHTML=`\n ${e}\n <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>\n `,this.element.insertBefore(t,this.element.firstChild),setTimeout(()=>{t.parentNode&&t.parentNode.removeChild(t)},3e3)}}async onBeforeRender(){await super.onBeforeRender(),this.setMeta({title:this.pageOptions.title,description:this.pageOptions.description})}async onAfterMount(){await super.onAfterMount(),"undefined"!=typeof document&&this.pageName&&document.body.classList.add(`page-${this.pageName.toLowerCase().replace(/\s+/g,"-")}`)}async onBeforeDestroy(){await super.onBeforeDestroy(),"undefined"!=typeof document&&this.pageName&&document.body.classList.remove(`page-${this.pageName.toLowerCase().replace(/\s+/g,"-")}`)}navigate(e,t={},s={}){return this.app&&this.app.router?this.app.router.navigate(e,s):"undefined"!=typeof window&&window.MOJO?.router?window.MOJO.router.navigate(e,s):void console.error("No router available for navigation")}getRoute(){if(this.route){let e=this.route;return"string"==typeof e&&e.startsWith("/")&&(e=e.substring(1)),e}return this.pageName}syncUrl(e=!0){this.updateBrowserUrl(this.query,!1,!1)}updateBrowserUrl(e=null,t=!1,s=!1){this.getApp(),this.app.router.updateBrowserUrl(this.getRoute(),e,t,s)}static define(e){class DefinedPage extends Page{constructor(t={}){super({...e,...t})}}return DefinedPage.template=e.template,DefinedPage.pageName=e.pageName,DefinedPage.route=e.route,DefinedPage}}class ContextMenu extends e{constructor(e={}){super({tagName:"div",className:"context-menu-view dropdown",...e}),this.config=e.contextMenu||e.config||{},this.context=e.context||{}}async renderTemplate(){const e=this.config.items||[];if(0===e.length)return"";const t=this.config.icon||"bi-three-dots-horizontal",s=this.config.buttonClass||"btn btn-link text-secondary ps-3 pe-0 pt-0 pb-1",i=`context-menu-${this.id}`;return`\n <button class="${s}" type="button" id="${i}" data-bs-toggle="dropdown" aria-expanded="false">\n <i class="${t}"></i>\n </button>\n <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="${i}">\n ${e.map(e=>this.buildMenuItemHTML(e)).join("")}\n </ul>\n `}buildMenuItemHTML(e){if("divider"===e.type||e.separator)return'<li><hr class="dropdown-divider"></li>';const t=e.icon?`<i class="${e.icon} me-2"></i>`:"",s=e.label||"",i=`dropdown-item ${e.danger?"text-danger":""} ${e.disabled?"disabled":""}`,a=e.action||"";return e.href?`<li><a class="${i}" href="${e.href}" target="${e.target||"_self"}">${t}${s}</a></li>`:`<li><a class="${i}" href="#" data-action="menu-item-click" data-item-action="${a}">${t}${s}</a></li>`}async onActionMenuItemClick(e,t){e.preventDefault();const s=t.getAttribute("data-item-action");if(!s)return;const i=this.config.items.find(e=>e.action===s);i&&!i.disabled&&("function"==typeof i.handler?i.handler(this.context,e,t):this.parent.events.dispatch(s,e,t),this.closeDropdown())}closeDropdown(){const e=this.element.querySelector('[data-bs-toggle="dropdown"]');if(e){const t=window.bootstrap?.Dropdown.getInstance(e);t?.hide()}}}export{ContextMenu as C,Page as P};
|
|
2
|
-
//# sourceMappingURL=ContextMenu-D-C7V6J6.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ContextMenu-D-C7V6J6.js","sources":["../../src/core/Page.js","../../src/core/views/feedback/ContextMenu.js"],"sourcesContent":["/**\n * Page - Extends View with routing capabilities for MOJO framework\n * Handles URL routing, parameters, and page-specific actions\n *\n * Event Emitter notes:\n * - Uses EventEmitter via View base class.\n * - Use .emit/.on/.off/.once for all custom events.\n */\n\nimport View from '@core/View.js';\n\nclass Page extends View {\n constructor(options = {}) {\n // Set default tag name for pages\n options.tagName = options.tagName || 'main';\n options.className = options.className || 'mojo-page';\n\n // Set page ID based on page name\n const pageName = options.pageName || '';\n if (pageName && !options.id) {\n options.id = 'page_' + pageName.toLowerCase().replace(/\\s+/g, '_');\n }\n\n super(options);\n\n // Core page properties from design doc\n this.pageName = options.pageName || this.constructor.pageName || '';\n this.route = options.route || this.constructor.route || '';\n this.title = options.title || this.pageName || '';\n\n // Set page ID if not already set and we have a page_name from constructor\n if (!this.id && this.constructor.pageName && !options.pageName) {\n this.id = 'page_' + this.constructor.pageName.toLowerCase().replace(/\\s+/g, '_');\n }\n\n // Page metadata for event system\n this.pageIcon = options.icon || options.pageIcon || this.constructor.pageIcon || 'bi bi-file-text';\n this.displayName = options.displayName || this.constructor.displayName || this.pageName || '';\n this.pageDescription = options.pageDescription || this.constructor.pageDescription || '';\n\n // Routing state\n this.params = {};\n this.query = {};\n this.matched = false;\n this.isActive = false;\n\n // Page-specific options\n this.pageOptions = {\n title: options.title || this.pageName || 'Untitled Page',\n description: options.description || '',\n requiresAuth: options.requiresAuth || false,\n ...options.pageOptions\n };\n\n // State preservation\n this.savedState = null;\n\n console.log(`Page ${this.pageName} constructed with route: ${this.route}`);\n }\n\n /**\n * Handle route parameters - from design doc\n * @param {object} params - Route parameters\n * @param {object} query - Query string parameters\n */\n async onParams(params = {}, query = {}) {\n // const paramsChanged = JSON.stringify(params) !== JSON.stringify(this.params);\n // const queryChanged = JSON.stringify(query) !== JSON.stringify(this.query);\n\n this.params = params;\n this.query = query;\n\n // Only re-render if params actually changed and page is active\n // if (this.isActive && (paramsChanged || queryChanged)) {\n // console.log(`Page ${this.pageName} params changed, re-rendering`);\n // await this.render();\n // }\n }\n\n canEnter() {\n if (this.options.permissions) {\n const user = this.getApp().activeUser;\n if (!user || !user.hasPermission(this.options.permissions)) {\n return false;\n }\n }\n if (this.options.requiresGroup && !this.getApp().activeGroup) {\n return false;\n }\n return true;\n }\n\n /**\n * Called when entering this page (before render)\n * Override this method for initialization logic\n */\n async onEnter() {\n this.isActive = true;\n await this.onInitView();\n\n // Restore saved state if exists\n if (this.savedState) {\n this.restoreState(this.savedState);\n this.savedState = null;\n }\n\n // Set page title if provided\n if (this.pageOptions && this.pageOptions.title && typeof document !== 'undefined') {\n document.title = this.pageOptions.title;\n }\n\n // Emit activation event\n this.emit('activated', {\n page: this.getMetadata()\n });\n\n console.log(`Page ${this.pageName} entered`);\n }\n\n /**\n * Called when leaving this page (before cleanup)\n * Override this method for cleanup logic like removing listeners, clearing timers, etc.\n */\n async onExit() {\n // Save state before exit\n this.savedState = this.captureState();\n this.isActive = false;\n\n // Emit deactivation event\n this.emit('deactivated', {\n page: this.getMetadata()\n });\n console.log(`Page ${this.pageName} exiting`);\n }\n\n /**\n * Called by PortalApp.setActiveGroup() whenever the user switches to a different group.\n *\n * Override this on any page that displays group-scoped data so the page\n * stays in sync when the active group changes. Always call super first.\n *\n * This is an MVC framework — your override should tell your Models and\n * Collections to re-fetch for the new group, then call this.render().\n * There is no framework-provided loadData() method; organise your own\n * fetch logic however you like.\n *\n * @param {Model} group - The newly active Group model.\n * this.getApp().activeGroup is already set to this value.\n *\n * @example\n * async onGroupChange(group) {\n * await super.onGroupChange(group);\n * if (!group) return;\n * await this.myCollection.fetch({ group_id: group.id });\n * await this.render();\n * }\n */\n async onGroupChange(group) {\n // Empty stub — override in subclasses that display group-scoped data.\n }\n\n /**\n * Get page metadata for display and events\n * @returns {object} Page metadata\n */\n getMetadata() {\n return {\n name: this.pageName,\n displayName: this.displayName || this.pageName,\n icon: this.pageIcon,\n description: this.pageDescription,\n route: this.route,\n isActive: this.isActive\n };\n }\n\n /**\n * Handle default action - fallback from design doc\n */\n async onActionDefault(action) {\n console.log(`Default action '${action}' triggered on page: ${this.pageName}`);\n }\n\n async makeActive() {\n this.getApp().showPage(this);\n }\n\n async onActionNavigate(event, element) {\n event.preventDefault();\n const page = element.dataset.page;\n this.getApp().showPage(page);\n }\n\n /**\n * Capture current page state for preservation\n * @returns {object|null} Captured state\n */\n captureState() {\n if (!this.element) return null;\n\n return {\n scrollTop: this.element.scrollTop,\n formData: this.captureFormData(),\n custom: this.captureCustomState()\n };\n }\n\n /**\n * Restore saved state\n * @param {object} state - State to restore\n */\n restoreState(state) {\n if (!state || !this.element) return;\n\n this.element.scrollTop = state.scrollTop || 0;\n this.restoreFormData(state.formData);\n if (state.custom) {\n this.restoreCustomState(state.custom);\n }\n }\n\n /**\n * Capture form data from page\n * @returns {object} Form data\n */\n captureFormData() {\n const data = {};\n if (!this.element) return data;\n\n this.element.querySelectorAll('input, select, textarea').forEach(field => {\n if (field.name) {\n if (field.type === 'checkbox') {\n data[field.name] = field.checked;\n } else if (field.type === 'radio') {\n if (field.checked) {\n data[field.name] = field.value;\n }\n } else {\n data[field.name] = field.value;\n }\n }\n });\n\n return data;\n }\n\n /**\n * Restore form data to page\n * @param {object} formData - Form data to restore\n */\n restoreFormData(formData) {\n if (!formData || !this.element) return;\n\n Object.entries(formData).forEach(([name, value]) => {\n const field = this.element.querySelector(`[name=\"${name}\"]`);\n if (field) {\n if (field.type === 'checkbox') {\n field.checked = value;\n } else if (field.type === 'radio') {\n const radio = this.element.querySelector(`[name=\"${name}\"][value=\"${value}\"]`);\n if (radio) radio.checked = true;\n } else {\n field.value = value;\n }\n }\n });\n }\n\n /**\n * Capture custom state - override in subclasses\n * @returns {object} Custom state\n */\n captureCustomState() {\n return {};\n }\n\n /**\n * Restore custom state - override in subclasses\n * @param {object} state - Custom state to restore\n */\n restoreCustomState(state) {\n // Override in subclasses\n }\n\n\n\n /**\n * Set page metadata\n * @param {object} meta - Metadata object\n */\n setMeta(meta = {}) {\n if (typeof document === 'undefined') {\n return;\n }\n\n // Set title\n if (meta.title) {\n document.title = meta.title;\n this.pageOptions.title = meta.title;\n }\n\n // Set description\n if (meta.description) {\n let descMeta = document.querySelector('meta[name=\"description\"]');\n if (!descMeta) {\n descMeta = document.createElement('meta');\n descMeta.name = 'description';\n document.head.appendChild(descMeta);\n }\n descMeta.content = meta.description;\n this.pageOptions.description = meta.description;\n }\n\n // Set other meta tags\n Object.entries(meta).forEach(([key, value]) => {\n if (key !== 'title' && key !== 'description') {\n let metaEl = document.querySelector(`meta[name=\"${key}\"]`);\n if (!metaEl) {\n metaEl = document.createElement('meta');\n metaEl.name = key;\n document.head.appendChild(metaEl);\n }\n metaEl.content = value;\n }\n });\n }\n\n\n /**\n * Show error message with page context\n * @param {string} message - Error message\n */\n showError(message) {\n super.showError(message);\n\n // Page-specific error display can be implemented here\n if (this.element) {\n // Example: Add error to page\n const errorDiv = document.createElement('div');\n errorDiv.className = 'alert alert-danger alert-dismissible fade show';\n errorDiv.innerHTML = `\n ${message}\n <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"alert\" aria-label=\"Close\"></button>\n `;\n\n // Insert at top of page\n this.element.insertBefore(errorDiv, this.element.firstChild);\n\n // Auto-remove after 5 seconds\n setTimeout(() => {\n if (errorDiv.parentNode) {\n errorDiv.parentNode.removeChild(errorDiv);\n }\n }, 5000);\n }\n }\n\n /**\n * Show success message with page context\n * @param {string} message - Success message\n */\n showSuccess(message) {\n super.showSuccess(message);\n\n // Page-specific success display\n if (this.element) {\n const successDiv = document.createElement('div');\n successDiv.className = 'alert alert-success alert-dismissible fade show';\n successDiv.innerHTML = `\n ${message}\n <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"alert\" aria-label=\"Close\"></button>\n `;\n\n // Insert at top of page\n this.element.insertBefore(successDiv, this.element.firstChild);\n\n // Auto-remove after 3 seconds\n setTimeout(() => {\n if (successDiv.parentNode) {\n successDiv.parentNode.removeChild(successDiv);\n }\n }, 3000);\n }\n }\n\n /**\n * Page-specific before render hook\n */\n async onBeforeRender() {\n await super.onBeforeRender();\n\n // Set page metadata before rendering\n this.setMeta({\n title: this.pageOptions.title,\n description: this.pageOptions.description\n });\n }\n\n /**\n * Page-specific after mount hook\n */\n async onAfterMount() {\n await super.onAfterMount();\n\n // Add page-specific class to body\n if (typeof document !== 'undefined' && this.pageName) {\n document.body.classList.add(`page-${this.pageName.toLowerCase().replace(/\\s+/g, '-')}`);\n }\n }\n\n /**\n * Page-specific before destroy hook\n */\n async onBeforeDestroy() {\n await super.onBeforeDestroy();\n\n // Remove page-specific class from body\n if (typeof document !== 'undefined' && this.pageName) {\n document.body.classList.remove(`page-${this.pageName.toLowerCase().replace(/\\s+/g, '-')}`);\n }\n }\n\n /**\n * Navigate to another page using the app's router\n * @param {string} route - Route to navigate to\n * @param {object} params - Route parameters\n * @param {object} options - Navigation options\n */\n navigate(route, params = {}, options = {}) {\n // Delegate to app's router\n if (this.app && this.app.router) {\n return this.app.router.navigate(route, options);\n }\n\n // Fallback to MOJO global router\n if (typeof window !== 'undefined' && window.MOJO?.router) {\n return window.MOJO.router.navigate(route, options);\n }\n\n console.error('No router available for navigation');\n }\n\n getRoute() {\n if (this.route) {\n let route = this.route;\n if (typeof route === 'string' && route.startsWith('/')) {\n route = route.substring(1);\n }\n return route;\n }\n return this.pageName;\n }\n\n syncUrl(force = true) {\n this.updateBrowserUrl(this.query, false, false);\n }\n\n updateBrowserUrl(query = null, replace = false, trigger = false) {\n this.getApp();\n // we need to do this to normalize the URL\n // const targetPath = this.app.buildPagePath(this, this.params, query);\n // const { pageName, queryParams } = this.app.router.parseInput(targetPath);\n this.app.router.updateBrowserUrl(this.getRoute(), query, replace, trigger);\n }\n\n /**\n * Static method to define a page class with metadata\n * @param {object} definition - Page class definition\n * @returns {class} Page class\n */\n static define(definition) {\n class DefinedPage extends Page {\n constructor(options = {}) {\n super({\n ...definition,\n ...options\n });\n }\n }\n\n // Copy static properties\n DefinedPage.template = definition.template;\n DefinedPage.pageName = definition.pageName;\n DefinedPage.route = definition.route;\n\n return DefinedPage;\n }\n}\n\nexport default Page;\n","/**\n * ContextMenu - A reusable context menu component for MOJO\n *\n * Renders a Bootstrap 5 dropdown menu based on a configuration object.\n * This component is designed to be easily embedded in any other View.\n * It supports the same configuration syntax as the Dialog's contextMenu.\n *\n * Features:\n * - Renders a dropdown button with a configurable icon.\n * - Generates menu items from a configuration array.\n * - Supports dividers, icons, labels, and links.\n * - Handles actions via inline handlers or by emitting an 'action' event.\n *\n * @example\n * const contextMenu = new ContextMenu({\n * config: {\n * icon: 'bi-gear', // Optional: custom trigger icon\n * items: [\n * { label: 'Edit', action: 'edit', icon: 'bi-pencil' },\n * { label: 'Delete', action: 'delete', icon: 'bi-trash', danger: true },\n * { type: 'divider' },\n * {\n * label: 'Custom Action',\n * action: 'custom',\n * icon: 'bi-star',\n * handler: (context) => {\n * console.log('Inline handler called with context:', context);\n * }\n * }\n * ]\n * },\n * context: { id: 123, name: 'My Item' } // Optional data to pass to handlers/events\n * });\n *\n * // Listen for actions from the parent view\n * contextMenu.on('action', (data) => {\n * console.log(`Action '${data.action}' triggered for context:`, data.context);\n * if (data.action === 'edit') {\n * // handle edit\n * }\n * });\n */\n\nimport View from '@core/View.js';\n\nclass ContextMenu extends View {\n constructor(options = {}) {\n super({\n tagName: 'div',\n className: 'context-menu-view dropdown',\n ...options\n });\n\n this.config = options.contextMenu || options.config || {};\n this.context = options.context || {}; // Optional data to pass to handlers/events\n }\n\n /**\n * Build the dropdown menu HTML from the configuration.\n */\n async renderTemplate() {\n const menuItems = this.config.items || [];\n if (menuItems.length === 0) {\n return ''; // Don't render anything if there are no items\n }\n\n const triggerIcon = this.config.icon || 'bi-three-dots-horizontal';\n const buttonClass = this.config.buttonClass || 'btn btn-link text-secondary ps-3 pe-0 pt-0 pb-1';\n const dropdownId = `context-menu-${this.id}`;\n\n const menuItemsHtml = menuItems.map(item => this.buildMenuItemHTML(item)).join('');\n\n return `\n <button class=\"${buttonClass}\" type=\"button\" id=\"${dropdownId}\" data-bs-toggle=\"dropdown\" aria-expanded=\"false\">\n <i class=\"${triggerIcon}\"></i>\n </button>\n <ul class=\"dropdown-menu dropdown-menu-end\" aria-labelledby=\"${dropdownId}\">\n ${menuItemsHtml}\n </ul>\n `;\n }\n\n /**\n * Build the HTML for a single menu item.\n * @param {object} item - The menu item configuration.\n * @returns {string} The HTML string for the list item.\n */\n buildMenuItemHTML(item) {\n if (item.type === 'divider' || item.separator) {\n return '<li><hr class=\"dropdown-divider\"></li>';\n }\n\n const icon = item.icon ? `<i class=\"${item.icon} me-2\"></i>` : '';\n const label = item.label || '';\n const itemClass = `dropdown-item ${item.danger ? 'text-danger' : ''} ${item.disabled ? 'disabled' : ''}`;\n const action = item.action || '';\n\n if (item.href) {\n return `<li><a class=\"${itemClass}\" href=\"${item.href}\" target=\"${item.target || '_self'}\">${icon}${label}</a></li>`;\n }\n\n return `<li><a class=\"${itemClass}\" href=\"#\" data-action=\"menu-item-click\" data-item-action=\"${action}\">${icon}${label}</a></li>`;\n }\n\n /**\n * Handle clicks on menu items.\n * @param {Event} event - The click event.\n * @param {HTMLElement} element - The clicked anchor element.\n */\n async onActionMenuItemClick(event, element) {\n event.preventDefault();\n const action = element.getAttribute('data-item-action');\n if (!action) return;\n\n const menuItem = this.config.items.find(item => item.action === action);\n if (!menuItem || menuItem.disabled) return;\n\n // Support for inline handlers\n if (typeof menuItem.handler === 'function') {\n menuItem.handler(this.context, event, element);\n } else {\n // Emit a general event for parent views to listen to\n // this.emit('action', {\n // action: action,\n // context: this.context,\n // sourceEvent: event\n // });\n this.parent.events.dispatch(action, event, element);\n }\n this.closeDropdown();\n }\n\n closeDropdown() {\n const dropdownTrigger = this.element.querySelector('[data-bs-toggle=\"dropdown\"]');\n if (dropdownTrigger) {\n const dropdownInstance = window.bootstrap?.Dropdown.getInstance(dropdownTrigger);\n dropdownInstance?.hide();\n }\n }\n}\n\nexport default ContextMenu;\n"],"names":["Page","View","constructor","options","tagName","className","pageName","id","toLowerCase","replace","super","this","route","title","pageIcon","icon","displayName","pageDescription","params","query","matched","isActive","pageOptions","description","requiresAuth","savedState","onParams","canEnter","permissions","user","getApp","activeUser","hasPermission","requiresGroup","activeGroup","onEnter","onInitView","restoreState","document","emit","page","getMetadata","onExit","captureState","onGroupChange","group","name","onActionDefault","action","makeActive","showPage","onActionNavigate","event","element","preventDefault","dataset","scrollTop","formData","captureFormData","custom","captureCustomState","state","restoreFormData","restoreCustomState","data","querySelectorAll","forEach","field","type","checked","value","Object","entries","querySelector","radio","setMeta","meta","descMeta","createElement","head","appendChild","content","key","metaEl","showError","message","errorDiv","innerHTML","insertBefore","firstChild","setTimeout","parentNode","removeChild","showSuccess","successDiv","onBeforeRender","onAfterMount","body","classList","add","onBeforeDestroy","remove","navigate","app","router","window","MOJO","console","error","getRoute","startsWith","substring","syncUrl","force","updateBrowserUrl","trigger","define","definition","DefinedPage","template","ContextMenu","config","contextMenu","context","renderTemplate","menuItems","items","length","triggerIcon","buttonClass","dropdownId","map","item","buildMenuItemHTML","join","separator","label","itemClass","danger","disabled","href","target","onActionMenuItemClick","getAttribute","menuItem","find","handler","parent","events","dispatch","closeDropdown","dropdownTrigger","dropdownInstance","bootstrap","Dropdown","getInstance","hide"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";const e=require("./Collection-CyK0u557.js");class Page extends e.View{constructor(e={}){e.tagName=e.tagName||"main",e.className=e.className||"mojo-page";const t=e.pageName||"";t&&!e.id&&(e.id="page_"+t.toLowerCase().replace(/\s+/g,"_")),super(e),this.pageName=e.pageName||this.constructor.pageName||"",this.route=e.route||this.constructor.route||"",this.title=e.title||this.pageName||"",this.id||!this.constructor.pageName||e.pageName||(this.id="page_"+this.constructor.pageName.toLowerCase().replace(/\s+/g,"_")),this.pageIcon=e.icon||e.pageIcon||this.constructor.pageIcon||"bi bi-file-text",this.displayName=e.displayName||this.constructor.displayName||this.pageName||"",this.pageDescription=e.pageDescription||this.constructor.pageDescription||"",this.params={},this.query={},this.matched=!1,this.isActive=!1,this.pageOptions={title:e.title||this.pageName||"Untitled Page",description:e.description||"",requiresAuth:e.requiresAuth||!1,...e.pageOptions},this.savedState=null,this.pageName,this.route}async onParams(e={},t={}){this.params=e,this.query=t}canEnter(){if(this.options.permissions){const e=this.getApp().activeUser;if(!e||!e.hasPermission(this.options.permissions))return!1}return!(this.options.requiresGroup&&!this.getApp().activeGroup)}async onEnter(){this.isActive=!0,await this.onInitView(),this.savedState&&(this.restoreState(this.savedState),this.savedState=null),this.pageOptions&&this.pageOptions.title&&"undefined"!=typeof document&&(document.title=this.pageOptions.title),this.emit("activated",{page:this.getMetadata()}),this.pageName}async onExit(){this.savedState=this.captureState(),this.isActive=!1,this.emit("deactivated",{page:this.getMetadata()}),this.pageName}async onGroupChange(e){}getMetadata(){return{name:this.pageName,displayName:this.displayName||this.pageName,icon:this.pageIcon,description:this.pageDescription,route:this.route,isActive:this.isActive}}async onActionDefault(e){this.pageName}async makeActive(){this.getApp().showPage(this)}async onActionNavigate(e,t){e.preventDefault();const s=t.dataset.page;this.getApp().showPage(s)}captureState(){return this.element?{scrollTop:this.element.scrollTop,formData:this.captureFormData(),custom:this.captureCustomState()}:null}restoreState(e){e&&this.element&&(this.element.scrollTop=e.scrollTop||0,this.restoreFormData(e.formData),e.custom&&this.restoreCustomState(e.custom))}captureFormData(){const e={};return this.element?(this.element.querySelectorAll("input, select, textarea").forEach(t=>{t.name&&("checkbox"===t.type?e[t.name]=t.checked:"radio"===t.type?t.checked&&(e[t.name]=t.value):e[t.name]=t.value)}),e):e}restoreFormData(e){e&&this.element&&Object.entries(e).forEach(([e,t])=>{const s=this.element.querySelector(`[name="${e}"]`);if(s)if("checkbox"===s.type)s.checked=t;else if("radio"===s.type){const s=this.element.querySelector(`[name="${e}"][value="${t}"]`);s&&(s.checked=!0)}else s.value=t})}captureCustomState(){return{}}restoreCustomState(e){}setMeta(e={}){if("undefined"!=typeof document){if(e.title&&(document.title=e.title,this.pageOptions.title=e.title),e.description){let t=document.querySelector('meta[name="description"]');t||(t=document.createElement("meta"),t.name="description",document.head.appendChild(t)),t.content=e.description,this.pageOptions.description=e.description}Object.entries(e).forEach(([e,t])=>{if("title"!==e&&"description"!==e){let s=document.querySelector(`meta[name="${e}"]`);s||(s=document.createElement("meta"),s.name=e,document.head.appendChild(s)),s.content=t}})}}showError(e){if(super.showError(e),this.element){const t=document.createElement("div");t.className="alert alert-danger alert-dismissible fade show",t.innerHTML=`\n ${e}\n <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>\n `,this.element.insertBefore(t,this.element.firstChild),setTimeout(()=>{t.parentNode&&t.parentNode.removeChild(t)},5e3)}}showSuccess(e){if(super.showSuccess(e),this.element){const t=document.createElement("div");t.className="alert alert-success alert-dismissible fade show",t.innerHTML=`\n ${e}\n <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>\n `,this.element.insertBefore(t,this.element.firstChild),setTimeout(()=>{t.parentNode&&t.parentNode.removeChild(t)},3e3)}}async onBeforeRender(){await super.onBeforeRender(),this.setMeta({title:this.pageOptions.title,description:this.pageOptions.description})}async onAfterMount(){await super.onAfterMount(),"undefined"!=typeof document&&this.pageName&&document.body.classList.add(`page-${this.pageName.toLowerCase().replace(/\s+/g,"-")}`)}async onBeforeDestroy(){await super.onBeforeDestroy(),"undefined"!=typeof document&&this.pageName&&document.body.classList.remove(`page-${this.pageName.toLowerCase().replace(/\s+/g,"-")}`)}navigate(e,t={},s={}){return this.app&&this.app.router?this.app.router.navigate(e,s):"undefined"!=typeof window&&window.MOJO?.router?window.MOJO.router.navigate(e,s):void console.error("No router available for navigation")}getRoute(){if(this.route){let e=this.route;return"string"==typeof e&&e.startsWith("/")&&(e=e.substring(1)),e}return this.pageName}syncUrl(e=!0){this.updateBrowserUrl(this.query,!1,!1)}updateBrowserUrl(e=null,t=!1,s=!1){this.getApp(),this.app.router.updateBrowserUrl(this.getRoute(),e,t,s)}static define(e){class DefinedPage extends Page{constructor(t={}){super({...e,...t})}}return DefinedPage.template=e.template,DefinedPage.pageName=e.pageName,DefinedPage.route=e.route,DefinedPage}}class ContextMenu extends e.View{constructor(e={}){super({tagName:"div",className:"context-menu-view dropdown",...e}),this.config=e.contextMenu||e.config||{},this.context=e.context||{}}async renderTemplate(){const e=this.config.items||[];if(0===e.length)return"";const t=this.config.icon||"bi-three-dots-horizontal",s=this.config.buttonClass||"btn btn-link text-secondary ps-3 pe-0 pt-0 pb-1",i=`context-menu-${this.id}`;return`\n <button class="${s}" type="button" id="${i}" data-bs-toggle="dropdown" aria-expanded="false">\n <i class="${t}"></i>\n </button>\n <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="${i}">\n ${e.map(e=>this.buildMenuItemHTML(e)).join("")}\n </ul>\n `}buildMenuItemHTML(e){if("divider"===e.type||e.separator)return'<li><hr class="dropdown-divider"></li>';const t=e.icon?`<i class="${e.icon} me-2"></i>`:"",s=e.label||"",i=`dropdown-item ${e.danger?"text-danger":""} ${e.disabled?"disabled":""}`,a=e.action||"";return e.href?`<li><a class="${i}" href="${e.href}" target="${e.target||"_self"}">${t}${s}</a></li>`:`<li><a class="${i}" href="#" data-action="menu-item-click" data-item-action="${a}">${t}${s}</a></li>`}async onActionMenuItemClick(e,t){e.preventDefault();const s=t.getAttribute("data-item-action");if(!s)return;const i=this.config.items.find(e=>e.action===s);i&&!i.disabled&&("function"==typeof i.handler?i.handler(this.context,e,t):this.parent.events.dispatch(s,e,t),this.closeDropdown())}closeDropdown(){const e=this.element.querySelector('[data-bs-toggle="dropdown"]');if(e){const t=window.bootstrap?.Dropdown.getInstance(e);t?.hide()}}}exports.ContextMenu=ContextMenu,exports.Page=Page;
|
|
2
|
-
//# sourceMappingURL=ContextMenu-hjqR1pGW.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ContextMenu-hjqR1pGW.js","sources":["../../src/core/Page.js","../../src/core/views/feedback/ContextMenu.js"],"sourcesContent":["/**\n * Page - Extends View with routing capabilities for MOJO framework\n * Handles URL routing, parameters, and page-specific actions\n *\n * Event Emitter notes:\n * - Uses EventEmitter via View base class.\n * - Use .emit/.on/.off/.once for all custom events.\n */\n\nimport View from '@core/View.js';\n\nclass Page extends View {\n constructor(options = {}) {\n // Set default tag name for pages\n options.tagName = options.tagName || 'main';\n options.className = options.className || 'mojo-page';\n\n // Set page ID based on page name\n const pageName = options.pageName || '';\n if (pageName && !options.id) {\n options.id = 'page_' + pageName.toLowerCase().replace(/\\s+/g, '_');\n }\n\n super(options);\n\n // Core page properties from design doc\n this.pageName = options.pageName || this.constructor.pageName || '';\n this.route = options.route || this.constructor.route || '';\n this.title = options.title || this.pageName || '';\n\n // Set page ID if not already set and we have a page_name from constructor\n if (!this.id && this.constructor.pageName && !options.pageName) {\n this.id = 'page_' + this.constructor.pageName.toLowerCase().replace(/\\s+/g, '_');\n }\n\n // Page metadata for event system\n this.pageIcon = options.icon || options.pageIcon || this.constructor.pageIcon || 'bi bi-file-text';\n this.displayName = options.displayName || this.constructor.displayName || this.pageName || '';\n this.pageDescription = options.pageDescription || this.constructor.pageDescription || '';\n\n // Routing state\n this.params = {};\n this.query = {};\n this.matched = false;\n this.isActive = false;\n\n // Page-specific options\n this.pageOptions = {\n title: options.title || this.pageName || 'Untitled Page',\n description: options.description || '',\n requiresAuth: options.requiresAuth || false,\n ...options.pageOptions\n };\n\n // State preservation\n this.savedState = null;\n\n console.log(`Page ${this.pageName} constructed with route: ${this.route}`);\n }\n\n /**\n * Handle route parameters - from design doc\n * @param {object} params - Route parameters\n * @param {object} query - Query string parameters\n */\n async onParams(params = {}, query = {}) {\n // const paramsChanged = JSON.stringify(params) !== JSON.stringify(this.params);\n // const queryChanged = JSON.stringify(query) !== JSON.stringify(this.query);\n\n this.params = params;\n this.query = query;\n\n // Only re-render if params actually changed and page is active\n // if (this.isActive && (paramsChanged || queryChanged)) {\n // console.log(`Page ${this.pageName} params changed, re-rendering`);\n // await this.render();\n // }\n }\n\n canEnter() {\n if (this.options.permissions) {\n const user = this.getApp().activeUser;\n if (!user || !user.hasPermission(this.options.permissions)) {\n return false;\n }\n }\n if (this.options.requiresGroup && !this.getApp().activeGroup) {\n return false;\n }\n return true;\n }\n\n /**\n * Called when entering this page (before render)\n * Override this method for initialization logic\n */\n async onEnter() {\n this.isActive = true;\n await this.onInitView();\n\n // Restore saved state if exists\n if (this.savedState) {\n this.restoreState(this.savedState);\n this.savedState = null;\n }\n\n // Set page title if provided\n if (this.pageOptions && this.pageOptions.title && typeof document !== 'undefined') {\n document.title = this.pageOptions.title;\n }\n\n // Emit activation event\n this.emit('activated', {\n page: this.getMetadata()\n });\n\n console.log(`Page ${this.pageName} entered`);\n }\n\n /**\n * Called when leaving this page (before cleanup)\n * Override this method for cleanup logic like removing listeners, clearing timers, etc.\n */\n async onExit() {\n // Save state before exit\n this.savedState = this.captureState();\n this.isActive = false;\n\n // Emit deactivation event\n this.emit('deactivated', {\n page: this.getMetadata()\n });\n console.log(`Page ${this.pageName} exiting`);\n }\n\n /**\n * Called by PortalApp.setActiveGroup() whenever the user switches to a different group.\n *\n * Override this on any page that displays group-scoped data so the page\n * stays in sync when the active group changes. Always call super first.\n *\n * This is an MVC framework — your override should tell your Models and\n * Collections to re-fetch for the new group, then call this.render().\n * There is no framework-provided loadData() method; organise your own\n * fetch logic however you like.\n *\n * @param {Model} group - The newly active Group model.\n * this.getApp().activeGroup is already set to this value.\n *\n * @example\n * async onGroupChange(group) {\n * await super.onGroupChange(group);\n * if (!group) return;\n * await this.myCollection.fetch({ group_id: group.id });\n * await this.render();\n * }\n */\n async onGroupChange(group) {\n // Empty stub — override in subclasses that display group-scoped data.\n }\n\n /**\n * Get page metadata for display and events\n * @returns {object} Page metadata\n */\n getMetadata() {\n return {\n name: this.pageName,\n displayName: this.displayName || this.pageName,\n icon: this.pageIcon,\n description: this.pageDescription,\n route: this.route,\n isActive: this.isActive\n };\n }\n\n /**\n * Handle default action - fallback from design doc\n */\n async onActionDefault(action) {\n console.log(`Default action '${action}' triggered on page: ${this.pageName}`);\n }\n\n async makeActive() {\n this.getApp().showPage(this);\n }\n\n async onActionNavigate(event, element) {\n event.preventDefault();\n const page = element.dataset.page;\n this.getApp().showPage(page);\n }\n\n /**\n * Capture current page state for preservation\n * @returns {object|null} Captured state\n */\n captureState() {\n if (!this.element) return null;\n\n return {\n scrollTop: this.element.scrollTop,\n formData: this.captureFormData(),\n custom: this.captureCustomState()\n };\n }\n\n /**\n * Restore saved state\n * @param {object} state - State to restore\n */\n restoreState(state) {\n if (!state || !this.element) return;\n\n this.element.scrollTop = state.scrollTop || 0;\n this.restoreFormData(state.formData);\n if (state.custom) {\n this.restoreCustomState(state.custom);\n }\n }\n\n /**\n * Capture form data from page\n * @returns {object} Form data\n */\n captureFormData() {\n const data = {};\n if (!this.element) return data;\n\n this.element.querySelectorAll('input, select, textarea').forEach(field => {\n if (field.name) {\n if (field.type === 'checkbox') {\n data[field.name] = field.checked;\n } else if (field.type === 'radio') {\n if (field.checked) {\n data[field.name] = field.value;\n }\n } else {\n data[field.name] = field.value;\n }\n }\n });\n\n return data;\n }\n\n /**\n * Restore form data to page\n * @param {object} formData - Form data to restore\n */\n restoreFormData(formData) {\n if (!formData || !this.element) return;\n\n Object.entries(formData).forEach(([name, value]) => {\n const field = this.element.querySelector(`[name=\"${name}\"]`);\n if (field) {\n if (field.type === 'checkbox') {\n field.checked = value;\n } else if (field.type === 'radio') {\n const radio = this.element.querySelector(`[name=\"${name}\"][value=\"${value}\"]`);\n if (radio) radio.checked = true;\n } else {\n field.value = value;\n }\n }\n });\n }\n\n /**\n * Capture custom state - override in subclasses\n * @returns {object} Custom state\n */\n captureCustomState() {\n return {};\n }\n\n /**\n * Restore custom state - override in subclasses\n * @param {object} state - Custom state to restore\n */\n restoreCustomState(state) {\n // Override in subclasses\n }\n\n\n\n /**\n * Set page metadata\n * @param {object} meta - Metadata object\n */\n setMeta(meta = {}) {\n if (typeof document === 'undefined') {\n return;\n }\n\n // Set title\n if (meta.title) {\n document.title = meta.title;\n this.pageOptions.title = meta.title;\n }\n\n // Set description\n if (meta.description) {\n let descMeta = document.querySelector('meta[name=\"description\"]');\n if (!descMeta) {\n descMeta = document.createElement('meta');\n descMeta.name = 'description';\n document.head.appendChild(descMeta);\n }\n descMeta.content = meta.description;\n this.pageOptions.description = meta.description;\n }\n\n // Set other meta tags\n Object.entries(meta).forEach(([key, value]) => {\n if (key !== 'title' && key !== 'description') {\n let metaEl = document.querySelector(`meta[name=\"${key}\"]`);\n if (!metaEl) {\n metaEl = document.createElement('meta');\n metaEl.name = key;\n document.head.appendChild(metaEl);\n }\n metaEl.content = value;\n }\n });\n }\n\n\n /**\n * Show error message with page context\n * @param {string} message - Error message\n */\n showError(message) {\n super.showError(message);\n\n // Page-specific error display can be implemented here\n if (this.element) {\n // Example: Add error to page\n const errorDiv = document.createElement('div');\n errorDiv.className = 'alert alert-danger alert-dismissible fade show';\n errorDiv.innerHTML = `\n ${message}\n <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"alert\" aria-label=\"Close\"></button>\n `;\n\n // Insert at top of page\n this.element.insertBefore(errorDiv, this.element.firstChild);\n\n // Auto-remove after 5 seconds\n setTimeout(() => {\n if (errorDiv.parentNode) {\n errorDiv.parentNode.removeChild(errorDiv);\n }\n }, 5000);\n }\n }\n\n /**\n * Show success message with page context\n * @param {string} message - Success message\n */\n showSuccess(message) {\n super.showSuccess(message);\n\n // Page-specific success display\n if (this.element) {\n const successDiv = document.createElement('div');\n successDiv.className = 'alert alert-success alert-dismissible fade show';\n successDiv.innerHTML = `\n ${message}\n <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"alert\" aria-label=\"Close\"></button>\n `;\n\n // Insert at top of page\n this.element.insertBefore(successDiv, this.element.firstChild);\n\n // Auto-remove after 3 seconds\n setTimeout(() => {\n if (successDiv.parentNode) {\n successDiv.parentNode.removeChild(successDiv);\n }\n }, 3000);\n }\n }\n\n /**\n * Page-specific before render hook\n */\n async onBeforeRender() {\n await super.onBeforeRender();\n\n // Set page metadata before rendering\n this.setMeta({\n title: this.pageOptions.title,\n description: this.pageOptions.description\n });\n }\n\n /**\n * Page-specific after mount hook\n */\n async onAfterMount() {\n await super.onAfterMount();\n\n // Add page-specific class to body\n if (typeof document !== 'undefined' && this.pageName) {\n document.body.classList.add(`page-${this.pageName.toLowerCase().replace(/\\s+/g, '-')}`);\n }\n }\n\n /**\n * Page-specific before destroy hook\n */\n async onBeforeDestroy() {\n await super.onBeforeDestroy();\n\n // Remove page-specific class from body\n if (typeof document !== 'undefined' && this.pageName) {\n document.body.classList.remove(`page-${this.pageName.toLowerCase().replace(/\\s+/g, '-')}`);\n }\n }\n\n /**\n * Navigate to another page using the app's router\n * @param {string} route - Route to navigate to\n * @param {object} params - Route parameters\n * @param {object} options - Navigation options\n */\n navigate(route, params = {}, options = {}) {\n // Delegate to app's router\n if (this.app && this.app.router) {\n return this.app.router.navigate(route, options);\n }\n\n // Fallback to MOJO global router\n if (typeof window !== 'undefined' && window.MOJO?.router) {\n return window.MOJO.router.navigate(route, options);\n }\n\n console.error('No router available for navigation');\n }\n\n getRoute() {\n if (this.route) {\n let route = this.route;\n if (typeof route === 'string' && route.startsWith('/')) {\n route = route.substring(1);\n }\n return route;\n }\n return this.pageName;\n }\n\n syncUrl(force = true) {\n this.updateBrowserUrl(this.query, false, false);\n }\n\n updateBrowserUrl(query = null, replace = false, trigger = false) {\n this.getApp();\n // we need to do this to normalize the URL\n // const targetPath = this.app.buildPagePath(this, this.params, query);\n // const { pageName, queryParams } = this.app.router.parseInput(targetPath);\n this.app.router.updateBrowserUrl(this.getRoute(), query, replace, trigger);\n }\n\n /**\n * Static method to define a page class with metadata\n * @param {object} definition - Page class definition\n * @returns {class} Page class\n */\n static define(definition) {\n class DefinedPage extends Page {\n constructor(options = {}) {\n super({\n ...definition,\n ...options\n });\n }\n }\n\n // Copy static properties\n DefinedPage.template = definition.template;\n DefinedPage.pageName = definition.pageName;\n DefinedPage.route = definition.route;\n\n return DefinedPage;\n }\n}\n\nexport default Page;\n","/**\n * ContextMenu - A reusable context menu component for MOJO\n *\n * Renders a Bootstrap 5 dropdown menu based on a configuration object.\n * This component is designed to be easily embedded in any other View.\n * It supports the same configuration syntax as the Dialog's contextMenu.\n *\n * Features:\n * - Renders a dropdown button with a configurable icon.\n * - Generates menu items from a configuration array.\n * - Supports dividers, icons, labels, and links.\n * - Handles actions via inline handlers or by emitting an 'action' event.\n *\n * @example\n * const contextMenu = new ContextMenu({\n * config: {\n * icon: 'bi-gear', // Optional: custom trigger icon\n * items: [\n * { label: 'Edit', action: 'edit', icon: 'bi-pencil' },\n * { label: 'Delete', action: 'delete', icon: 'bi-trash', danger: true },\n * { type: 'divider' },\n * {\n * label: 'Custom Action',\n * action: 'custom',\n * icon: 'bi-star',\n * handler: (context) => {\n * console.log('Inline handler called with context:', context);\n * }\n * }\n * ]\n * },\n * context: { id: 123, name: 'My Item' } // Optional data to pass to handlers/events\n * });\n *\n * // Listen for actions from the parent view\n * contextMenu.on('action', (data) => {\n * console.log(`Action '${data.action}' triggered for context:`, data.context);\n * if (data.action === 'edit') {\n * // handle edit\n * }\n * });\n */\n\nimport View from '@core/View.js';\n\nclass ContextMenu extends View {\n constructor(options = {}) {\n super({\n tagName: 'div',\n className: 'context-menu-view dropdown',\n ...options\n });\n\n this.config = options.contextMenu || options.config || {};\n this.context = options.context || {}; // Optional data to pass to handlers/events\n }\n\n /**\n * Build the dropdown menu HTML from the configuration.\n */\n async renderTemplate() {\n const menuItems = this.config.items || [];\n if (menuItems.length === 0) {\n return ''; // Don't render anything if there are no items\n }\n\n const triggerIcon = this.config.icon || 'bi-three-dots-horizontal';\n const buttonClass = this.config.buttonClass || 'btn btn-link text-secondary ps-3 pe-0 pt-0 pb-1';\n const dropdownId = `context-menu-${this.id}`;\n\n const menuItemsHtml = menuItems.map(item => this.buildMenuItemHTML(item)).join('');\n\n return `\n <button class=\"${buttonClass}\" type=\"button\" id=\"${dropdownId}\" data-bs-toggle=\"dropdown\" aria-expanded=\"false\">\n <i class=\"${triggerIcon}\"></i>\n </button>\n <ul class=\"dropdown-menu dropdown-menu-end\" aria-labelledby=\"${dropdownId}\">\n ${menuItemsHtml}\n </ul>\n `;\n }\n\n /**\n * Build the HTML for a single menu item.\n * @param {object} item - The menu item configuration.\n * @returns {string} The HTML string for the list item.\n */\n buildMenuItemHTML(item) {\n if (item.type === 'divider' || item.separator) {\n return '<li><hr class=\"dropdown-divider\"></li>';\n }\n\n const icon = item.icon ? `<i class=\"${item.icon} me-2\"></i>` : '';\n const label = item.label || '';\n const itemClass = `dropdown-item ${item.danger ? 'text-danger' : ''} ${item.disabled ? 'disabled' : ''}`;\n const action = item.action || '';\n\n if (item.href) {\n return `<li><a class=\"${itemClass}\" href=\"${item.href}\" target=\"${item.target || '_self'}\">${icon}${label}</a></li>`;\n }\n\n return `<li><a class=\"${itemClass}\" href=\"#\" data-action=\"menu-item-click\" data-item-action=\"${action}\">${icon}${label}</a></li>`;\n }\n\n /**\n * Handle clicks on menu items.\n * @param {Event} event - The click event.\n * @param {HTMLElement} element - The clicked anchor element.\n */\n async onActionMenuItemClick(event, element) {\n event.preventDefault();\n const action = element.getAttribute('data-item-action');\n if (!action) return;\n\n const menuItem = this.config.items.find(item => item.action === action);\n if (!menuItem || menuItem.disabled) return;\n\n // Support for inline handlers\n if (typeof menuItem.handler === 'function') {\n menuItem.handler(this.context, event, element);\n } else {\n // Emit a general event for parent views to listen to\n // this.emit('action', {\n // action: action,\n // context: this.context,\n // sourceEvent: event\n // });\n this.parent.events.dispatch(action, event, element);\n }\n this.closeDropdown();\n }\n\n closeDropdown() {\n const dropdownTrigger = this.element.querySelector('[data-bs-toggle=\"dropdown\"]');\n if (dropdownTrigger) {\n const dropdownInstance = window.bootstrap?.Dropdown.getInstance(dropdownTrigger);\n dropdownInstance?.hide();\n }\n }\n}\n\nexport default ContextMenu;\n"],"names":["Page","View","constructor","options","tagName","className","pageName","id","toLowerCase","replace","super","this","route","title","pageIcon","icon","displayName","pageDescription","params","query","matched","isActive","pageOptions","description","requiresAuth","savedState","onParams","canEnter","permissions","user","getApp","activeUser","hasPermission","requiresGroup","activeGroup","onEnter","onInitView","restoreState","document","emit","page","getMetadata","onExit","captureState","onGroupChange","group","name","onActionDefault","action","makeActive","showPage","onActionNavigate","event","element","preventDefault","dataset","scrollTop","formData","captureFormData","custom","captureCustomState","state","restoreFormData","restoreCustomState","data","querySelectorAll","forEach","field","type","checked","value","Object","entries","querySelector","radio","setMeta","meta","descMeta","createElement","head","appendChild","content","key","metaEl","showError","message","errorDiv","innerHTML","insertBefore","firstChild","setTimeout","parentNode","removeChild","showSuccess","successDiv","onBeforeRender","onAfterMount","body","classList","add","onBeforeDestroy","remove","navigate","app","router","window","MOJO","console","error","getRoute","startsWith","substring","syncUrl","force","updateBrowserUrl","trigger","define","definition","DefinedPage","template","ContextMenu","config","contextMenu","context","renderTemplate","menuItems","items","length","triggerIcon","buttonClass","dropdownId","map","item","buildMenuItemHTML","join","separator","label","itemClass","danger","disabled","href","target","onActionMenuItemClick","getAttribute","menuItem","find","handler","parent","events","dispatch","closeDropdown","dropdownTrigger","dropdownInstance","bootstrap","Dropdown","getInstance","hide"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./Collection-CyK0u557.js");class DataView extends e.View{constructor(e={}){const{data:t,model:s,fields:i,columns:a,responsive:n,showEmptyValues:r,emptyValueText:l,...o}=e;super({tagName:"div",className:"data-view",...o}),this.data=t||{},this.fields=i||[],this.model=s||null,this.model&&(this.data=this.model),this.dataViewOptions={columns:a||2,responsive:!1!==n,showEmptyValues:r||!1,emptyValueText:l||"—",rowClass:"row g-3",itemClass:"data-view-item",labelClass:"data-view-label fw-semibold text-muted small text-uppercase",valueClass:"data-view-value"}}async onBeforeRender(){0===this.fields.length&&this.getData()&&this.generateFieldsFromData()}async renderTemplate(){const e=this.buildItemsHTML();return`\n <div class="${this.dataViewOptions.rowClass}">\n ${e}\n </div>\n `}generateFieldsFromData(){const e=this.getData();e&&"object"==typeof e&&(this.fields=Object.keys(e).map(t=>{const s=e[t],i=this.inferFieldType(s,t),a=this.inferFormatter(s,t,i);return{name:t,label:this.formatLabel(t),type:i,format:a,formatter:a}}))}formatLabel(e){return e.replace(/([A-Z])/g," $1").replace(/[_-]/g," ").replace(/\b\w/g,e=>e.toUpperCase()).trim()}inferFieldType(e,t=""){if(null==e)return"text";const s=t.toLowerCase(),i=typeof e;if(s.includes("date")||s.includes("time")||s.includes("created")||s.includes("updated")||s.includes("modified")||s.includes("last_login")||s.includes("expires")||s.includes("last_activity"))return"datetime";if(s.includes("email")||s.includes("mail"))return"email";if(s.includes("url")||s.includes("link")||s.includes("website")||s.includes("homepage"))return"url";if(s.includes("phone")||s.includes("tel")||s.includes("mobile")||s.includes("cell"))return"phone";if(s.includes("price")||s.includes("cost")||s.includes("amount")||s.includes("fee")||s.includes("salary")||s.includes("revenue"))return"currency";if(s.includes("size")||s.includes("bytes"))return"filesize";if(s.includes("percent")||s.includes("rate")||s.includes("ratio")&&"number"===i)return"percent";if("boolean"===i)return"boolean";if("number"===i)return"number";if("object"===i)return Array.isArray(e)?"array":e&&e.renditions?"file":this.shouldUseDataView(e,s)?"dataview":"object";if("string"===i){if(e.includes("@")&&e.includes("."))return"email";if(e.match(/^\d{4}-\d{2}-\d{2}/))return"date";if(e.match(/^https?:\/\//))return"url";if(e.match(/^\+?[\d\s\-\(\)]+$/))return"phone"}return"text"}inferFormatter(e,t,s){const i=t.toLowerCase(),a=[];switch(s){case"datetime":i.includes("time")&&!i.includes("date")?a.push("time"):i.includes("relative")||i.includes("ago")||i.includes("last_")?a.push("relative"):i.includes("created")||i.includes("updated")||i.includes("modified")?a.push('date("MMM D, YYYY")'):a.push('date("MMMM D, YYYY")');break;case"date":i.includes("birth")||i.includes("dob")?a.push('date("MMMM D, YYYY")'):a.push('date("MMM D, YYYY")');break;case"email":case"url":case"boolean":case"array":case"object":case"dataview":break;case"phone":a.push("phone");break;case"currency":a.push("currency"),i.includes("eur")||i.includes("euro")?a[a.length-1]='currency("EUR")':(i.includes("gbp")||i.includes("pound"))&&(a[a.length-1]='currency("GBP")');break;case"filesize":a.push("filesize");break;case"percent":a.push("percent");break;case"number":if("number"==typeof e)if(i.includes("count")||i.includes("total")||i.includes("followers")||i.includes("views"))e>=1e3?a.push("compact"):a.push("number");else if(i.includes("score")||i.includes("rating"))a.push("number"),e%1!=0&&(a[a.length-1]="number(1)");else{if(i.includes("version")||i.includes("id"))return null;a.push("number")}break;case"text":"string"==typeof e&&(i.includes("description")||i.includes("content")||i.includes("body")?e.length>200?a.push("truncate(200)"):e.length>100&&a.push("truncate(100)"):i.includes("summary")||i.includes("excerpt")?e.length>150&&a.push("truncate(150)"):i.includes("name")||i.includes("title")||i.includes("label")?(a.push("capitalize"),e.length>50&&a.unshift("truncate(50)")):i.includes("slug")||i.includes("handle")||i.includes("username")?a.push("slug"):i.includes("code")||i.includes("token")||i.includes("key")?e.length>20&&a.push("mask"):e.length>100&&a.push("truncate(100)"));break;default:"string"==typeof e&&e.length>100&&a.push("truncate(100)")}return a.length>0?a.join("|"):null}shouldUseDataView(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return!1;if(window.utils&&window.utils.isObject(e)&&e.id)return!0;if(["permissions","perms","access","rights","settings","config","configuration","options","profile","info","details","data","metadata","meta","attributes","props","preferences","prefs","user_data","contact","address","location","stats","statistics","metrics","counts"].some(e=>t.includes(e))){const t=Object.keys(e);if(t.length>=2&&t.length<=20&&!t.some(t=>"object"==typeof e[t]&&null!==e[t]&&!Array.isArray(e[t])&&Object.keys(e[t]).length>3))return!0}return!1}getData(){return this.model&&this.model.attributes?{...this.model.attributes}:this.data||{}}getFieldValue(t){let s,i=t.name||t.key,a=t.format||t.formatter;if(!i)return null;if(i&&i.includes("|")){const e=i.split("|");i=e[0].trim(),a||(a=e.slice(1).join("|").trim())}if(s=this.model&&"function"==typeof this.model.get?this.model.get(i):this.getData()[i],a&&(s=e.dataFormatter.pipe(s,a)),null==s||""===s)return this.dataViewOptions.showEmptyValues?this.dataViewOptions.emptyValueText:null;if(t.template){const e=this.model?this.model:this.data;return this.renderTemplateString(t.template,e)}return s}renderTemplateString(t,s){return t&&s?t.replace(/\{\{([^}]+)\}\}/g,(t,i)=>{let a;const n=i.trim().split("|"),r=n[0],l=n.slice(1).join("|");return a=this.model&&"function"==typeof this.model.get?this.model.get(r):r.split(".").reduce((e,t)=>e?e[t]:void 0,s),l&&(a=e.dataFormatter.pipe(a,l)),null!=a?a:""}):""}getColumnClasses(e){let t=this.getColumnSizeClasses(e);return"right"==e.justify?t+=" d-flex justify-content-end":"center"==e.justify&&(t+=" d-flex justify-content-center"),t}getColumnSizeClasses(e){if("array"===e.type||"object"===e.type||"dataview"===e.type)return"col-12";const t=e.columns||e.colSize||e.cols||Math.floor(12/this.dataViewOptions.columns);return this.dataViewOptions.responsive?`col-12 col-md-${t}`:`col-${t}`}buildItemsHTML(){return this.fields.map(e=>this.buildItemHTML(e)).filter(Boolean).join("")}buildItemHTML(e){const t=this.getFieldValue(e);if(null===t&&!this.dataViewOptions.showEmptyValues)return"";const s=e.label||this.formatLabel(e.name);return`\n <div class="${this.getColumnClasses(e)}">\n <div class="${this.dataViewOptions.itemClass} ${e.className}" data-field="${e.name}">\n ${this.buildLabelHTML(s,e)}\n ${this.buildValueHTML(t,e)}\n </div>\n </div>\n `}buildLabelHTML(e,t){return`<div class="${t.labelClass||this.dataViewOptions.labelClass}">${this.escapeHtml(e)}:</div>`}buildValueHTML(e,t){return`<div class="${t.valueClass||this.dataViewOptions.valueClass}">${this.formatDisplayValue(e,t)}</div>`}formatDisplayValue(e,t){if(null==e)return this.dataViewOptions.emptyValueText;if(t.template)return String(e);if(t.format||t.formatter)return String(e);const s=this.getData()[t.name];switch(t.type){case"boolean":return s?'<span class="badge bg-success">Yes</span>':'<span class="badge bg-secondary">No</span>';case"email":const i=String(e);return`<a href="mailto:${this.escapeHtml(i)}" class="text-decoration-none">${this.escapeHtml(i)}</a>`;case"url":const a=String(e);return`<a href="${this.escapeHtml(a)}" target="_blank" rel="noopener" class="text-decoration-none">${this.escapeHtml(a)} <i class="bi bi-box-arrow-up-right"></i></a>`;case"array":case"object":return this.formatAsJson(s);case"dataview":return this.formatAsDataView(s,t);case"phone":const n=String(e);return`<a href="tel:${n.replace(/[^\d\+]/g,"")}" class="text-decoration-none">${this.escapeHtml(n)}</a>`;default:return this.escapeHtml(String(e))}}formatAsJson(e){try{const t=JSON.stringify(e,null,2),s=this.escapeHtml(t),i=t.split("\n").length,a=i>10||t.length>500,n=`json-${Math.random().toString(36).substr(2,9)}`;if(a){const a=JSON.stringify(e).substring(0,100)+(JSON.stringify(e).length>100?"...":""),r=this.escapeHtml(a);return`\n <div class="json-container">\n <div class="d-flex align-items-center justify-content-between mb-1">\n <small class="text-muted">${Array.isArray(e)?"Array":"Object"} (${i} lines)</small>\n <div class="btn-group btn-group-sm" role="group">\n <button type="button" class="btn btn-outline-secondary btn-sm json-toggle" data-bs-toggle="collapse" data-bs-target="#${n}" aria-expanded="false">\n <i class="bi bi-eye"></i> Show\n </button>\n <button type="button" class="btn btn-outline-secondary btn-sm json-copy" data-json='${this.escapeHtml(t)}' title="Copy JSON">\n <i class="bi bi-clipboard"></i>\n </button>\n </div>\n </div>\n <div class="json-preview bg-light p-2 rounded small border" style="font-family: 'Courier New', monospace;">\n <code class="text-muted">${r}</code>\n </div>\n <div class="collapse mt-2" id="${n}">\n <pre class="json-display p-3 rounded small mb-0" style="max-height: 400px; overflow-y: auto; white-space: pre-wrap; font-family: 'Courier New', monospace;"><code>${this.syntaxHighlightJson(s)}</code></pre>\n </div>\n </div>\n `}return`\n <div class="json-container">\n <div class="d-flex align-items-center justify-content-between mb-1">\n <small class="text-muted">${Array.isArray(e)?"Array":"Object"}</small>\n <button type="button" class="btn btn-outline-secondary btn-sm json-copy" data-json='${this.escapeHtml(t)}' title="Copy JSON">\n <i class="bi bi-clipboard"></i>\n </button>\n </div>\n <pre class="json-display bg-light p-2 rounded small mb-0 border" style="white-space: pre-wrap; font-family: 'Courier New', monospace;"><code>${this.syntaxHighlightJson(s)}</code></pre>\n </div>\n `}catch(t){return`<span class="text-muted fst-italic">[Object: ${typeof e}] - Cannot display as JSON</span>`}}syntaxHighlightJson(e){return e.replace(/("([^"\\]|\\.)*")\s*:/g,'<span style="color: #0969da;">$1</span>:').replace(/:\s*("([^"\\]|\\.)*")/g,': <span style="color: #0a3069;">$1</span>').replace(/:\s*(true|false)/g,': <span style="color: #8250df;">$1</span>').replace(/:\s*(null)/g,': <span style="color: #656d76;">$1</span>').replace(/:\s*(-?\d+\.?\d*)/g,': <span style="color: #0550ae;">$1</span>')}bindEvents(){super.bindEvents(),this.element&&this.element.addEventListener("click",e=>{const t=e.target.closest("[data-field]");if(t){const s=t.dataset.field,i=this.fields.find(e=>e.name===s);this.emit("field:click",{field:i,fieldName:s,element:t,event:e})}e.target.closest(".json-copy")&&(e.preventDefault(),e.stopPropagation(),this.handleJsonCopy(e.target.closest(".json-copy"))),e.target.closest(".json-toggle")&&this.handleJsonToggle(e.target.closest(".json-toggle"))})}handleJsonCopy(e){const t=e.getAttribute("data-json");if(t)try{if(navigator.clipboard&&window.isSecureContext)navigator.clipboard.writeText(t).then(()=>{this.showCopyFeedback(e)});else{const s=document.createElement("textarea");s.value=t,document.body.appendChild(s),s.select(),document.execCommand("copy"),document.body.removeChild(s),this.showCopyFeedback(e)}}catch(s){console.warn("Failed to copy JSON:",s)}}handleJsonToggle(e){const t=e.querySelector("i"),s="true"===e.getAttribute("aria-expanded");setTimeout(()=>{s?(t.className="bi bi-eye-slash",e.innerHTML='<i class="bi bi-eye-slash"></i> Hide'):(t.className="bi bi-eye",e.innerHTML='<i class="bi bi-eye"></i> Show')},10)}showCopyFeedback(e){const t=e.querySelector("i").className,s=e.querySelector("i");s.className="bi bi-check text-success",e.classList.add("btn-success"),e.classList.remove("btn-outline-secondary"),setTimeout(()=>{s.className=t,e.classList.remove("btn-success"),e.classList.add("btn-outline-secondary")},1e3)}formatAsDataView(e,t){if(!e||"object"!=typeof e)return'<span class="text-muted fst-italic">No data available</span>';try{const s=new this.constructor({data:e,columns:t.dataViewColumns||2,showEmptyValues:t.showEmptyValues??!0,emptyValueText:t.emptyValueText||"Not set",...t.dataViewOptions||{}});s.onInit(),s.generateFieldsFromData();const i=s.buildItemsHTML();return`\n <div class="nested-dataview border rounded p-3 bg-light">\n <div class="${s.dataViewOptions.rowClass}">\n ${i}\n </div>\n </div>\n `}catch(s){return console.error("Error creating nested DataView:",s),'<span class="text-danger">Error displaying nested data</span>'}}async updateData(e){return this.data=e,this.model&&"function"==typeof this.model.set&&this.model.set(e),this.fields.length>0&&!this.options.fields&&(this.fields=[]),await this.render(),this.emit("data:updated",{data:e}),this}async updateFields(e){return this.fields=e,await this.render(),this.emit("fields:updated",{fields:e}),this}async updateConfig(e){return this.dataViewOptions={...this.dataViewOptions,...e},await this.render(),this.emit("config:updated",{options:this.dataViewOptions}),this}async refresh(){if(this.model&&"function"==typeof this.model.fetch)try{await this.model.fetch(),this.emit("data:refreshed",{model:this.model})}catch(e){throw this.emit("error",{error:e,message:"Failed to refresh data"}),e}return this}getCurrentData(){return this.getData()}getField(e){return this.fields.find(t=>t.name===e)||null}setFieldFormat(e,t){const s=this.getField(e);return s?(s.format=t,s.formatter=t):this.fields.push({name:e,label:this.formatLabel(e),type:this.inferFieldType(this.getData()[e],e),format:t,formatter:t}),this}addFormatPipe(e,t){const s=this.getField(e);return s&&(s.format?(s.format+=`|${t}`,s.formatter=s.format):(s.format=t,s.formatter=t)),this}clearFieldFormat(e){const t=this.getField(e);if(t){const s=this.getData(),i=this.inferFormatter(s[e],e,t.type);t.format=i,t.formatter=i}return this}getFormattedValue(t,s=null){const i=this.getField(t);if(!i)return null;const a=null!==s?s:this.getData()[t],n=i.format||i.formatter;return n&&null!=a?e.dataFormatter.pipe(a,n):a}setFieldFormats(e){return Object.entries(e).forEach(([e,t])=>{this.setFieldFormat(e,t)}),this}getFieldFormats(){const e={};return this.fields.forEach(t=>{const s=t.format||t.formatter;s&&(e[t.name]=s)}),e}onInit(){super.onInit(),this.model&&"function"==typeof this.model.on&&this.model.on("change",()=>{this.isMounted()&&this.render()})}static create(e={}){return new DataView(e)}}exports.default=DataView;
|
|
2
|
-
//# sourceMappingURL=DataView-BFx2glFg.js.map
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{V as e,d as t}from"./Collection-C39Oy2q0.js";class DataView extends e{constructor(e={}){const{data:t,model:s,fields:i,columns:a,responsive:n,showEmptyValues:l,emptyValueText:r,...o}=e;super({tagName:"div",className:"data-view",...o}),this.data=t||{},this.fields=i||[],this.model=s||null,this.model&&(this.data=this.model),this.dataViewOptions={columns:a||2,responsive:!1!==n,showEmptyValues:l||!1,emptyValueText:r||"—",rowClass:"row g-3",itemClass:"data-view-item",labelClass:"data-view-label fw-semibold text-muted small text-uppercase",valueClass:"data-view-value"}}async onBeforeRender(){0===this.fields.length&&this.getData()&&this.generateFieldsFromData()}async renderTemplate(){const e=this.buildItemsHTML();return`\n <div class="${this.dataViewOptions.rowClass}">\n ${e}\n </div>\n `}generateFieldsFromData(){const e=this.getData();e&&"object"==typeof e&&(this.fields=Object.keys(e).map(t=>{const s=e[t],i=this.inferFieldType(s,t),a=this.inferFormatter(s,t,i);return{name:t,label:this.formatLabel(t),type:i,format:a,formatter:a}}))}formatLabel(e){return e.replace(/([A-Z])/g," $1").replace(/[_-]/g," ").replace(/\b\w/g,e=>e.toUpperCase()).trim()}inferFieldType(e,t=""){if(null==e)return"text";const s=t.toLowerCase(),i=typeof e;if(s.includes("date")||s.includes("time")||s.includes("created")||s.includes("updated")||s.includes("modified")||s.includes("last_login")||s.includes("expires")||s.includes("last_activity"))return"datetime";if(s.includes("email")||s.includes("mail"))return"email";if(s.includes("url")||s.includes("link")||s.includes("website")||s.includes("homepage"))return"url";if(s.includes("phone")||s.includes("tel")||s.includes("mobile")||s.includes("cell"))return"phone";if(s.includes("price")||s.includes("cost")||s.includes("amount")||s.includes("fee")||s.includes("salary")||s.includes("revenue"))return"currency";if(s.includes("size")||s.includes("bytes"))return"filesize";if(s.includes("percent")||s.includes("rate")||s.includes("ratio")&&"number"===i)return"percent";if("boolean"===i)return"boolean";if("number"===i)return"number";if("object"===i)return Array.isArray(e)?"array":e&&e.renditions?"file":this.shouldUseDataView(e,s)?"dataview":"object";if("string"===i){if(e.includes("@")&&e.includes("."))return"email";if(e.match(/^\d{4}-\d{2}-\d{2}/))return"date";if(e.match(/^https?:\/\//))return"url";if(e.match(/^\+?[\d\s\-\(\)]+$/))return"phone"}return"text"}inferFormatter(e,t,s){const i=t.toLowerCase(),a=[];switch(s){case"datetime":i.includes("time")&&!i.includes("date")?a.push("time"):i.includes("relative")||i.includes("ago")||i.includes("last_")?a.push("relative"):i.includes("created")||i.includes("updated")||i.includes("modified")?a.push('date("MMM D, YYYY")'):a.push('date("MMMM D, YYYY")');break;case"date":i.includes("birth")||i.includes("dob")?a.push('date("MMMM D, YYYY")'):a.push('date("MMM D, YYYY")');break;case"email":case"url":case"boolean":case"array":case"object":case"dataview":break;case"phone":a.push("phone");break;case"currency":a.push("currency"),i.includes("eur")||i.includes("euro")?a[a.length-1]='currency("EUR")':(i.includes("gbp")||i.includes("pound"))&&(a[a.length-1]='currency("GBP")');break;case"filesize":a.push("filesize");break;case"percent":a.push("percent");break;case"number":if("number"==typeof e)if(i.includes("count")||i.includes("total")||i.includes("followers")||i.includes("views"))e>=1e3?a.push("compact"):a.push("number");else if(i.includes("score")||i.includes("rating"))a.push("number"),e%1!=0&&(a[a.length-1]="number(1)");else{if(i.includes("version")||i.includes("id"))return null;a.push("number")}break;case"text":"string"==typeof e&&(i.includes("description")||i.includes("content")||i.includes("body")?e.length>200?a.push("truncate(200)"):e.length>100&&a.push("truncate(100)"):i.includes("summary")||i.includes("excerpt")?e.length>150&&a.push("truncate(150)"):i.includes("name")||i.includes("title")||i.includes("label")?(a.push("capitalize"),e.length>50&&a.unshift("truncate(50)")):i.includes("slug")||i.includes("handle")||i.includes("username")?a.push("slug"):i.includes("code")||i.includes("token")||i.includes("key")?e.length>20&&a.push("mask"):e.length>100&&a.push("truncate(100)"));break;default:"string"==typeof e&&e.length>100&&a.push("truncate(100)")}return a.length>0?a.join("|"):null}shouldUseDataView(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return!1;if(window.utils&&window.utils.isObject(e)&&e.id)return!0;if(["permissions","perms","access","rights","settings","config","configuration","options","profile","info","details","data","metadata","meta","attributes","props","preferences","prefs","user_data","contact","address","location","stats","statistics","metrics","counts"].some(e=>t.includes(e))){const t=Object.keys(e);if(t.length>=2&&t.length<=20&&!t.some(t=>"object"==typeof e[t]&&null!==e[t]&&!Array.isArray(e[t])&&Object.keys(e[t]).length>3))return!0}return!1}getData(){return this.model&&this.model.attributes?{...this.model.attributes}:this.data||{}}getFieldValue(e){let s,i=e.name||e.key,a=e.format||e.formatter;if(!i)return null;if(i&&i.includes("|")){const e=i.split("|");i=e[0].trim(),a||(a=e.slice(1).join("|").trim())}if(s=this.model&&"function"==typeof this.model.get?this.model.get(i):this.getData()[i],a&&(s=t.pipe(s,a)),null==s||""===s)return this.dataViewOptions.showEmptyValues?this.dataViewOptions.emptyValueText:null;if(e.template){const t=this.model?this.model:this.data;return this.renderTemplateString(e.template,t)}return s}renderTemplateString(e,s){return e&&s?e.replace(/\{\{([^}]+)\}\}/g,(e,i)=>{let a;const n=i.trim().split("|"),l=n[0],r=n.slice(1).join("|");return a=this.model&&"function"==typeof this.model.get?this.model.get(l):l.split(".").reduce((e,t)=>e?e[t]:void 0,s),r&&(a=t.pipe(a,r)),null!=a?a:""}):""}getColumnClasses(e){let t=this.getColumnSizeClasses(e);return"right"==e.justify?t+=" d-flex justify-content-end":"center"==e.justify&&(t+=" d-flex justify-content-center"),t}getColumnSizeClasses(e){if("array"===e.type||"object"===e.type||"dataview"===e.type)return"col-12";const t=e.columns||e.colSize||e.cols||Math.floor(12/this.dataViewOptions.columns);return this.dataViewOptions.responsive?`col-12 col-md-${t}`:`col-${t}`}buildItemsHTML(){return this.fields.map(e=>this.buildItemHTML(e)).filter(Boolean).join("")}buildItemHTML(e){const t=this.getFieldValue(e);if(null===t&&!this.dataViewOptions.showEmptyValues)return"";const s=e.label||this.formatLabel(e.name);return`\n <div class="${this.getColumnClasses(e)}">\n <div class="${this.dataViewOptions.itemClass} ${e.className}" data-field="${e.name}">\n ${this.buildLabelHTML(s,e)}\n ${this.buildValueHTML(t,e)}\n </div>\n </div>\n `}buildLabelHTML(e,t){return`<div class="${t.labelClass||this.dataViewOptions.labelClass}">${this.escapeHtml(e)}:</div>`}buildValueHTML(e,t){return`<div class="${t.valueClass||this.dataViewOptions.valueClass}">${this.formatDisplayValue(e,t)}</div>`}formatDisplayValue(e,t){if(null==e)return this.dataViewOptions.emptyValueText;if(t.template)return String(e);if(t.format||t.formatter)return String(e);const s=this.getData()[t.name];switch(t.type){case"boolean":return s?'<span class="badge bg-success">Yes</span>':'<span class="badge bg-secondary">No</span>';case"email":const i=String(e);return`<a href="mailto:${this.escapeHtml(i)}" class="text-decoration-none">${this.escapeHtml(i)}</a>`;case"url":const a=String(e);return`<a href="${this.escapeHtml(a)}" target="_blank" rel="noopener" class="text-decoration-none">${this.escapeHtml(a)} <i class="bi bi-box-arrow-up-right"></i></a>`;case"array":case"object":return this.formatAsJson(s);case"dataview":return this.formatAsDataView(s,t);case"phone":const n=String(e);return`<a href="tel:${n.replace(/[^\d\+]/g,"")}" class="text-decoration-none">${this.escapeHtml(n)}</a>`;default:return this.escapeHtml(String(e))}}formatAsJson(e){try{const t=JSON.stringify(e,null,2),s=this.escapeHtml(t),i=t.split("\n").length,a=i>10||t.length>500,n=`json-${Math.random().toString(36).substr(2,9)}`;if(a){const a=JSON.stringify(e).substring(0,100)+(JSON.stringify(e).length>100?"...":""),l=this.escapeHtml(a);return`\n <div class="json-container">\n <div class="d-flex align-items-center justify-content-between mb-1">\n <small class="text-muted">${Array.isArray(e)?"Array":"Object"} (${i} lines)</small>\n <div class="btn-group btn-group-sm" role="group">\n <button type="button" class="btn btn-outline-secondary btn-sm json-toggle" data-bs-toggle="collapse" data-bs-target="#${n}" aria-expanded="false">\n <i class="bi bi-eye"></i> Show\n </button>\n <button type="button" class="btn btn-outline-secondary btn-sm json-copy" data-json='${this.escapeHtml(t)}' title="Copy JSON">\n <i class="bi bi-clipboard"></i>\n </button>\n </div>\n </div>\n <div class="json-preview bg-light p-2 rounded small border" style="font-family: 'Courier New', monospace;">\n <code class="text-muted">${l}</code>\n </div>\n <div class="collapse mt-2" id="${n}">\n <pre class="json-display p-3 rounded small mb-0" style="max-height: 400px; overflow-y: auto; white-space: pre-wrap; font-family: 'Courier New', monospace;"><code>${this.syntaxHighlightJson(s)}</code></pre>\n </div>\n </div>\n `}return`\n <div class="json-container">\n <div class="d-flex align-items-center justify-content-between mb-1">\n <small class="text-muted">${Array.isArray(e)?"Array":"Object"}</small>\n <button type="button" class="btn btn-outline-secondary btn-sm json-copy" data-json='${this.escapeHtml(t)}' title="Copy JSON">\n <i class="bi bi-clipboard"></i>\n </button>\n </div>\n <pre class="json-display bg-light p-2 rounded small mb-0 border" style="white-space: pre-wrap; font-family: 'Courier New', monospace;"><code>${this.syntaxHighlightJson(s)}</code></pre>\n </div>\n `}catch(t){return`<span class="text-muted fst-italic">[Object: ${typeof e}] - Cannot display as JSON</span>`}}syntaxHighlightJson(e){return e.replace(/("([^"\\]|\\.)*")\s*:/g,'<span style="color: #0969da;">$1</span>:').replace(/:\s*("([^"\\]|\\.)*")/g,': <span style="color: #0a3069;">$1</span>').replace(/:\s*(true|false)/g,': <span style="color: #8250df;">$1</span>').replace(/:\s*(null)/g,': <span style="color: #656d76;">$1</span>').replace(/:\s*(-?\d+\.?\d*)/g,': <span style="color: #0550ae;">$1</span>')}bindEvents(){super.bindEvents(),this.element&&this.element.addEventListener("click",e=>{const t=e.target.closest("[data-field]");if(t){const s=t.dataset.field,i=this.fields.find(e=>e.name===s);this.emit("field:click",{field:i,fieldName:s,element:t,event:e})}e.target.closest(".json-copy")&&(e.preventDefault(),e.stopPropagation(),this.handleJsonCopy(e.target.closest(".json-copy"))),e.target.closest(".json-toggle")&&this.handleJsonToggle(e.target.closest(".json-toggle"))})}handleJsonCopy(e){const t=e.getAttribute("data-json");if(t)try{if(navigator.clipboard&&window.isSecureContext)navigator.clipboard.writeText(t).then(()=>{this.showCopyFeedback(e)});else{const s=document.createElement("textarea");s.value=t,document.body.appendChild(s),s.select(),document.execCommand("copy"),document.body.removeChild(s),this.showCopyFeedback(e)}}catch(s){console.warn("Failed to copy JSON:",s)}}handleJsonToggle(e){const t=e.querySelector("i"),s="true"===e.getAttribute("aria-expanded");setTimeout(()=>{s?(t.className="bi bi-eye-slash",e.innerHTML='<i class="bi bi-eye-slash"></i> Hide'):(t.className="bi bi-eye",e.innerHTML='<i class="bi bi-eye"></i> Show')},10)}showCopyFeedback(e){const t=e.querySelector("i").className,s=e.querySelector("i");s.className="bi bi-check text-success",e.classList.add("btn-success"),e.classList.remove("btn-outline-secondary"),setTimeout(()=>{s.className=t,e.classList.remove("btn-success"),e.classList.add("btn-outline-secondary")},1e3)}formatAsDataView(e,t){if(!e||"object"!=typeof e)return'<span class="text-muted fst-italic">No data available</span>';try{const s=new this.constructor({data:e,columns:t.dataViewColumns||2,showEmptyValues:t.showEmptyValues??!0,emptyValueText:t.emptyValueText||"Not set",...t.dataViewOptions||{}});s.onInit(),s.generateFieldsFromData();const i=s.buildItemsHTML();return`\n <div class="nested-dataview border rounded p-3 bg-light">\n <div class="${s.dataViewOptions.rowClass}">\n ${i}\n </div>\n </div>\n `}catch(s){return console.error("Error creating nested DataView:",s),'<span class="text-danger">Error displaying nested data</span>'}}async updateData(e){return this.data=e,this.model&&"function"==typeof this.model.set&&this.model.set(e),this.fields.length>0&&!this.options.fields&&(this.fields=[]),await this.render(),this.emit("data:updated",{data:e}),this}async updateFields(e){return this.fields=e,await this.render(),this.emit("fields:updated",{fields:e}),this}async updateConfig(e){return this.dataViewOptions={...this.dataViewOptions,...e},await this.render(),this.emit("config:updated",{options:this.dataViewOptions}),this}async refresh(){if(this.model&&"function"==typeof this.model.fetch)try{await this.model.fetch(),this.emit("data:refreshed",{model:this.model})}catch(e){throw this.emit("error",{error:e,message:"Failed to refresh data"}),e}return this}getCurrentData(){return this.getData()}getField(e){return this.fields.find(t=>t.name===e)||null}setFieldFormat(e,t){const s=this.getField(e);return s?(s.format=t,s.formatter=t):this.fields.push({name:e,label:this.formatLabel(e),type:this.inferFieldType(this.getData()[e],e),format:t,formatter:t}),this}addFormatPipe(e,t){const s=this.getField(e);return s&&(s.format?(s.format+=`|${t}`,s.formatter=s.format):(s.format=t,s.formatter=t)),this}clearFieldFormat(e){const t=this.getField(e);if(t){const s=this.getData(),i=this.inferFormatter(s[e],e,t.type);t.format=i,t.formatter=i}return this}getFormattedValue(e,s=null){const i=this.getField(e);if(!i)return null;const a=null!==s?s:this.getData()[e],n=i.format||i.formatter;return n&&null!=a?t.pipe(a,n):a}setFieldFormats(e){return Object.entries(e).forEach(([e,t])=>{this.setFieldFormat(e,t)}),this}getFieldFormats(){const e={};return this.fields.forEach(t=>{const s=t.format||t.formatter;s&&(e[t.name]=s)}),e}onInit(){super.onInit(),this.model&&"function"==typeof this.model.on&&this.model.on("change",()=>{this.isMounted()&&this.render()})}static create(e={}){return new DataView(e)}}export{DataView as default};
|
|
2
|
-
//# sourceMappingURL=DataView-D5C_lDdg.js.map
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{V as e,d as t,C as s,M as i}from"./Collection-C39Oy2q0.js";class ToastService{constructor(e={}){this.options={containerId:"toast-container",position:"top-end",autohide:!0,defaultDelay:3e3,maxToasts:5,...e},this.toasts=/* @__PURE__ */new Map,this.toastCounter=0,this.init()}init(){this.createContainer()}createContainer(){let e=document.getElementById(this.options.containerId);e||(e=document.createElement("div"),e.id=this.options.containerId,e.className=`toast-container position-fixed ${this.getPositionClasses()}`,e.style.zIndex="1070",e.setAttribute("aria-live","polite"),e.setAttribute("aria-atomic","true"),document.body.appendChild(e)),this.container=e}getPositionClasses(){const e={"top-start":"top-0 start-0 p-3","top-center":"top-0 start-50 translate-middle-x p-3","top-end":"top-0 end-0 p-3","middle-start":"top-50 start-0 translate-middle-y p-3","middle-center":"top-50 start-50 translate-middle p-3","middle-end":"top-50 end-0 translate-middle-y p-3","bottom-start":"bottom-0 start-0 p-3","bottom-center":"bottom-0 start-50 translate-middle-x p-3","bottom-end":"bottom-0 end-0 p-3"};return e[this.options.position]||e["top-end"]}success(e,t={}){return this.show(e,"success",{icon:"bi-check-circle-fill",...t})}error(e,t={}){return this.show(e,"error",{icon:"bi-exclamation-triangle-fill",autohide:!0,...t})}info(e,t={}){return this.show(e,"info",{icon:"bi-info-circle-fill",...t})}warning(e,t={}){return this.show(e,"warning",{icon:"bi-exclamation-triangle-fill",...t})}plain(e,t={}){return this.show(e,"plain",{...t})}show(e,t="info",s={}){this.enforceMaxToasts();const i="toast-"+ ++this.toastCounter,o={title:this.getDefaultTitle(t),icon:this.getDefaultIcon(t),autohide:this.options.autohide,delay:this.options.defaultDelay,dismissible:!0,...s},a=this.createToastElement(i,e,t,o);if(this.container.appendChild(a),"undefined"==typeof bootstrap)throw new Error("Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.");const n=new bootstrap.Toast(a,{autohide:o.autohide,delay:o.delay});return this.toasts.set(i,{element:a,bootstrap:n,type:t,message:e}),a.addEventListener("hidden.bs.toast",()=>{this.cleanup(i)}),n.show(),{id:i,hide:()=>{try{n.hide()}catch(e){console.warn("Error hiding toast:",e)}},dispose:()=>this.cleanup(i),updateProgress:s.updateProgress||null}}showView(e,t="info",s={}){this.enforceMaxToasts();const i="toast-"+ ++this.toastCounter,o={title:s.title||this.getDefaultTitle(t),icon:s.icon||this.getDefaultIcon(t),autohide:this.options.autohide,delay:this.options.defaultDelay,dismissible:!0,size:"md",...s},a=this.createViewToastElement(i,e,t,o);if(this.container.appendChild(a),"undefined"==typeof bootstrap)throw new Error("Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.");const n=new bootstrap.Toast(a,{autohide:o.autohide,delay:o.delay});this.toasts.set(i,{element:a,bootstrap:n,type:t,view:e,message:"View toast"}),a.addEventListener("hidden.bs.toast",()=>{this.cleanupView(i)});const l=a.querySelector(".toast-view-body");return l&&e&&e.render(!0,l),n.show(),{id:i,view:e,hide:()=>{try{n.hide()}catch(e){console.warn("Error hiding view toast:",e)}},dispose:()=>this.cleanupView(i),updateProgress:t=>{e&&"function"==typeof e.updateProgress&&e.updateProgress(t)}}}createToastElement(e,t,s,i){const o=document.createElement("div");o.id=e,o.className=`toast toast-service-${s}${i.size?` toast-${i.size}`:""}`,o.setAttribute("role","alert"),o.setAttribute("aria-live","assertive"),o.setAttribute("aria-atomic","true");const a=i.title||i.icon?this.createToastHeader(i,s):"",n=this.createToastBody(t,i.icon&&!i.title);return o.innerHTML=`\n ${a}\n ${n}\n `,o}createViewToastElement(e,t,s,i){const o=document.createElement("div");o.id=e,o.className=`toast toast-service-${s}${i.size?` toast-${i.size}`:""}`,o.setAttribute("role","alert"),o.setAttribute("aria-live","assertive"),o.setAttribute("aria-atomic","true");const a=i.title||i.icon?this.createToastHeader(i,s):"",n=this.createViewToastBody();return o.innerHTML=`\n ${a}\n ${n}\n `,o}createViewToastBody(){return'\n <div class="toast-body p-0">\n <div class="toast-view-body p-3"></div>\n </div>\n '}createToastHeader(e,t){const s=e.icon?`<i class="${e.icon} toast-service-icon me-2"></i>`:"",i=e.title?`<strong class="me-auto">${s}${this.escapeHtml(e.title)}</strong>`:"",o=e.showTime?`<small class="text-muted">${this.getTimeString()}</small>`:"",a=e.dismissible?'<button type="button" class="btn-close toast-service-close" data-bs-dismiss="toast" aria-label="Close"></button>':"";return i||o||a?`\n <div class="toast-header">\n ${i}\n ${o}\n ${a}\n </div>\n `:""}createToastBody(e,t=!1){return`\n <div class="toast-body d-flex align-items-center">\n ${t?`<i class="${this.getDefaultIcon("info")} toast-service-icon me-2"></i>`:""}\n <span>${this.escapeHtml(e)}</span>\n </div>\n `}getDefaultTitle(e){return{success:"Success",error:"Error",warning:"Warning",info:"Information",plain:""}[e]||"Notification"}getDefaultIcon(e){return{success:"bi-check-circle-fill",error:"bi-exclamation-triangle-fill",warning:"bi-exclamation-triangle-fill",info:"bi-info-circle-fill",plain:""}[e]||"bi-info-circle-fill"}enforceMaxToasts(){if(Array.from(this.toasts.values()).length>=this.options.maxToasts){const e=this.toasts.keys().next().value,t=this.toasts.get(e);t&&t.bootstrap.hide()}}cleanup(e){const t=this.toasts.get(e);if(t){try{t.bootstrap.dispose()}catch(s){console.warn("Error disposing toast:",s)}t.element&&t.element.parentNode&&t.element.parentNode.removeChild(t.element),this.toasts.delete(e)}}cleanupView(e){const t=this.toasts.get(e);if(t){if(t.view&&"function"==typeof t.view.dispose)try{t.view.dispose()}catch(s){console.warn("Error disposing view in toast:",s)}try{t.bootstrap.dispose()}catch(s){console.warn("Error disposing toast:",s)}t.element&&t.element.parentNode&&t.element.parentNode.removeChild(t.element),this.toasts.delete(e)}}hideAll(){this.toasts.forEach((e,t)=>{e.bootstrap.hide()})}clearAll(){this.toasts.forEach((e,t)=>{this.cleanup(t)})}getTimeString(){/* @__PURE__ */
|
|
2
|
-
return(new Date).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}escapeHtml(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}dispose(){this.clearAll(),this.container&&this.container.parentNode&&this.container.parentNode.removeChild(this.container)}getStats(){const e={total:this.toasts.size,byType:{}};return this.toasts.forEach(t=>{e.byType[t.type]=(e.byType[t.type]||0)+1}),e}setOptions(e){this.options={...this.options,...e},e.position&&this.container&&(this.container.className=`toast-container position-fixed ${this.getPositionClasses()}`)}}class ProgressView extends e{constructor(e={}){super({template:"progress-view-template",...e}),this.filename=e.filename||"Unknown file",this.filesize=e.filesize||0,this.filesizeFormatted=t.pipe(this.filesize,"filesize"),this.progress=0,this.percentage=0,this.loaded=0,this.total=this.filesize,this.loadedFormatted="0 B",this.totalFormatted=this.filesizeFormatted,this.status="Starting upload...",this.showCancel=!1!==e.showCancel,this.onCancel=e.onCancel||null,this.cancelled=!1,this.completed=!1}getTemplate(){return'\n <div class="progress-view">\n <div class="d-flex justify-content-between align-items-start mb-2">\n <div class="flex-grow-1 min-width-0">\n <div class="fw-medium text-truncate" title="{{filename}}">\n <i class="bi bi-file-earmark me-1"></i>\n {{filename}}\n </div>\n <small class="text-muted">{{status}}</small>\n </div>\n {{#showCancel}}\n <button type="button" \n class="btn btn-sm btn-outline-secondary ms-2" \n data-action="cancel"\n {{#cancelled}}disabled{{/cancelled}}>\n <i class="bi bi-x"></i>\n </button>\n {{/showCancel}}\n </div>\n \n <div class="progress mb-2" style="height: 8px;">\n <div class="progress-bar" \n role="progressbar" \n style="width: {{percentage}}%"\n aria-valuenow="{{percentage}}" \n aria-valuemin="0" \n aria-valuemax="100">\n </div>\n </div>\n \n <div class="d-flex justify-content-between">\n <small class="text-muted">\n {{loadedFormatted}} / {{totalFormatted}}\n </small>\n <small class="text-muted">\n {{percentage}}%\n </small>\n </div>\n </div>\n '}updateProgress(e){this.cancelled||this.completed||(this.progress=e.progress,this.percentage=e.percentage,this.loaded=e.loaded,this.total=e.total||this.filesize,this.loadedFormatted=t.pipe(this.loaded,"filesize"),this.totalFormatted=t.pipe(this.total,"filesize"),this.percentage<100?this.status=`Uploading... ${this.percentage}%`:this.status="Finalizing upload...",this.render())}markCompleted(e="Upload completed!"){this.completed=!0,this.progress=1,this.percentage=100,this.status=e,this.render()}markFailed(e="Upload failed"){this.status=e,this.render()}markCancelled(){this.cancelled=!0,this.status="Upload cancelled",this.render()}async onActionCancel(e,t,s){if(!this.cancelled&&!this.completed&&(s.disabled=!0,this.markCancelled(),this.emit("cancel"),"function"==typeof this.onCancel))try{await this.onCancel()}catch(i){console.error("Error in cancel callback:",i)}}setFilename(e){this.filename=e,this.render()}setFilesize(e){this.filesize=e,this.filesizeFormatted=t.pipe(e,"filesize"),this.total=e,this.totalFormatted=this.filesizeFormatted,this.render()}getPercentage(){return this.percentage}isCompleted(){return this.completed}isCancelled(){return this.cancelled}getStats(){return{filename:this.filename,filesize:this.filesize,progress:this.progress,percentage:this.percentage,loaded:this.loaded,total:this.total,cancelled:this.cancelled,completed:this.completed,status:this.status}}}class FileUpload{constructor(e,t={}){if(this.fileModel=e,this.options={file:null,name:null,group:null,description:null,onProgress:null,onComplete:null,onError:null,showToast:!0,...t},!(this.options.file&&this.options.file instanceof File))throw new Error("FileUpload requires a valid File object");this.cancelled=!1,this.uploadRequest=null,this.progressToast=null,this.progressView=null,this.toastService=null,this.options.showToast&&(this.toastService=new ToastService),this.promise=this._startUpload()}async _startUpload(){try{let t,s,i;this.options.showToast&&this._showProgressToast();try{t=await this._initiateUpload()}catch(e){throw new Error(`Failed to initiate upload: ${e.message}`)}if(this.cancelled)throw new Error("Upload cancelled");if(!t||!t.upload_url)throw new Error("Invalid upload response: missing upload URL");if("string"==typeof t.upload_url)s={url:t.upload_url,method:"PUT",fields:null,headers:{}};else{if(!t.upload_url||"object"!=typeof t.upload_url||!t.upload_url.upload_url)throw new Error(`Invalid upload response: unrecognised upload_url format. Server returned: ${JSON.stringify(t.upload_url)}`);s={url:t.upload_url.upload_url,method:t.upload_url.method||"POST",fields:t.upload_url.fields||null,headers:t.upload_url.headers||{}}}try{i=await this._performUpload(s)}catch(e){throw new Error(`File upload failed: ${e.message}`)}if(this.cancelled)throw new Error("Upload cancelled");try{await this._completeUpload()}catch(e){console.warn("Failed to mark upload as completed:",e)}return this._onComplete(this.fileModel),this.fileModel}catch(e){throw"Upload cancelled"!==e.message&&this._onError(e),e}}async _initiateUpload(){try{const e={filename:this.options.name||this.options.file.name,file_size:this.options.file.size,content_type:this.options.file.type};this.options.group&&(e.group=this.options.group),this.options.description&&(e.description=this.options.description);const t=await this.fileModel.rest.POST("/api/fileman/upload/initiate",e);if(!t)throw new Error("No response from upload initiation API");if(!t.data)throw new Error("Upload initiation response missing data");if(!t.data.status){const e=t.data.error||"Upload initiation failed";throw new Error(e)}if(!t.data.data)throw new Error("Upload initiation response missing data payload");return t.data.data.id&&this.fileModel.set("id",t.data.data.id),t.data.data}catch(e){if("Network Error"===e.message||"TypeError"===e.name)throw new Error("Network error during upload initiation. Please check your connection.");throw e}}async _performUpload(e){return new Promise((t,s)=>{if(!(this.options.file instanceof File))return void s(new Error("Only single File objects are supported"));const{url:i,method:o,fields:a,headers:n}=e,l="POST"===o&&null!==a,r=new XMLHttpRequest;this.uploadRequest=r,r.upload.onprogress=e=>{this.cancelled||this._onProgress({progress:e.loaded/e.total,loaded:e.loaded,total:e.total,percentage:Math.round(e.loaded/e.total*100)})},r.onload=()=>{r.status>=200&&r.status<300?t({data:r.response,status:r.status,statusText:r.statusText,xhr:r}):s(new Error(`Upload failed: ${r.status} ${r.statusText}`))},r.onerror=()=>s(new Error("Upload failed: Network error")),r.ontimeout=()=>s(new Error("Upload timed out — file may be too large or connection too slow")),r.onabort=()=>s(new Error("Upload cancelled"));let d=i;i.startsWith("/")&&!i.startsWith("/api/")&&(d="/api"+i);const c=this.fileModel.rest.buildUrl(d);if(r.open(o,c),r.timeout=3e4,l){for(const[t,s]of Object.entries(n||{}))"content-type"!==t.toLowerCase()&&r.setRequestHeader(t,s);const e=new FormData;for(const[t,s]of Object.entries(a))e.append(t,s);e.append("file",this.options.file),r.send(e)}else{r.setRequestHeader("Content-Type",this.options.file.type);for(const[e,t]of Object.entries(n||{}))"content-type"!==e.toLowerCase()&&r.setRequestHeader(e,t);r.send(this.options.file)}})}async _completeUpload(){try{const e=await this.fileModel.save({action:"mark_as_completed"});if(!e)throw new Error("No response from upload completion API");if(e.data&&!e.data.status){const t=e.data.error||"Failed to mark upload as completed";throw new Error(t)}return e}catch(e){if("Network Error"===e.message||"TypeError"===e.name)throw new Error("Network error during upload completion. The file may have uploaded successfully.");throw e}}_onProgress(e){this.progressToast&&this.progressToast.updateProgress&&this.progressToast.updateProgress(e),"function"==typeof this.options.onProgress&&this.options.onProgress(e)}_onComplete(e){this.progressView&&this.progressView.markCompleted("Upload completed successfully!"),this.progressToast&&setTimeout(()=>{try{this.progressToast&&"function"==typeof this.progressToast.hide&&this.progressToast.hide()}catch(e){console.warn("Error hiding progress toast:",e)}},2e3),"function"==typeof this.options.onComplete&&this.options.onComplete(e)}_onError(e){if(this.progressToast)try{this.progressToast.hide()}catch(t){console.warn("Error hiding progress toast on error:",t)}this.toastService&&this.toastService.error(`Upload failed: ${e.message}`),"function"==typeof this.options.onError&&this.options.onError(e)}_showProgressToast(){this.progressView=new ProgressView({filename:this.options.name||this.options.file.name,filesize:this.options.file.size,showCancel:!0,onCancel:()=>this.cancel()}),this.progressToast=this.toastService.showView(this.progressView,"info",{title:"File Upload",autohide:!1,dismissible:!1})}cancel(){return!this.cancelled&&(this.cancelled=!0,this.uploadRequest&&"function"==typeof this.uploadRequest.abort&&this.uploadRequest.abort(),this.progressView&&this.progressView.markCancelled(),this.progressToast&&setTimeout(()=>{try{this.progressToast&&"function"==typeof this.progressToast.hide&&this.progressToast.hide()}catch(e){console.warn("Error hiding progress toast on cancel:",e)}},1500),!0)}isCancelled(){return this.cancelled}then(e,t){return this.promise.then(e,t)}catch(e){return this.promise.catch(e)}finally(e){return this.promise.finally(e)}getStats(){return{filename:this.options.file.name,size:this.options.file.size,type:this.options.file.type,cancelled:this.cancelled,group:this.options.group,description:this.options.description}}}class Group extends i{constructor(e={}){super(e,{endpoint:"/api/group"})}}class GroupList extends s{constructor(e={}){super({ModelClass:Group,endpoint:"/api/group",size:10,...e})}}const o={org:"Organization",division:"Division",department:"Department",team:"Team",merchant:"Merchant",partner:"Partner",client:"Client",iso:"ISO",sales:"Sales",reseller:"Reseller",location:"Location",region:"Region",route:"Route",project:"Project",inventory:"Inventory",test:"Testing",misc:"Miscellaneous",qa:"Quality Assurance"},a=Object.entries(o).map(([e,t])=>({value:e,label:t})),n={create:{title:"Create Group",fields:[{name:"name",type:"text",label:"Group Name",required:!0,placeholder:"Enter group name"},{name:"kind",type:"select",label:"Group Kind",required:!0,options:a},{type:"collection",name:"parent",label:"Parent Group",Collection:GroupList,labelField:"name",valueField:"id",maxItems:10,placeholder:"Search groups...",emptyFetch:!1,debounceMs:300}]},edit:{title:"Edit Group",fields:[{name:"name",type:"text",label:"Group Name",required:!0,placeholder:"Enter group name"},{name:"kind",type:"select",label:"Group Kind",required:!0,options:a},{type:"collection",name:"parent",label:"Parent Group",Collection:GroupList,labelField:"name",valueField:"id",maxItems:10,placeholder:"Search groups...",emptyFetch:!1,debounceMs:300},{name:"metadata.domain",type:"text",label:"Default Domain",placeholder:"Enter Domain"},{name:"metadata.portal",type:"text",label:"Default Portal",placeholder:"Enter Portal URL"},{name:"is_active",type:"switch",label:"Is Active",cols:4}]},detailed:{title:"Group Details",fields:[{type:"header",text:"Profile Information",level:4,class:"text-primary mb-3"},{type:"group",columns:{xs:12,md:4},fields:[{type:"image",name:"avatar",size:"lg",imageSize:{width:200,height:200},placeholder:"Upload your avatar",help:"Square images work best",columns:12},{name:"is_active",type:"switch",label:"Is Active",columns:12}]},{type:"group",columns:{xs:12,md:8},title:"Details",fields:[{name:"name",type:"text",label:"Group Name",required:!0,placeholder:"Enter group name",columns:12},{name:"kind",type:"select",label:"Group Kind",required:!0,columns:12,options:[{value:"org",label:"Organization"},{value:"team",label:"Team"},{value:"department",label:"Department"},{value:"merchant",label:"Merchant"},{value:"iso",label:"ISO"},{value:"group",label:"Group"}]},{type:"collection",name:"parent",label:"Parent Group",Collection:GroupList,labelField:"name",valueField:"id",maxItems:10,placeholder:"Search groups...",emptyFetch:!1,debounceMs:300,columns:12}]},{type:"group",columns:12,title:"Account Settings",class:"pt-3",fields:[{type:"select",name:"metadata.timezone",label:"Timezone",columns:6,value:"America/Los_Angeles",options:[{value:"America/New_York",text:"Eastern Time"},{value:"America/Chicago",text:"Central Time"},{value:"America/Denver",text:"Mountain Time"},{value:"America/Los_Angeles",text:"Pacific Time"},{value:"UTC",text:"UTC"}]},{type:"select",name:"metadata.eod_hour",label:"End of Day Hour",columns:6,options:[{value:0,text:"Midnight"},{value:1,text:"1 AM"},{value:2,text:"2 AM"},{value:3,text:"3 AM"},{value:4,text:"4 AM"},{value:5,text:"5 AM"},{value:6,text:"6 AM"},{value:7,text:"7 AM"},{value:8,text:"8 AM"},{value:9,text:"9 AM"},{value:10,text:"10 AM"},{value:11,text:"11 AM"},{value:12,text:"12 PM"},{value:13,text:"1 PM"},{value:14,text:"2 PM"},{value:15,text:"3 PM"},{value:16,text:"4 PM"},{value:17,text:"5 PM"},{value:18,text:"6 PM"},{value:19,text:"7 PM"},{value:20,text:"8 PM"},{value:21,text:"9 PM"},{value:22,text:"10 PM"},{value:23,text:"11 PM"}]}]},{type:"text",label:"Email Template (Prefix)",name:"metadata.email_template",columns:12}]}};Group.EDIT_FORM=n.edit,Group.ADD_FORM=n.create,Group.CREATE_FORM=n.create,Group.GroupKindOptions=a,Group.GroupKinds=o;const l=/* @__PURE__ */Object.freeze(/* @__PURE__ */Object.defineProperty({__proto__:null,Group:Group,GroupForms:n,GroupList:GroupList},Symbol.toStringTag,{value:"Module"}));class User extends i{constructor(e={}){super(e,{endpoint:"/api/user"})}hasPermission(e){if(this.get("is_superuser"))return!0;if(Array.isArray(e))return e.some(e=>this.hasPermission(e));const t=e.startsWith("sys."),s=t?e.substring(4):e;return!!this._hasPermission(s)||!(t||!this.member||!this.member.hasPermission(e))}_hasPermission(e){const t=this.get("permissions");if(!t)return!1;if(1==t[e])return!0;const s=User.GRANULAR_TO_CATEGORY[e];return!(!s||1!=t[s])}hasPerm(e){return this.hasPermission(e)}}class UserList extends s{constructor(e={}){super({ModelClass:User,endpoint:"/api/user",...e})}}User.CATEGORY_PERMISSIONS=[{name:"view_admin",label:"Admin Panel",tooltip:"Access the admin panel, assistant, and system tools"},{name:"security",label:"Security",tooltip:"Incidents, events, rules, tickets, firewall, bouncer, GeoIP, system logs"},{name:"users",label:"Users",tooltip:"User records, passkeys, TOTP, API keys, OAuth, devices, locations"},{name:"groups",label:"Groups",tooltip:"Groups, members, group API keys, settings"},{name:"comms",label:"Communications",tooltip:"Email, phone, SMS, push notifications, chat, notifications"},{name:"jobs",label:"Jobs",tooltip:"Jobs, job events, job logs, runners, queue control, system stats"},{name:"metrics",label:"Metrics",tooltip:"Metrics recording, fetching, categories, values, permissions"},{name:"files",label:"Files",tooltip:"File managers, files, renditions, vault files, vault data, S3 buckets"},{name:"assistant",label:"AI Assistant",tooltip:"Access to the AI Assistant"},{name:"comms",label:"Communication",tooltip:"Ability to notify users"}],User.GRANULAR_PERMISSION_TABS=[{label:"Account",permissions:[{name:"view_users",label:"View Users"},{name:"manage_users",label:"Manage Users"},{name:"view_groups",label:"View Groups"},{name:"manage_groups",label:"Manage Groups"},{name:"manage_group",label:"Manage Own Group"},{name:"view_members",label:"View Members"},{name:"manage_settings",label:"Manage Settings"}]},{label:"Communication",permissions:[{name:"manage_chat",label:"Manage Chat"},{name:"manage_aws",label:"Manage Email (AWS)"},{name:"view_notifications",label:"View Notifications"},{name:"manage_notifications",label:"Manage Notifications"},{name:"send_notifications",label:"Send Notifications"},{name:"view_devices",label:"View Push Devices"},{name:"manage_devices",label:"Manage Push Devices"},{name:"manage_push_config",label:"Push Config"},{name:"view_phone_numbers",label:"View Phone Numbers"},{name:"manage_phone_numbers",label:"Manage Phone Numbers"},{name:"manage_phone_config",label:"Phone Config"},{name:"view_sms",label:"View SMS"},{name:"manage_sms",label:"Manage SMS"},{name:"send_sms",label:"Send SMS"}]},{label:"Platform",permissions:[{name:"view_security",label:"View Security"},{name:"manage_security",label:"Manage Security"},{name:"admin",label:"Log Admin"},{name:"view_logs",label:"View Logs"},{name:"manage_logs",label:"Manage Logs"},{name:"view_jobs",label:"View Jobs"},{name:"manage_jobs",label:"Manage Jobs"},{name:"view_metrics",label:"View Metrics"},{name:"manage_metrics",label:"Manage Metrics"},{name:"write_metrics",label:"Write Metrics"},{name:"view_fileman",label:"View File Managers"},{name:"manage_files",label:"Manage Files"},{name:"view_vault",label:"View Vault"},{name:"manage_vault",label:"Manage Vault"},{name:"manage_docit",label:"Manage Docs"},{name:"manage_shortlinks",label:"Manage Shortlinks"}]}],User.CATEGORY_GRANULAR_MAP={security:["view_security","manage_security"],users:["view_users","manage_users","view_members"],groups:["view_groups","manage_groups","manage_group"],comms:["manage_chat","manage_aws","view_notifications","manage_notifications","send_notifications","view_devices","manage_devices","manage_push_config","view_phone_numbers","manage_phone_numbers","manage_phone_config","view_sms","manage_sms","send_sms"],jobs:["view_jobs","manage_jobs"],metrics:["view_metrics","manage_metrics","write_metrics"],files:["view_fileman","manage_files","view_vault","manage_vault"]},User.GRANULAR_TO_CATEGORY={};for(const[g,y]of Object.entries(User.CATEGORY_GRANULAR_MAP))for(const e of y)User.GRANULAR_TO_CATEGORY[e]=g;User.APP_CATEGORY_PERMISSIONS=[],User.APP_GRANULAR_PERMISSIONS=[],User.PERMISSIONS=[...User.CATEGORY_PERMISSIONS,...User.GRANULAR_PERMISSION_TABS.flatMap(e=>e.permissions),...User.APP_CATEGORY_PERMISSIONS,...User.APP_GRANULAR_PERMISSIONS],User.PERMISSION_FIELDS=[...User.PERMISSIONS.map(e=>({name:`permissions.${e.name}`,type:"switch",label:e.label,columns:6}))];const r=e=>({name:`permissions.${e.name}`,type:"switch",label:e.label,columns:6,...e.tooltip?{tooltip:e.tooltip}:{}});User.CATEGORY_PERMISSION_FIELDS=(()=>{const e=[{label:"System",fields:User.CATEGORY_PERMISSIONS.map(r)}];return User.APP_CATEGORY_PERMISSIONS.length>0&&e.push({label:"App",fields:User.APP_CATEGORY_PERMISSIONS.map(r)}),[{type:"tabset",tabs:e}]})(),User.GRANULAR_PERMISSION_FIELDS=(()=>{const e=User.GRANULAR_PERMISSION_TABS.map(e=>({label:e.label,fields:e.permissions.map(r)}));return User.APP_GRANULAR_PERMISSIONS.length>0&&e.push({label:"App",fields:User.APP_GRANULAR_PERMISSIONS.map(r)}),[{type:"tabset",tabs:e}]})();const d={create:{title:"Create User",fields:[{name:"email",type:"text",label:"Email",required:!0},{name:"phone_number",type:"text",label:"Phone number",columns:12},{name:"display_name",type:"text",label:"Display Name"}]},edit:{title:"Edit User",fields:[{name:"email",type:"email",label:"Email",columns:12},{name:"display_name",type:"text",label:"Display Name",columns:12},{name:"phone_number",type:"text",label:"Phone number",columns:12},{type:"collection",name:"org",label:"Organization",Collection:GroupList,labelField:"name",valueField:"id",columns:12}]},permissions:{title:"Edit Permissions",fields:User.PERMISSION_FIELDS}},c={profile:{title:"User Profile",columns:2,fields:[{name:"id",label:"User ID",type:"number",columns:4},{name:"last_login",label:"Last Login",type:"datetime",format:"relative",columns:4},{name:"last_activity",label:"Last Activity",type:"datetime",format:"relative",columns:4},{name:"username",label:"Username",type:"text",format:"lowercase",columns:4},{name:"display_name",label:"Display Name",type:"text",columns:4},{name:"email",label:"Email",type:"email",columns:12},{name:"org.name",label:"Organization",type:"text",columns:6},{name:"phone_number",label:"Phone Number",type:"text",columns:6}]},activity:{title:"User Activity",columns:2,fields:[{name:"last_login",label:"Last Login",type:"datetime",format:"relative",colSize:6},{name:"last_activity",label:"Last Activity",type:"datetime",format:"relative",colSize:6}]},detailed:{title:"Detailed User Information",columns:2,showEmptyValues:!0,emptyValueText:"Not set",fields:[{name:"id",label:"User ID",type:"number",colSize:3},{name:"display_name",label:"Display Name",type:"text",format:'capitalize|default("Unnamed User")',colSize:9},{name:"username",label:"Username",type:"text",format:"lowercase",colSize:6},{name:"email",label:"Email Address",type:"email",colSize:6},{name:"phone_number",label:"Phone Number",type:"phone",format:'phone|default("Not provided")',colSize:6},{name:"is_active",label:"Account Status",type:"boolean",colSize:6},{name:"last_login",label:"Last Login",type:"datetime",format:"relative",colSize:6},{name:"last_activity",label:"Last Activity",type:"datetime",format:"relative",colSize:6},{name:"avatar.url",label:"Avatar",type:"url",colSize:12},{name:"permissions",label:"User Permissions",type:"dataview",dataViewColumns:2,showEmptyValues:!1},{name:"metadata",label:"User Metadata",type:"dataview",dataViewColumns:1},{name:"avatar",label:"Avatar Details",type:"dataview",dataViewColumns:1}]},permissions:{title:"User Permissions",columns:1,fields:[{name:"display_name",label:"User",type:"text",format:"capitalize",columns:12},{name:"permissions",label:"Assigned Permissions",type:"dataview",dataViewColumns:3,showEmptyValues:!1,colSize:12}]},summary:{title:"User Summary",columns:3,fields:[{name:"display_name",label:"Name",type:"text",format:"capitalize|truncate(30)"},{name:"email",label:"Email",type:"email"},{name:"is_active",label:"Status",type:"boolean"},{name:"last_activity",label:"Last Seen",type:"datetime",format:"relative",colSize:12}]}};User.DATA_VIEW=c.detailed,User.EDIT_FORM=d.edit,User.ADD_FORM=d.create;class UserDevice extends i{constructor(e={}){super(e,{endpoint:"/api/user/device"})}static async getByDuid(e){const t=new UserDevice,s=await t.rest.GET("/api/user/device/lookup",{duid:e});return s.success&&s.data&&s.data.data?new UserDevice(s.data.data):null}}class UserDeviceList extends s{constructor(e={}){super({ModelClass:UserDevice,endpoint:"/api/user/device",...e})}}class UserDeviceLocation extends i{constructor(e={}){super(e,{endpoint:"/api/user/device/location"})}}class UserDeviceLocationList extends s{constructor(e={}){super({ModelClass:UserDeviceLocation,endpoint:"/api/user/device/location",...e})}}const u=/* @__PURE__ */Object.freeze(/* @__PURE__ */Object.defineProperty({__proto__:null,User:User,UserDataView:c,UserDevice:UserDevice,UserDeviceList:UserDeviceList,UserDeviceLocation:UserDeviceLocation,UserDeviceLocationList:UserDeviceLocationList,UserForms:d,UserList:UserList},Symbol.toStringTag,{value:"Module"}));class FileManager extends i{constructor(e={}){super(e,{endpoint:"/api/fileman/manager"})}}class FileManagerList extends s{constructor(e={}){super({ModelClass:FileManager,endpoint:"/api/fileman/manager",size:10,...e})}}const h={create:{title:"Add Storage Backend",fields:[{name:"name",type:"text",label:"Display Name",placeholder:"Enter Display Name",cols:12},{name:"use",type:"text",label:"Use",placeholder:"Enter User or Leave Blank",cols:12},{name:"backend_url",type:"text",label:"Backend URL",required:!0,value:"s3://BUCKET_NAME/OPTION_FOLDER",placeholder:"s3://bucket_name/optional folder",help:"Format: service://path. Valid services: s3",cols:12},{name:"aws_region",type:"select",label:"AWS Region (optional)",value:"us-east-1",options:[{value:"",text:"System Default"},{value:"us-east-1",text:"US East (N. Virginia)"},{value:"us-east-2",text:"US East (Ohio)"},{value:"us-west-1",text:"US West (N. California)"},{value:"us-west-2",text:"US West (Oregon)"},{value:"ca-central-1",text:"Canada (Central)"},{value:"eu-west-1",text:"Europe (Ireland)"},{value:"eu-west-2",text:"Europe (London)"},{value:"eu-west-3",text:"Europe (Paris)"},{value:"eu-central-1",text:"Europe (Frankfurt)"},{value:"eu-north-1",text:"Europe (Stockholm)"},{value:"eu-south-1",text:"Europe (Milan)"},{value:"ap-southeast-2",text:"Asia Pacific (Sydney)"}],columns:12,help:"Optional. Defaults to project AWS_REGION if omitted."},{name:"aws_key",type:"text",label:"AWS Key (optional)",placeholder:"enter your AWS Key with S3 permissions",columns:12,help:"Optional, AWS Key with S3 permissions"},{name:"aws_secret",type:"text",label:"AWS Secret (optional)",placeholder:"enter your AWS Secret with S3 permissions",columns:12,help:"Optional, AWS Secret with S3 permissions"},{name:"is_default",type:"switch",label:"Is Default",cols:6},{name:"is_active",type:"switch",label:"Is Active",default:!0,cols:6}]},edit:{title:"Edit Storage Backend",fields:[{name:"name",type:"text",label:"Display Name",placeholder:"Enter Display Name",cols:12},{name:"use",type:"text",label:"Use",placeholder:"Enter User or Leave Blank",cols:12},{name:"backend_url",type:"text",label:"Backend URL",required:!0,placeholder:"s3://bucket_name/optional folder",help:"Format: service://path. Valid services: s3",cols:12},{name:"allowed_origins",type:"text",label:"Domains Who Can Upload",cols:12},{name:"is_default",type:"switch",label:"Is Default",cols:6},{name:"is_active",type:"switch",label:"Is Active",default:!0,cols:6},{name:"is_public",type:"switch",label:"Is Public",default:!0,cols:6}]},owners:{fields:[{type:"collection",name:"group",label:"Group (Owner)",Collection:GroupList,labelField:"name",valueField:"id",maxItems:10,placeholder:"Search groups...",emptyFetch:!1,debounceMs:300},{type:"collection",name:"user",label:"User (Owner)",Collection:UserList,labelField:"display_name",valueField:"id",maxItems:10,placeholder:"Search users...",emptyFetch:!1,debounceMs:300}]},credentials:{fields:[{name:"aws_region",type:"select",label:"AWS Region (optional)",value:"us-east-1",options:[{value:"",text:"System Default"},{value:"us-east-1",text:"US East (N. Virginia)"},{value:"us-east-2",text:"US East (Ohio)"},{value:"us-west-1",text:"US West (N. California)"},{value:"us-west-2",text:"US West (Oregon)"},{value:"ca-central-1",text:"Canada (Central)"},{value:"eu-west-1",text:"Europe (Ireland)"},{value:"eu-west-2",text:"Europe (London)"},{value:"eu-west-3",text:"Europe (Paris)"},{value:"eu-central-1",text:"Europe (Frankfurt)"},{value:"eu-north-1",text:"Europe (Stockholm)"},{value:"eu-south-1",text:"Europe (Milan)"},{value:"ap-southeast-2",text:"Asia Pacific (Sydney)"}],columns:12,help:"Optional. Defaults to project AWS_REGION if omitted."},{name:"aws_key",type:"text",label:"AWS Key (optional)",placeholder:"enter your AWS Key with S3 permissions",columns:12,help:"Optional, AWS Key with S3 permissions"},{name:"aws_secret",type:"text",label:"AWS Secret (optional)",placeholder:"enter your AWS Secret with S3 permissions",columns:12,help:"Optional, AWS Secret with S3 permissions"}]}};let m=class extends i{constructor(e={}){super(e,{endpoint:"/api/fileman/file"})}isImage(){return"image"===this.get("category")}getCategory(){return this.get("category")||this._inferCategoryFromContentType()}_inferCategoryFromContentType(){const e=(this.get("content_type")||"").toLowerCase();return e?e.startsWith("image/")?"image":e.startsWith("video/")?"video":e.startsWith("audio/")?"audio":"application/pdf"===e?"pdf":e.startsWith("text/")||"application/msword"===e||e.startsWith("application/vnd.openxmlformats-officedocument.wordprocessingml")||"application/vnd.oasis.opendocument.text"===e?"document":"application/vnd.ms-excel"===e||e.startsWith("application/vnd.openxmlformats-officedocument.spreadsheetml")||"application/vnd.oasis.opendocument.spreadsheet"===e?"spreadsheet":"application/vnd.ms-powerpoint"===e||e.startsWith("application/vnd.openxmlformats-officedocument.presentationml")||"application/vnd.oasis.opendocument.presentation"===e?"presentation":"application/zip"===e||"application/x-rar-compressed"===e||"application/x-7z-compressed"===e||"application/x-tar"===e||"application/gzip"===e?"archive":"other":"other"}hasRenditions(){const e=this.get("renditions");return!(!e||!Object.keys(e).length)}isUploadPending(){const e=this.get("upload_status");return!(!e||"completed"===e||"failed"===e)}regenerateRenditions(e){const t=this.id||this.get("id");if(!t)return Promise.reject(new Error("Cannot regenerate renditions on an unsaved file"));const s=Array.isArray(e)&&e.length?{regenerate_renditions:e}:{regenerate_renditions:!0};return this.rest.POST(`${this.endpoint}/${t}`,s)}share(e=!0){const t=this.id||this.get("id");return t?this.rest.POST(`${this.endpoint}/${t}`,{share:e}):Promise.reject(new Error("Cannot share an unsaved file"))}getRenditions(){const e=this.get("renditions");return e?Object.values(e):[]}getBestImageRendition(){const e=this.getRenditions().filter(e=>e&&"string"==typeof e.content_type&&e.content_type.startsWith("image/"));return e.length?e.reduce((e,t)=>{const s=(parseInt(e.width)||0)*(parseInt(e.height)||0);return(parseInt(t.width)||0)*(parseInt(t.height)||0)>s?t:e}):null}getThumbnailUrl(){const e=this.get("renditions")||{};if(e.thumbnail&&e.thumbnail.url)return e.thumbnail.url;const t=this.getBestImageRendition();return t?t.url:null}upload(e={}){return new FileUpload(this,e)}};class FileList extends s{constructor(e={}){super({ModelClass:m,endpoint:"/api/fileman/file",size:10,...e})}}const p={create:{title:"Add File",fields:[]},edit:{title:"Edit File Backend",fields:[]}};class Dialog extends e{static _openDialogs=[];static _baseZIndex={backdrop:1050,modal:1055};static getFullscreenAwareZIndex(){return document.querySelector(".table-fullscreen")?{backdrop:10040,modal:10050}:this._baseZIndex}static _busyIndicator=null;static _busyCounter=0;static _busyTimeout=null;static fixAllBackdropStacking(){const e=document.querySelectorAll(".modal-backdrop"),t=Dialog._openDialogs;if(0===e.length||0===t.length)return;const s=[...t].sort((e,t)=>(e._dialogZIndex||0)-(t._dialogZIndex||0));e.forEach((e,t)=>{if(t<s.length){const i=s[t]._dialogZIndex-5;e.style.zIndex=i;const o=document.querySelector(".table-fullscreen")||document.body;e.parentNode!==o&&o.appendChild(e)}})}static updateAllBackdropStacking(){Dialog.fixAllBackdropStacking()}static showBusy(e={}){const{timeout:t=3e4,message:s="Loading..."}=e;if(this._busyCounter++,1===this._busyCounter){if(this._busyTimeout&&clearTimeout(this._busyTimeout),!this._busyIndicator){const e=this.getFullscreenAwareZIndex().modal+1e3;this._busyIndicator=document.createElement("div"),this._busyIndicator.className="mojo-busy-indicator",this._busyIndicator.innerHTML=`\n <div class="mojo-busy-spinner">\n <div class="spinner-border text-light" role="status">\n <span class="visually-hidden">Loading...</span>\n </div>\n <p class="mojo-busy-message mt-3 text-light">${s}</p>\n </div>\n <style>\n .mojo-busy-indicator {\n position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;\n background-color: rgba(0, 0, 0, 0.5); z-index: ${e};\n display: flex; align-items: center; justify-content: center;\n opacity: 0; transition: opacity 0.15s linear;\n }\n .mojo-busy-indicator.show { opacity: 1; }\n .mojo-busy-spinner .spinner-border { width: 3rem; height: 3rem; }\n </style>\n `,document.body.appendChild(this._busyIndicator)}const e=this._busyIndicator.querySelector(".mojo-busy-message");e&&(e.textContent=s),setTimeout(()=>this._busyIndicator.classList.add("show"),10),this._busyTimeout=setTimeout(()=>{console.error("Busy indicator timed out."),this.hideBusy(!0),this.alert({title:"Operation Timed Out",message:"The operation took too long. Please check your connection and try again.",type:"danger"})},t)}}static hideBusy(e=!1){e?this._busyCounter=0:this._busyCounter--,this._busyCounter<=0&&(this._busyCounter=0,this._busyTimeout&&(clearTimeout(this._busyTimeout),this._busyTimeout=null),this._busyIndicator&&(this._busyIndicator.classList.remove("show"),setTimeout(()=>{this._busyIndicator&&0===this._busyCounter&&(this._busyIndicator.remove(),this._busyIndicator=null)},150)))}constructor(e={}){const t=e.id||`modal-${Date.now()}`;super({...e,id:t,tagName:"div",className:`modal ${!1!==e.fade?"fade":""} ${e.className||""}`,attributes:{tabindex:"-1","aria-hidden":"true","aria-labelledby":e.labelledBy||`${t}-label`,"aria-describedby":e.describedBy||null,...e.attributes}}),this.modalId=t,this.title=e.title||"",this.titleId=`${this.modalId}-label`,this.size=e.size||"",this.centered=void 0!==e.centered&&e.centered,this.scrollable=void 0!==e.scrollable&&e.scrollable,this.autoSize=e.autoSize||"auto"===e.size,this.backdrop=void 0===e.backdrop||e.backdrop,this.keyboard=void 0===e.keyboard||e.keyboard,this.focus=void 0===e.focus||e.focus,this.header=void 0===e.header||e.header,this.headerContent=e.headerContent||null,this.headerView=null,this.closeButton=void 0===e.closeButton||e.closeButton,this.contextMenu=e.contextMenu||null,this._processHeaderContent(this.headerContent),this.body=e.body??e.view??e.message??e.content??"",this.bodyView=null,this.bodyClass=e.bodyClass||"",this.noBodyPadding=e.noBodyPadding||!1,this.minWidth=e.minWidth||300,this.minHeight=e.minHeight||200,e.maxHeight&&(this.maxHeight=e.maxHeight),this.maxWidthPercent=e.maxWidthPercent||.9,this.maxHeightPercent=e.maxHeightPercent||.8,this._processBodyContent(this.body),this.footer=e.footer||null,this.footerView=null,this.footerClass=e.footerClass||"",this._processFooterContent(this.footer),this.buttons=e.buttons||null,this.onShow=e.onShow||null,this.onShown=e.onShown||null,this.onHide=e.onHide||null,this.onHidden=e.onHidden||null,this.onHidePrevented=e.onHidePrevented||null,this.autoShow=void 0!==e.autoShow&&e.autoShow,this.modal=null,this.relatedTarget=e.relatedTarget||null}_processBodyContent(t){if(t instanceof e||t&&"object"==typeof t&&"function"==typeof t.render)this.bodyView=t,this.body="",this.addChild(this.bodyView);else if("function"==typeof t)try{const s=t();s instanceof e?(this.bodyView=s,this.body="",this.addChild(this.bodyView)):s instanceof Promise?(this.bodyPromise=s,this.body='<div class="text-center"><div class="spinner-border spinner-border-sm"></div></div>'):this.body=s}catch(s){console.error("Error processing body function:",s),this.body=t}else this.body=t}_processHeaderContent(t){if(t instanceof e)this.headerView=t,this.headerContent=null,this.addChild(this.headerView);else if("function"==typeof t)try{const s=t();s instanceof e?(this.headerView=s,this.headerContent=null,this.addChild(this.headerView)):s instanceof Promise?(this.headerPromise=s,this.headerContent='<div class="text-center"><div class="spinner-border spinner-border-sm"></div></div>'):this.headerContent=s}catch(s){console.error("Error processing headerContent function:",s),this.headerContent=t}else this.headerContent=t}_processFooterContent(t){if(t instanceof e)this.footerView=t,this.footer=null,this.addChild(this.footerView);else if("function"==typeof t)try{const s=t();s instanceof e?(this.footerView=s,this.footer=null,this.addChild(this.footerView)):s instanceof Promise?(this.footerPromise=s,this.footer='<div class="text-center"><div class="spinner-border spinner-border-sm"></div></div>'):this.footer=s}catch(s){console.error("Error processing footer function:",s),this.footer=t}else this.footer=t}async getTemplate(){const e=["modal-dialog"];return this.size&&"auto"!==this.size&&(this.size.startsWith("fullscreen")?e.push(`modal-${this.size}`):["sm","lg","xl","xxl"].includes(this.size)&&(e.push(`modal-${this.size}`),["lg","xl","xxl"].includes(this.size)&&e.push("modal-fullscreen-sm-down"))),this.centered&&e.push("modal-dialog-centered"),this.scrollable&&(this.maxHeight?e.push("overflow-hidden"):e.push("modal-dialog-scrollable")),`\n <div class="${e.join(" ")}">\n <div class="modal-content">\n ${await this.buildHeader()}\n ${await this.buildBody()}\n ${await this.buildFooter()}\n </div>\n </div>\n `}async buildHeader(){if(!this.header)return"";if(this.headerView)return this.headerView.replaceById=!0,`<div class="modal-header" data-view-container="header">\n \x3c!-- View will be mounted here --\x3e\n <div id="${this.headerView.id}"></div>\n </div>`;if(this.headerContent)return`<div class="modal-header">${this.headerContent}</div>`;let e="";return this.contextMenu&&this.contextMenu.items&&this.contextMenu.items.length>0?e=await this.buildContextMenu():this.closeButton&&(e='<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>'),`\n <div class="modal-header">\n ${this.title?`<h5 class="modal-title" id="${this.titleId}">${this.title}</h5>`:""}\n ${e}\n </div>\n `}async buildContextMenu(){const e=await this.filterContextMenuItems();if(0===e.length)return this.closeButton?'<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>':"";const t=this.contextMenu.icon||"bi-three-dots-vertical";return`\n <div class="dropdown">\n <button class="${this.contextMenu.buttonClass||"btn btn-link p-1 mojo-modal-context-menu-btn"}" type="button" data-bs-toggle="dropdown" aria-expanded="false">\n <i class="${t}"></i>\n </button>\n <ul class="dropdown-menu dropdown-menu-end">\n ${e.map(e=>{if("divider"===e.type)return'<li><hr class="dropdown-divider"></li>';const t=e.icon?`<i class="${e.icon} me-2"></i>`:"",s=e.label||"";if(e.href)return`<li><a class="dropdown-item" href="${e.href}"${e.target?` target="${e.target}"`:""}>${t}${s}</a></li>`;if(e.action){const i=Object.keys(e).filter(e=>e.startsWith("data-")).map(t=>`${t}="${e[t]}"`).join(" ");return`<li><a class="dropdown-item" data-action="${e.action}" ${i}>${t}${s}</a></li>`}return""}).join("")}\n </ul>\n </div>\n `}async filterContextMenuItems(){if(!this.contextMenu||!this.contextMenu.items)return[];const e=[];for(const i of this.contextMenu.items)if("divider"!==i.type){if(i.permissions)try{const e=this.getApp?.();let s=null;if(e&&(s=e.activeUser||e.getState?.("activeUser")),!s&&"undefined"!=typeof window&&window.getApp)try{const e=window.getApp();s=e?.activeUser}catch(t){}if(!s||!s.hasPermission)continue;if(!s.hasPermission(i.permissions))continue}catch(s){console.warn("Error checking permissions for context menu item:",s);continue}e.push(i)}else e.push(i);return e}async buildBody(){return this.bodyView?(this.bodyView.replaceById=!0,`<div class="${this.noBodyPadding?`modal-body p-0 ${this.bodyClass}`:`modal-body ${this.bodyClass}`}" data-view-container="body">\n \x3c!-- View will be mounted here --\x3e\n <div id="${this.bodyView.id}"></div>\n </div>`):this.body||""===this.body?`\n <div class="${this.noBodyPadding?`modal-body p-0 ${this.bodyClass}`:`modal-body ${this.bodyClass}`}">\n ${this.body}\n </div>\n `:""}async buildFooter(){if(this.footerView)return`<div class="modal-footer ${this.footerClass}" data-view-container="footer"></div>`;if(null!==this.footer&&"string"==typeof this.footer)return`<div class="modal-footer ${this.footerClass}">${this.footer}</div>`;if(this.buttons&&this.buttons.length>0){const e=this.buttons.map(e=>{const t=e.dismiss?'data-bs-dismiss="modal"':"",s=e.action?`data-action="${e.action}"`:"",i=e.id?`id="${e.id}"`:"",o=e.disabled?"disabled":"";return`\n <button type="${e.type||"button"}"\n class="btn ${e.class||"btn-secondary"}"\n ${i}\n ${t}\n ${s}\n ${o}>\n ${e.icon?`<i class="bi ${e.icon} me-1"></i>`:""}\n ${e.text||"Button"}\n </button>\n `}).join("");return`<div class="modal-footer ${this.footerClass}">${e}</div>`}return""}async mount(e=null){if(!this.mounted&&!this.destroyed){if(!this.element)throw new Error("Cannot mount dialog without element");return await this.onBeforeMount(),(document.querySelector(".table-fullscreen")||document.body).appendChild(this.element),this.bindEvents(),this.mounted=!0,await this.onAfterMount(),this.emit("mounted",{view:this}),this}}async onAfterRender(){if(await super.onAfterRender(),window.Prism&&this.element&&this.element.querySelectorAll("pre code").length>0&&window.Prism.highlightAllUnder(this.element),this.autoSize)this.setupAutoSizing();else if(this.maxHeight){const e=this.element.querySelector(".modal-body");e&&(e.style.maxHeight=`${this.maxHeight}px`)}}async onAfterMount(){await super.onAfterMount(),"undefined"!=typeof window&&window.bootstrap&&window.bootstrap.Modal&&("static"===this.backdrop&&this.element.setAttribute("data-bs-backdrop","static"),this.keyboard||this.element.setAttribute("data-bs-keyboard","false"),this.modal=new window.bootstrap.Modal(this.element,{backdrop:this.backdrop,keyboard:this.keyboard,focus:this.focus}),this.bindBootstrapEvents(),this.autoShow&&this.show(this.relatedTarget))}setupAutoSizing(){this.element&&(this.element.addEventListener("shown.bs.modal",()=>{this.applyAutoSizing()},{once:!0}),setTimeout(()=>{this.isShown()&&this.applyAutoSizing()},100))}applyAutoSizing(){if(this.element)try{const e=this.element.querySelector(".modal-dialog"),t=this.element.querySelector(".modal-content"),s=this.element.querySelector(".modal-body");if(!e||!t||!s)return void console.warn("Dialog auto-sizing: Required elements not found");if(this.bodyView&&!this.bodyView.element)return void setTimeout(()=>this.applyAutoSizing(),50);const i={dialogMaxWidth:e.style.maxWidth,dialogWidth:e.style.width,contentWidth:t.style.width,contentMaxHeight:t.style.maxHeight,hadScrollableClass:e.classList.contains("modal-dialog-scrollable")};e.style.maxWidth="none",e.style.width="auto",t.style.width="auto",t.style.maxHeight="none",t.offsetHeight;const o=t.getBoundingClientRect(),a=40,n=Math.min(window.innerWidth*this.maxWidthPercent,window.innerWidth-a);let l=Math.min(window.innerHeight*this.maxHeightPercent,window.innerHeight-a),r=Math.max(this.minWidth,Math.ceil(o.width+20)),d=Math.max(this.minHeight,Math.ceil(o.height));this.maxHeight&&(l=Math.min(this.maxHeight,l),e.style.maxHeight=`${l}px`),r=Math.min(r,n);const c=o.height>l;e.style.maxWidth=`${r}px`,e.style.width=`${r}px`,c&&(e.classList.contains("modal-dialog-scrollable")||e.classList.add("modal-dialog-scrollable"),t.style.maxHeight=`${l}px`,d=l),this.autoSizedWidth=r,this.autoSizedHeight=d,this._originalStyles=i}catch(e){console.error("Error in dialog auto-sizing:",e),this.element.querySelector(".modal-dialog").style.maxWidth=""}}resetAutoSizing(){if(this.autoSize&&this._originalStyles&&this.element)try{const e=this.element.querySelector(".modal-dialog"),t=this.element.querySelector(".modal-content"),s=this.element.querySelector(".modal-body");e&&t&&s&&(e.style.maxWidth=this._originalStyles.dialogMaxWidth||"",e.style.width=this._originalStyles.dialogWidth||"",t.style.width=this._originalStyles.contentWidth||"",t.style.maxHeight=this._originalStyles.contentMaxHeight||"",!this._originalStyles.hadScrollableClass&&e.classList.contains("modal-dialog-scrollable")&&e.classList.remove("modal-dialog-scrollable"),delete this.autoSizedWidth,delete this.autoSizedHeight,delete this._originalStyles)}catch(e){console.error("Error resetting dialog auto-sizing:",e)}}bindBootstrapEvents(){this.element.addEventListener("show.bs.modal",e=>{const t=Dialog._openDialogs.length,s=Dialog.getFullscreenAwareZIndex().modal+20*t;this.element.style.zIndex=s,this._dialogZIndex=s,this._backdropZIndex=s-10,Dialog._openDialogs.push(this),this.onShow&&this.onShow(e),this.emit("show",{dialog:this,relatedTarget:e.relatedTarget})}),this.element.addEventListener("shown.bs.modal",e=>{if(setTimeout(()=>{Dialog.fixAllBackdropStacking()},50),this.onShown&&this.onShown(e),this.emit("shown",{dialog:this,relatedTarget:e.relatedTarget}),this.focus){const e=this.element.querySelector('input:not([type="hidden"]), textarea, select');e&&e.focus()}}),this.element.addEventListener("hide.bs.modal",e=>{const t=this.element.querySelector(":focus");t&&t.blur(),this.onHide&&!1===this.onHide(e)?e.preventDefault():this.emit("hide",{dialog:this})}),this.element.addEventListener("hidden.bs.modal",e=>{const t=Dialog._openDialogs.indexOf(this);t>-1&&Dialog._openDialogs.splice(t,1),Dialog._openDialogs.length>0&&(document.body.classList.add("modal-open"),setTimeout(()=>{Dialog.fixAllBackdropStacking()},50)),this.previousFocus&&document.body.contains(this.previousFocus)&&this.previousFocus.focus(),this.onHidden&&this.onHidden(e),this.emit("hidden",{dialog:this})}),this.element.addEventListener("hidePrevented.bs.modal",e=>{this.onHidePrevented&&this.onHidePrevented(e),this.emit("hidePrevented",{dialog:this})})}show(e=null){this.previousFocus=document.activeElement,window.lastDialog=this,this.modal&&this.modal.show(e)}hide(){const e=this.element?.querySelector(":focus");e&&e.blur(),this.modal&&this.modal.hide()}toggle(e=null){this.modal&&this.modal.toggle(e)}async destroy(){if(this.modal){const e=this.element?.querySelector(":focus");e&&e.blur(),this.modal.dispose(),this.modal=null}this.previousFocus&&document.body.contains(this.previousFocus)&&(this.previousFocus.focus(),this.previousFocus=null),this.autoSize&&this.resetAutoSizing(),await super.destroy()}handleUpdate(){this.modal&&this.modal.handleUpdate()}async setContent(t){if(t instanceof e){this.bodyView&&(await this.bodyView.destroy(),this.removeChild(this.bodyView)),this.bodyView=t,this.body="",this.addChild(this.bodyView);const e=this.element?.querySelector(".modal-body");e&&(e.innerHTML="",await this.bodyView.render(e))}else{this.body=t;const e=this.element?.querySelector(".modal-body");e&&(e.innerHTML=t)}this.handleUpdate()}setTitle(e){this.title=e;const t=this.element?.querySelector(".modal-title");t&&(t.textContent=e)}setLoading(e=!0,t="Loading..."){const s=this.element?.querySelector(".modal-body");s&&(e?s.innerHTML=`\n <div class="text-center py-4">\n <div class="spinner-border text-primary mb-3" role="status">\n <span class="visually-hidden">Loading...</span>\n </div>\n <p>${t}</p>\n </div>\n `:this.bodyView&&s.replaceChildren(this.bodyView.element))}async onBeforeDestroy(){this.headerView&&await this.headerView.destroy(),this.bodyView&&await this.bodyView.destroy(),this.footerView&&await this.footerView.destroy(),await super.onBeforeDestroy(),this.modal&&(this.modal.dispose(),this.modal=null)}static async showCode(e={}){const t=new Dialog({title:e.title||"Source Code",size:e.size||"lg",scrollable:!0,body:Dialog.formatCode(e.code,e.language),buttons:[{text:"Copy to Clipboard",class:"btn-primary",icon:"bi-clipboard",action:"copy"},{text:"Close",class:"btn-secondary",dismiss:!0}]});t.on("action:copy",async()=>{if(navigator.clipboard)try{await navigator.clipboard.writeText(e.code),t.showCopySuccess()}catch(s){console.error("Failed to copy:",s)}});const s=document.querySelector(".table-fullscreen")||document.body;return await t.render(!0,s),window.Prism&&t.element&&window.Prism.highlightAllUnder(t.element),t.show(),t.on("hidden",()=>{t.destroy(),t.element.remove()}),t}static formatCode(e,t="javascript"){let s;s=window.Prism&&window.Prism.languages[t]?window.Prism.highlight(e,window.Prism.languages[t],t):e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'");const i=window.Prism?`language-${t}`:"";return`\n <style>\n /* Custom Prism theme overrides for Dialog */\n .dialog-code-block .token.comment { color: #6a9955; }\n .dialog-code-block .token.string { color: #ce9178; }\n .dialog-code-block .token.keyword { color: #569cd6; }\n .dialog-code-block .token.function { color: #dcdcaa; }\n .dialog-code-block .token.number { color: #b5cea8; }\n .dialog-code-block .token.operator { color: #d4d4d4; }\n .dialog-code-block .token.class-name { color: #4ec9b0; }\n .dialog-code-block .token.punctuation { color: #d4d4d4; }\n .dialog-code-block .token.boolean { color: #569cd6; }\n .dialog-code-block .token.property { color: #9cdcfe; }\n .dialog-code-block .token.tag { color: #569cd6; }\n .dialog-code-block .token.attr-name { color: #9cdcfe; }\n .dialog-code-block .token.attr-value { color: #ce9178; }\n .dialog-code-block ::selection { background: #264f78; }\n </style>\n <pre class="${i} dialog-code-block" style="${"\n max-height: 60vh;\n overflow-y: auto;\n background: #1e1e1e;\n color: #d4d4d4;\n padding: 1.25rem;\n border-radius: 0.5rem;\n margin: 0;\n font-family: 'Cascadia Code', 'Fira Code', 'JetBrains Mono', 'Consolas', 'Monaco', monospace;\n font-size: 0.9rem;\n line-height: 1.6;\n border: 1px solid #2d2d30;\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);\n ".replace(/\s+/g," ").trim()}">\n <code class="${i}" style="color: inherit; background: transparent; text-shadow: none;">${s}</code>\n </pre>\n `}static highlightCodeBlocks(e=document){window.Prism&&window.Prism.highlightAllUnder&&window.Prism.highlightAllUnder(e)}showCopySuccess(){const e=this.element.querySelector('[data-action="copy"]');if(e){const t=e.innerHTML;e.innerHTML='<i class="bi bi-check me-1"></i>Copied!',e.classList.remove("btn-primary"),e.classList.add("btn-success"),e.disabled=!0,setTimeout(()=>{e.innerHTML=t,e.classList.remove("btn-success"),e.classList.add("btn-primary"),e.disabled=!1},2e3)}}static async showHtmlPreview(e={}){const t=e.html||e.content||"",s=e.title||"HTML Preview",i=e.size||"lg",o=e.height||500,a=new Dialog({title:s,size:i,scrollable:!1,body:`\n <div class="html-preview-container">\n <div class="d-flex justify-content-between align-items-center mb-2">\n <small class="text-muted">Preview (sandboxed)</small>\n <button type="button" class="btn btn-sm btn-outline-secondary" data-action="refresh-preview">\n <i class="bi bi-arrow-clockwise"></i> Refresh\n </button>\n </div>\n <iframe\n id="html-preview-frame"\n class="border rounded w-100"\n style="height: ${o}px; background: white;"\n sandbox="allow-same-origin"\n frameborder="0"\n ></iframe>\n </div>\n `,buttons:[{text:"Close",class:"btn-secondary",dismiss:!0}]});a.on("action:refresh-preview",async e=>{const s=a.element.querySelector("#html-preview-frame");if(!s)return;const i=s.contentDocument||s.contentWindow.document;i.open(),i.write(t),i.close()});const n=document.querySelector(".table-fullscreen")||document.body;await a.render(!0,n);const l=a.element.querySelector("#html-preview-frame");if(l){const e=l.contentDocument||l.contentWindow.document;e.open(),e.write(t),e.close()}return a.show(),a.on("hidden",()=>{a.destroy(),a.element.remove()}),a}static async showDialog(e={}){"string"==typeof e&&(e={...arguments[2]||{},body:arguments[0],title:arguments[1]||"Alert"});const{title:t="Dialog",content:s,body:i,view:o,message:a,size:n="md",centered:l=!0,buttons:r=[{text:"OK",class:"btn-primary",value:!0}],rejectOnDismiss:d=!1,...c}=e,u=new Dialog({title:t,body:i??o??a??s??"",size:n,centered:l,buttons:r,...c}),h=document.querySelector(".table-fullscreen")||document.body;return await u.render(!0,h),new Promise((e,t)=>{let s=!1;u.element.querySelectorAll(".modal-footer button").forEach((t,i)=>{const o=r[i];o&&t.addEventListener("click",async t=>{if(s)return;const a=void 0!==o.value?o.value:o.action??i;if("function"==typeof o.handler)try{const n=await o.handler({dialog:u,button:o,index:i,event:t});if(null===n||!1===n)return;const l=!0===n||void 0===n?a:n;s=!0,o.dismiss||u.hide(),e(l)}catch(n){return void console.error("Dialog button handler error:",n)}else s=!0,o.dismiss||u.hide(),e(a)})}),u.on("hidden",()=>{s||(s=!0,d?t(new Error("Dialog dismissed")):e(null)),setTimeout(()=>{u.destroy(),u.element.remove()},100)}),u.show()})}static async alert(e={}){"string"==typeof e&&(e={message:e,title:"Alert"});const{message:t="",title:s="Alert",type:i="info",...o}=e;let a="",n="";switch(i){case"success":a='<i class="bi bi-check-circle-fill text-success me-2"></i>',n="text-success";break;case"warning":a='<i class="bi bi-exclamation-triangle-fill text-warning me-2"></i>',n="text-warning";break;case"danger":case"error":a='<i class="bi bi-x-circle-fill text-danger me-2"></i>',n="text-danger";break;default:a='<i class="bi bi-info-circle-fill text-info me-2"></i>',n="text-info"}return Dialog.showDialog({title:`<span class="${n}">${a}${s}</span>`,body:`<p>${t}</p>`,size:"sm",centered:!0,buttons:[{text:"OK",class:"btn-primary",value:!0}],...o})}static async confirm(e,t="Confirm",s={}){"object"==typeof e&&(e=(s=e).message,t=s.title||t);const i=new Dialog({title:t,body:`<p>${e}</p>`,size:s.size||"sm",centered:!0,backdrop:"static",buttons:[{text:s.cancelText||"Cancel",class:"btn-secondary",dismiss:!0,action:"cancel"},{text:s.confirmText||"Confirm",class:s.confirmClass||"btn-primary",action:"confirm"}],...s}),o=document.querySelector(".table-fullscreen")||document.body;return await i.render(!0,o),i.show(),new Promise(e=>{let t=!1;i.on("action:confirm",()=>{t=!0,i.hide()}),i.on("hidden",()=>{i.destroy(),i.element.remove(),e(t)})})}static async prompt(e,t="Input",s={}){const i=`prompt-input-${Date.now()}`,o=s.defaultValue||"",a=s.inputType||"text",n=s.placeholder||"",l=new Dialog({title:t,body:`\n <p>${e}</p>\n <input type="${a}"\n class="form-control"\n id="${i}"\n value="${o}"\n placeholder="${n}">\n `,size:s.size||"sm",centered:!0,backdrop:"static",buttons:[{text:"Cancel",class:"btn-secondary",dismiss:!0},{text:"OK",class:"btn-primary",action:"ok"}],...s}),r=document.querySelector(".table-fullscreen")||document.body;return await l.render(!0,r),l.show(),l.on("shown",()=>{const e=l.element.querySelector(`#${i}`);e&&(e.focus(),e.select())}),new Promise(e=>{let t=null;l.on("action:ok",()=>{const e=l.element.querySelector(`#${i}`);t=e?e.value:null,l.hide()}),l.on("hidden",()=>{l.destroy(),l.element.remove(),e(t)})})}getModal(){return this.modal}isShown(){return this.element?.classList.contains("show")||!1}static async showForm(e={}){const{title:t="Form",formConfig:s={},size:i="md",centered:o=!0,submitText:a="Submit",cancelText:n="Cancel",...l}=e,r=new(0,(await import("./FormView-Cu4iPfvU.js").then(e=>e.b)).default)({fileHandling:e.fileHandling||"base64",data:e.data,defaults:e.defaults,model:e.model,formConfig:{fields:s.fields||e.fields,...s,submitButton:!1,resetButton:!1}}),d=new Dialog({title:t,body:r,size:i,centered:o,buttons:[{text:n,class:"btn-secondary",action:"cancel"},{text:a,class:"btn-primary",action:"submit"}],...l}),c=document.querySelector(".table-fullscreen")||document.body;return await d.render(!0,c),d.show(),new Promise(t=>{let s=!1;d.on("action:submit",async()=>{if(!s)if(r.validate()){if(e.autoSave&&e.model){d.setLoading(!0);const e=await r.saveModel();if(!e.success)return d.setLoading(!1),d.render(),void d.getApp().toast.error(e.message);s=!0,d.hide(),t(e)}try{const e=await r.getFormData();s=!0,d.hide(),t(e)}catch(i){console.error("Error collecting form data:",i),r.showError("Error collecting form data")}}else r.focusFirstError()}),d.on("action:cancel",()=>{s||(s=!0,d.hide(),t(null))}),d.on("hidden",()=>{s||(s=!0,t(null)),setTimeout(()=>{r.destroy(),d.destroy()},100)})})}static async updateModelImage(e={},t={}){const s=e.upload||!1,i=t.name||e.field||"image",o={title:"Upload Your Avatar",model:null,autoSave:!s,size:"sm",fields:[{type:"image",name:i,size:"lg",imageSize:{width:200,height:200},placeholder:"Upload your image",...t}],...e},a=await Dialog.showForm(o);if(!s||!a||!e.model)return a;const n=a[i];if(!n||!n.startsWith("data:"))return a;const l=n.split(","),r=l[0]?.match(/:(.*?);/),d=r?.[1]||"image/png",c=atob(l[1]);let u=c.length;const h=new Uint8Array(u);for(;u--;)h[u]=c.charCodeAt(u);const p=d.split("/")[1]||"png",b="undefined"!=typeof window&&window.File||globalThis.File;if(!b)throw new Error("File API is not available in this environment");const g=new b([h],`${i}.${p}`,{type:d}),y=new m;return await y.upload({file:g,name:`${i}.${p}`,description:e.uploadDescription||`${i} upload`,showToast:!0}),await e.model.save({[i]:y.id})}static async showModelView(e,t){const s=new(0,e.constructor.VIEW_CLASS)({model:e});return t=t||{},await Dialog.showDialog({header:!1,body:s,size:"lg",centered:!1,...t})}static async showModelForm(e={}){const{title:t="Edit",formConfig:s={},size:i="md",centered:o=!0,submitText:a="Save",cancelText:n="Cancel",model:l,fields:r,...d}=e;if(!l)throw new Error("showModelForm requires a model");const c=new(0,(await import("./FormView-Cu4iPfvU.js").then(e=>e.b)).default)({fileHandling:e.fileHandling||"base64",model:l,data:e.data,defaults:e.defaults,formConfig:{fields:r||s.fields||[],...s,submitButton:!1,resetButton:!1}}),u=new Dialog({title:t,body:c,size:i,centered:o,buttons:[{text:n,class:"btn-secondary",action:"cancel"},{text:a,class:"btn-primary",action:"submit"}],...d}),h=document.querySelector(".table-fullscreen")||document.body;return await u.render(!0,h),u.show(),new Promise(e=>{let t=!1;u.on("action:submit",async()=>{if(!t){u.setLoading(!0,"Saving...");try{const s=await c.handleSubmit();if(s.success)t=!0,u.hide(),e(s);else{u.setLoading(!1);let e=s.error;s.data&&s.data.error&&(e=s.data.error),u.getApp().toast.error(e)}}catch(s){console.error("Error saving form:",s),await u.setContent(c),c.showError(s.message||"An error occurred while saving")}}}),u.on("action:cancel",()=>{t||(t=!0,u.hide(),e(null))}),u.on("hidden",()=>{t||(t=!0,e(null)),setTimeout(()=>{c.destroy(),u.destroy()},100)})})}static async showData(e={}){const{title:t="Data View",data:s={},model:i=null,fields:o=[],columns:a=2,responsive:n=!0,showEmptyValues:l=!1,emptyValueText:r="—",size:d="lg",centered:c=!0,closeText:u="Close",...h}=e,m=new(0,(await import("./DataView-D5C_lDdg.js")).default)({data:s,model:i,fields:o,columns:a,responsive:n,showEmptyValues:l,emptyValueText:r}),p=new Dialog({title:t,body:m,size:d,centered:c,buttons:[{text:u,class:"btn-secondary",value:"close"}],...h}),b=document.querySelector(".table-fullscreen")||document.body;return await p.render(!0,b),p.show(),new Promise(e=>{let t=!1;const s=p.element.querySelector(".modal-footer button");s?.addEventListener("click",()=>{t||(t=!0,p.hide(),e(!0))}),p.on("hidden",()=>{t||(t=!0,e(!0)),setTimeout(()=>{m.destroy(),p.destroy(),p.element.remove()},100)}),m.on("field:click",e=>{p.emit("dataview:field:click",e)}),m.on("error",e=>{p.emit("dataview:error",e)})})}}Dialog.showConfirm=Dialog.confirm,Dialog.showError=Dialog.alert;const b=/* @__PURE__ */Object.freeze(/* @__PURE__ */Object.defineProperty({__proto__:null,default:Dialog},Symbol.toStringTag,{value:"Module"}));export{Dialog as D,m as F,GroupList as G,ProgressView as P,ToastService as T,User as U,UserDeviceLocationList as a,UserDeviceList as b,Group as c,p as d,FileList as e,FileManager as f,h as g,FileManagerList as h,FileUpload as i,n as j,c as k,UserDevice as l,UserDeviceLocation as m,d as n,UserList as o,l as p,u as q,b as r};
|
|
3
|
-
//# sourceMappingURL=Dialog-1umNJi4B.js.map
|