web-mojo 2.2.84 → 2.2.85

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/admin.cjs.js +1 -1
  2. package/dist/admin.cjs.js.map +1 -1
  3. package/dist/admin.css +212 -0
  4. package/dist/admin.es.js +1 -1
  5. package/dist/admin.es.js.map +1 -1
  6. package/dist/auth.cjs.js +1 -1
  7. package/dist/auth.es.js +1 -1
  8. package/dist/charts.cjs.js +1 -1
  9. package/dist/charts.es.js +1 -1
  10. package/dist/chunks/{ChatView-BkS8Ek80.js → ChatView-BC4vHjIO.js} +2 -2
  11. package/dist/chunks/{ChatView-BGRSX9Sq.js.map → ChatView-BC4vHjIO.js.map} +1 -1
  12. package/dist/chunks/{ChatView-BGRSX9Sq.js → ChatView-DZBmWPu-.js} +2 -2
  13. package/dist/chunks/{ChatView-BkS8Ek80.js.map → ChatView-DZBmWPu-.js.map} +1 -1
  14. package/dist/chunks/Dialog-2gXM2UcO.js +3 -0
  15. package/dist/chunks/Dialog-2gXM2UcO.js.map +1 -0
  16. package/dist/chunks/Dialog-EZ9XD2AO.js +3 -0
  17. package/dist/chunks/Dialog-EZ9XD2AO.js.map +1 -0
  18. package/dist/chunks/{FormView-CrXCpM9j.js → FormView-BqqtbPVb.js} +2 -2
  19. package/dist/chunks/{FormView-CrXCpM9j.js.map → FormView-BqqtbPVb.js.map} +1 -1
  20. package/dist/chunks/{FormView-BcRIRWcN.js → FormView-CRPeN8tp.js} +2 -2
  21. package/dist/chunks/{FormView-BcRIRWcN.js.map → FormView-CRPeN8tp.js.map} +1 -1
  22. package/dist/chunks/{MetricsMiniChartWidget-Bj9mbQbd.js → MetricsMiniChartWidget-B4CcNlv-.js} +2 -2
  23. package/dist/chunks/{MetricsMiniChartWidget-Bj9mbQbd.js.map → MetricsMiniChartWidget-B4CcNlv-.js.map} +1 -1
  24. package/dist/chunks/{MetricsMiniChartWidget-nmEb8fxQ.js → MetricsMiniChartWidget-y-KklF3c.js} +2 -2
  25. package/dist/chunks/{MetricsMiniChartWidget-nmEb8fxQ.js.map → MetricsMiniChartWidget-y-KklF3c.js.map} +1 -1
  26. package/dist/chunks/{Modal-BOnZ3jlz.js → Modal-DoIYUAKi.js} +2 -2
  27. package/dist/chunks/{Modal-BOnZ3jlz.js.map → Modal-DoIYUAKi.js.map} +1 -1
  28. package/dist/chunks/{Modal-AFggmQ5t.js → Modal-Y1PW_Fmf.js} +2 -2
  29. package/dist/chunks/{Modal-AFggmQ5t.js.map → Modal-Y1PW_Fmf.js.map} +1 -1
  30. package/dist/chunks/{PDFViewer-DTWJyqLN.js → PDFViewer-Cg_tbqb7.js} +2 -2
  31. package/dist/chunks/{PDFViewer-DTWJyqLN.js.map → PDFViewer-Cg_tbqb7.js.map} +1 -1
  32. package/dist/chunks/{PDFViewer-C6jQRd1i.js → PDFViewer-DYAJqRAV.js} +2 -2
  33. package/dist/chunks/{PDFViewer-C6jQRd1i.js.map → PDFViewer-DYAJqRAV.js.map} +1 -1
  34. package/dist/chunks/{Passkeys-BnfMtc_e.js → Passkeys-DNpner4L.js} +2 -2
  35. package/dist/chunks/{Passkeys-BnfMtc_e.js.map → Passkeys-DNpner4L.js.map} +1 -1
  36. package/dist/chunks/{Passkeys-Df_sesCg.js → Passkeys-DapTiqNW.js} +2 -2
  37. package/dist/chunks/{Passkeys-Df_sesCg.js.map → Passkeys-DapTiqNW.js.map} +1 -1
  38. package/dist/chunks/{TokenManager-CpQiouEe.js → TokenManager-BAyuM351.js} +2 -2
  39. package/dist/chunks/{TokenManager-CpQiouEe.js.map → TokenManager-BAyuM351.js.map} +1 -1
  40. package/dist/chunks/{TokenManager-dPm2wXim.js → TokenManager-BLd64x-1.js} +2 -2
  41. package/dist/chunks/{TokenManager-dPm2wXim.js.map → TokenManager-BLd64x-1.js.map} +1 -1
  42. package/dist/chunks/{UserProfileView-C51avAXU.js → UserProfileView-BDD3WOjd.js} +2 -2
  43. package/dist/chunks/{UserProfileView-C51avAXU.js.map → UserProfileView-BDD3WOjd.js.map} +1 -1
  44. package/dist/chunks/{UserProfileView-DDLFDrSq.js → UserProfileView-q29bxm0T.js} +2 -2
  45. package/dist/chunks/{UserProfileView-DDLFDrSq.js.map → UserProfileView-q29bxm0T.js.map} +1 -1
  46. package/dist/chunks/{WebApp-6X5Na7jP.js → WebApp-C98dm94j.js} +2 -2
  47. package/dist/chunks/{WebApp-6X5Na7jP.js.map → WebApp-C98dm94j.js.map} +1 -1
  48. package/dist/chunks/{WebApp-CPMiCjY7.js → WebApp-DgmB8vpR.js} +2 -2
  49. package/dist/chunks/{WebApp-CPMiCjY7.js.map → WebApp-DgmB8vpR.js.map} +1 -1
  50. package/dist/chunks/{index-CsVVg7HM.js → index-DXlIy6bc.js} +2 -2
  51. package/dist/chunks/{index-CsVVg7HM.js.map → index-DXlIy6bc.js.map} +1 -1
  52. package/dist/chunks/{index-B8MmVPsw.js → index-iPINfbSy.js} +2 -2
  53. package/dist/chunks/{index-B8MmVPsw.js.map → index-iPINfbSy.js.map} +1 -1
  54. package/dist/chunks/{version-C9BOiwG3.js → version-Bps8Ct3l.js} +2 -2
  55. package/dist/chunks/{version-C9BOiwG3.js.map → version-Bps8Ct3l.js.map} +1 -1
  56. package/dist/chunks/{version-gygZr-Ew.js → version-iFGy9HZj.js} +2 -2
  57. package/dist/chunks/{version-gygZr-Ew.js.map → version-iFGy9HZj.js.map} +1 -1
  58. package/dist/css/web-mojo.css +1 -1
  59. package/dist/docit.cjs.js +1 -1
  60. package/dist/docit.es.js +1 -1
  61. package/dist/index.cjs.js +1 -1
  62. package/dist/index.es.js +1 -1
  63. package/dist/lightbox.cjs.js +1 -1
  64. package/dist/lightbox.es.js +1 -1
  65. package/dist/map.cjs.js +1 -1
  66. package/dist/map.es.js +1 -1
  67. package/dist/user-profile.cjs.js +1 -1
  68. package/dist/user-profile.es.js +1 -1
  69. package/dist/web-mojo.lite.iife.js +2 -1
  70. package/dist/web-mojo.lite.iife.js.map +1 -1
  71. package/dist/web-mojo.lite.iife.min.js +1 -1
  72. package/dist/web-mojo.lite.iife.min.js.map +1 -1
  73. package/package.json +1 -1
  74. package/dist/chunks/Dialog-DB9A3lyf.js +0 -3
  75. package/dist/chunks/Dialog-DB9A3lyf.js.map +0 -1
  76. package/dist/chunks/Dialog-v6R0OESu.js +0 -3
  77. package/dist/chunks/Dialog-v6R0OESu.js.map +0 -1
@@ -1,2 +1,2 @@
1
- import{D as o}from"./Dialog-v6R0OESu.js";class Modal{static async show(n,a={}){return o.showDialog({header:void 0!==a.title&&!!a.title,title:a.title||void 0,body:n,size:"lg",centered:!1,buttons:[{text:"Close",class:"btn-secondary",dismiss:!0}],...a})}static async showModel(o,n={}){const a=o.constructor,e=a?.VIEW_CLASS;if(!e)throw new Error(`Modal.showModel: No VIEW_CLASS defined on ${a?.name||"model"}. Set ${a?.name||"Model"}.VIEW_CLASS = YourView to use this method.`);const t=new e({model:o});return Modal.show(t,n)}static async showModelById(n,a,e={}){const t=new n({id:a});return await t.fetch(),t.id?Modal.showModel(t,e):(o.alert({message:`Could not find ${n.name||"record"} with ID: ${a}`,type:"warning"}),null)}static confirm(n,a,e){return o.confirm(n,a,e)}static alert(n,a,e){return o.alert(n,a,e)}static prompt(n,a,e){return o.prompt(n,a,e)}static form(n){return o.showForm(n)}static modelForm(n){return o.showModelForm(n)}static data(n){return o.showData(n)}static dialog(n){return o.showDialog(n)}static showError(n){return o.alert(n,"Error",{type:"danger"})}static _loadingEl=null;static _loadingCounter=0;static _loadingTimeout=null;static loading(o){"string"==typeof o&&(o={message:o});const{message:n="Loading...",timeout:a=3e4}=o||{};if(Modal._loadingCounter++,1===Modal._loadingCounter){Modal._loadingTimeout&&clearTimeout(Modal._loadingTimeout),Modal._loadingEl||(Modal._loadingEl=document.createElement("div"),Modal._loadingEl.className="mojo-loading-overlay",Modal._loadingEl.innerHTML=`\n <div class="mojo-loading-card">\n <div class="mojo-loading-spinner"></div>\n <div class="mojo-loading-message">${n}</div>\n </div>\n <style>\n .mojo-loading-overlay {\n position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;\n background: rgba(255, 255, 255, 0.4);\n backdrop-filter: blur(2px);\n -webkit-backdrop-filter: blur(2px);\n z-index: 99999;\n display: flex; align-items: center; justify-content: center;\n opacity: 0; transition: opacity 0.2s ease;\n }\n .mojo-loading-overlay.show { opacity: 1; }\n .mojo-loading-card {\n display: flex; align-items: center; gap: 0.85rem;\n background: #fff;\n border: 1px solid #e9ecef;\n border-radius: 12px;\n padding: 1rem 1.5rem;\n box-shadow: 0 4px 24px rgba(0,0,0,0.08), 0 1px 4px rgba(0,0,0,0.04);\n }\n .mojo-loading-spinner {\n width: 22px; height: 22px;\n border: 2.5px solid #e9ecef;\n border-top-color: #0d6efd;\n border-radius: 50%;\n animation: mojo-spin 0.7s linear infinite;\n flex-shrink: 0;\n }\n .mojo-loading-message {\n font-size: 0.88rem;\n font-weight: 500;\n color: #495057;\n white-space: nowrap;\n }\n @keyframes mojo-spin {\n to { transform: rotate(360deg); }\n }\n </style>\n `,document.body.appendChild(Modal._loadingEl));const o=Modal._loadingEl.querySelector(".mojo-loading-message");o&&(o.textContent=n),requestAnimationFrame(()=>{Modal._loadingEl&&Modal._loadingEl.classList.add("show")}),a>0&&(Modal._loadingTimeout=setTimeout(()=>{console.error("Modal.loading timed out."),Modal.hideLoading(!0)},a))}else if(Modal._loadingEl){const o=Modal._loadingEl.querySelector(".mojo-loading-message");o&&(o.textContent=n)}}static hideLoading(o){o?Modal._loadingCounter=0:Modal._loadingCounter--,Modal._loadingCounter<=0&&(Modal._loadingCounter=0,Modal._loadingTimeout&&(clearTimeout(Modal._loadingTimeout),Modal._loadingTimeout=null),Modal._loadingEl&&(Modal._loadingEl.classList.remove("show"),setTimeout(()=>{Modal._loadingEl&&0===Modal._loadingCounter&&(Modal._loadingEl.remove(),Modal._loadingEl=null)},200)))}static showBusy(o){return Modal.loading(o)}static hideBusy(o){return Modal.hideLoading(o)}}export{Modal as default};
2
- //# sourceMappingURL=Modal-BOnZ3jlz.js.map
1
+ import{D as o}from"./Dialog-EZ9XD2AO.js";class Modal{static async show(n,a={}){return o.showDialog({header:void 0!==a.title&&!!a.title,title:a.title||void 0,body:n,size:"lg",centered:!1,buttons:[{text:"Close",class:"btn-secondary",dismiss:!0}],...a})}static async showModel(o,n={}){const a=o.constructor,e=a?.VIEW_CLASS;if(!e)throw new Error(`Modal.showModel: No VIEW_CLASS defined on ${a?.name||"model"}. Set ${a?.name||"Model"}.VIEW_CLASS = YourView to use this method.`);const t=new e({model:o});return Modal.show(t,n)}static async showModelById(n,a,e={}){const t=new n({id:a});return await t.fetch(),t.id?Modal.showModel(t,e):(o.alert({message:`Could not find ${n.name||"record"} with ID: ${a}`,type:"warning"}),null)}static confirm(n,a,e){return o.confirm(n,a,e)}static alert(n,a,e){return o.alert(n,a,e)}static prompt(n,a,e){return o.prompt(n,a,e)}static form(n){return o.showForm(n)}static modelForm(n){return o.showModelForm(n)}static data(n){return o.showData(n)}static dialog(n){return o.showDialog(n)}static showError(n){return o.alert(n,"Error",{type:"danger"})}static _loadingEl=null;static _loadingCounter=0;static _loadingTimeout=null;static loading(o){"string"==typeof o&&(o={message:o});const{message:n="Loading...",timeout:a=3e4}=o||{};if(Modal._loadingCounter++,1===Modal._loadingCounter){Modal._loadingTimeout&&clearTimeout(Modal._loadingTimeout),Modal._loadingEl||(Modal._loadingEl=document.createElement("div"),Modal._loadingEl.className="mojo-loading-overlay",Modal._loadingEl.innerHTML=`\n <div class="mojo-loading-card">\n <div class="mojo-loading-spinner"></div>\n <div class="mojo-loading-message">${n}</div>\n </div>\n <style>\n .mojo-loading-overlay {\n position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;\n background: rgba(255, 255, 255, 0.4);\n backdrop-filter: blur(2px);\n -webkit-backdrop-filter: blur(2px);\n z-index: 99999;\n display: flex; align-items: center; justify-content: center;\n opacity: 0; transition: opacity 0.2s ease;\n }\n .mojo-loading-overlay.show { opacity: 1; }\n .mojo-loading-card {\n display: flex; align-items: center; gap: 0.85rem;\n background: #fff;\n border: 1px solid #e9ecef;\n border-radius: 12px;\n padding: 1rem 1.5rem;\n box-shadow: 0 4px 24px rgba(0,0,0,0.08), 0 1px 4px rgba(0,0,0,0.04);\n }\n .mojo-loading-spinner {\n width: 22px; height: 22px;\n border: 2.5px solid #e9ecef;\n border-top-color: #0d6efd;\n border-radius: 50%;\n animation: mojo-spin 0.7s linear infinite;\n flex-shrink: 0;\n }\n .mojo-loading-message {\n font-size: 0.88rem;\n font-weight: 500;\n color: #495057;\n white-space: nowrap;\n }\n @keyframes mojo-spin {\n to { transform: rotate(360deg); }\n }\n </style>\n `,document.body.appendChild(Modal._loadingEl));const o=Modal._loadingEl.querySelector(".mojo-loading-message");o&&(o.textContent=n),requestAnimationFrame(()=>{Modal._loadingEl&&Modal._loadingEl.classList.add("show")}),a>0&&(Modal._loadingTimeout=setTimeout(()=>{console.error("Modal.loading timed out."),Modal.hideLoading(!0)},a))}else if(Modal._loadingEl){const o=Modal._loadingEl.querySelector(".mojo-loading-message");o&&(o.textContent=n)}}static hideLoading(o){o?Modal._loadingCounter=0:Modal._loadingCounter--,Modal._loadingCounter<=0&&(Modal._loadingCounter=0,Modal._loadingTimeout&&(clearTimeout(Modal._loadingTimeout),Modal._loadingTimeout=null),Modal._loadingEl&&(Modal._loadingEl.classList.remove("show"),setTimeout(()=>{Modal._loadingEl&&0===Modal._loadingCounter&&(Modal._loadingEl.remove(),Modal._loadingEl=null)},200)))}static showBusy(o){return Modal.loading(o)}static hideBusy(o){return Modal.hideLoading(o)}}export{Modal as default};
2
+ //# sourceMappingURL=Modal-DoIYUAKi.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Modal-BOnZ3jlz.js","sources":["../../src/core/views/feedback/Modal.js"],"sourcesContent":["/**\n * Modal — AI-friendly static API for showing views in modal dialogs.\n *\n * Thin wrapper around Dialog's static helpers. All methods are static —\n * there is no instance API. Use Modal instead of `new Dialog()`.\n *\n * @example\n * import Modal from '@core/views/feedback/Modal.js';\n *\n * // Show any View in a modal\n * await Modal.show(new MyView({ model }));\n *\n * // Show a model's VIEW_CLASS automatically\n * await Modal.showModel(userModel);\n *\n * // Fetch a model by ID, then show its VIEW_CLASS\n * await Modal.showModelById(User, 42);\n *\n * // Convenience — no need to import Dialog separately\n * const yes = await Modal.confirm('Delete this?', 'Confirm');\n * await Modal.alert('Done!');\n * const data = await Modal.form({ title: 'Add', fields: [...] });\n * await Modal.modelForm({ model, formConfig: MyForms.edit });\n */\nimport Dialog from './Dialog.js';\n\nclass Modal {\n\n /**\n * Show a View instance in a modal dialog.\n *\n * @param {View} view - The view to display (rendered automatically)\n * @param {object} [options] - Dialog options (size, title, buttons, etc.)\n * @param {string} [options.size='lg'] - Dialog size: sm, md, lg, xl, xxl, fullscreen\n * @param {string|false} [options.title=false] - Dialog title (false = no header)\n * @param {Array} [options.buttons] - Footer buttons\n * @returns {Promise<*>} Resolves with button value or null if dismissed\n *\n * @example\n * await Modal.show(new DeviceView({ model: device }));\n * await Modal.show(new GroupView({ model }), { size: 'xl' });\n */\n static async show(view, options = {}) {\n return Dialog.showDialog({\n header: options.title !== undefined ? !!options.title : false,\n title: options.title || undefined,\n body: view,\n size: 'lg',\n centered: false,\n buttons: [{ text: 'Close', class: 'btn-secondary', dismiss: true }],\n ...options\n });\n }\n\n /**\n * Show a model's VIEW_CLASS in a modal dialog.\n * Looks up `model.constructor.VIEW_CLASS` and instantiates it.\n *\n * @param {Model} model - Model instance (must have VIEW_CLASS on its constructor)\n * @param {object} [options] - Dialog options passed to Modal.show()\n * @returns {Promise<*>} Resolves with button value or null if dismissed\n * @throws {Error} If no VIEW_CLASS is defined on the model's constructor\n *\n * @example\n * await Modal.showModel(userModel);\n * await Modal.showModel(groupModel, { size: 'xl' });\n */\n static async showModel(model, options = {}) {\n const ModelClass = model.constructor;\n const ViewClass = ModelClass?.VIEW_CLASS;\n\n if (!ViewClass) {\n throw new Error(\n `Modal.showModel: No VIEW_CLASS defined on ${ModelClass?.name || 'model'}. ` +\n `Set ${ModelClass?.name || 'Model'}.VIEW_CLASS = YourView to use this method.`\n );\n }\n\n const view = new ViewClass({ model });\n return Modal.show(view, options);\n }\n\n /**\n * Fetch a model by ID, then show its VIEW_CLASS in a modal.\n * Handles the common fetch-then-display pattern.\n *\n * @param {Function} ModelClass - The Model class constructor (e.g., User, Group)\n * @param {string|number} id - The model ID to fetch\n * @param {object} [options] - Dialog options passed to Modal.showModel()\n * @returns {Promise<*>} Resolves with button value, or null if model not found\n *\n * @example\n * await Modal.showModelById(User, 42);\n * await Modal.showModelById(Group, parentId, { size: 'xl' });\n */\n static async showModelById(ModelClass, id, options = {}) {\n const model = new ModelClass({ id });\n await model.fetch();\n\n if (!model.id) {\n Dialog.alert({\n message: `Could not find ${ModelClass.name || 'record'} with ID: ${id}`,\n type: 'warning'\n });\n return null;\n }\n\n return Modal.showModel(model, options);\n }\n\n // ── Convenience aliases ─────────────────────────\n // Re-exported so you never need to import Dialog separately.\n\n /** @see Dialog.confirm */\n static confirm(message, title, options) {\n return Dialog.confirm(message, title, options);\n }\n\n /** @see Dialog.alert */\n static alert(message, title, options) {\n return Dialog.alert(message, title, options);\n }\n\n /** @see Dialog.prompt */\n static prompt(message, title, options) {\n return Dialog.prompt(message, title, options);\n }\n\n /** @see Dialog.showForm */\n static form(options) {\n return Dialog.showForm(options);\n }\n\n /** @see Dialog.showModelForm */\n static modelForm(options) {\n return Dialog.showModelForm(options);\n }\n\n /** @see Dialog.showData */\n static data(options) {\n return Dialog.showData(options);\n }\n\n /** @see Dialog.showDialog — generic dialog with full options control */\n static dialog(options) {\n return Dialog.showDialog(options);\n }\n\n /** @see Dialog.showError */\n static showError(message) {\n return Dialog.alert(message, 'Error', { type: 'danger' });\n }\n\n // ── Loading indicator ───────────────────────\n // Full-screen overlay with spinner for blocking operations.\n // Supports nested calls (counter-based) — call hide for each show.\n // Modern frosted-glass card design with configurable message.\n\n static _loadingEl = null;\n static _loadingCounter = 0;\n static _loadingTimeout = null;\n\n /**\n * Show full-screen loading overlay.\n * @param {string|object} [options] - Message string or { message, timeout }\n * @param {string} [options.message='Loading...'] - Message to display\n * @param {number} [options.timeout=30000] - Auto-hide timeout in ms (0 = no timeout)\n * @example\n * Modal.loading('Saving...');\n * await someApiCall();\n * Modal.hideLoading();\n *\n * // With timeout\n * Modal.loading({ message: 'Processing...', timeout: 60000 });\n */\n static loading(options) {\n if (typeof options === 'string') options = { message: options };\n const { message = 'Loading...', timeout = 30000 } = options || {};\n\n Modal._loadingCounter++;\n\n if (Modal._loadingCounter === 1) {\n if (Modal._loadingTimeout) {\n clearTimeout(Modal._loadingTimeout);\n }\n\n if (!Modal._loadingEl) {\n Modal._loadingEl = document.createElement('div');\n Modal._loadingEl.className = 'mojo-loading-overlay';\n Modal._loadingEl.innerHTML = `\n <div class=\"mojo-loading-card\">\n <div class=\"mojo-loading-spinner\"></div>\n <div class=\"mojo-loading-message\">${message}</div>\n </div>\n <style>\n .mojo-loading-overlay {\n position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;\n background: rgba(255, 255, 255, 0.4);\n backdrop-filter: blur(2px);\n -webkit-backdrop-filter: blur(2px);\n z-index: 99999;\n display: flex; align-items: center; justify-content: center;\n opacity: 0; transition: opacity 0.2s ease;\n }\n .mojo-loading-overlay.show { opacity: 1; }\n .mojo-loading-card {\n display: flex; align-items: center; gap: 0.85rem;\n background: #fff;\n border: 1px solid #e9ecef;\n border-radius: 12px;\n padding: 1rem 1.5rem;\n box-shadow: 0 4px 24px rgba(0,0,0,0.08), 0 1px 4px rgba(0,0,0,0.04);\n }\n .mojo-loading-spinner {\n width: 22px; height: 22px;\n border: 2.5px solid #e9ecef;\n border-top-color: #0d6efd;\n border-radius: 50%;\n animation: mojo-spin 0.7s linear infinite;\n flex-shrink: 0;\n }\n .mojo-loading-message {\n font-size: 0.88rem;\n font-weight: 500;\n color: #495057;\n white-space: nowrap;\n }\n @keyframes mojo-spin {\n to { transform: rotate(360deg); }\n }\n </style>\n `;\n document.body.appendChild(Modal._loadingEl);\n }\n\n // Update message\n const msgEl = Modal._loadingEl.querySelector('.mojo-loading-message');\n if (msgEl) msgEl.textContent = message;\n\n // Trigger fade-in\n requestAnimationFrame(() => {\n if (Modal._loadingEl) Modal._loadingEl.classList.add('show');\n });\n\n // Auto-timeout\n if (timeout > 0) {\n Modal._loadingTimeout = setTimeout(() => {\n console.error('Modal.loading timed out.');\n Modal.hideLoading(true);\n }, timeout);\n }\n } else {\n // Counter > 1: just update the message\n if (Modal._loadingEl) {\n const msgEl = Modal._loadingEl.querySelector('.mojo-loading-message');\n if (msgEl) msgEl.textContent = message;\n }\n }\n }\n\n /**\n * Hide the loading overlay.\n * @param {boolean} [force=false] - Force-hide regardless of counter\n * @example\n * Modal.hideLoading(); // decrement counter\n * Modal.hideLoading(true); // force-hide immediately\n */\n static hideLoading(force) {\n if (force) {\n Modal._loadingCounter = 0;\n } else {\n Modal._loadingCounter--;\n }\n\n if (Modal._loadingCounter <= 0) {\n Modal._loadingCounter = 0;\n\n if (Modal._loadingTimeout) {\n clearTimeout(Modal._loadingTimeout);\n Modal._loadingTimeout = null;\n }\n\n if (Modal._loadingEl) {\n Modal._loadingEl.classList.remove('show');\n setTimeout(() => {\n if (Modal._loadingEl && Modal._loadingCounter === 0) {\n Modal._loadingEl.remove();\n Modal._loadingEl = null;\n }\n }, 200);\n }\n }\n }\n\n /** @alias loading — backward compat with Dialog.showBusy */\n static showBusy(options) { return Modal.loading(options); }\n /** @alias hideLoading — backward compat with Dialog.hideBusy */\n static hideBusy(force) { return Modal.hideLoading(force); }\n}\n\nexport default Modal;\n"],"names":["Modal","show","view","options","Dialog","showDialog","header","title","body","size","centered","buttons","text","class","dismiss","showModel","model","ModelClass","constructor","ViewClass","VIEW_CLASS","Error","name","showModelById","id","fetch","alert","message","type","confirm","prompt","form","showForm","modelForm","showModelForm","data","showData","dialog","showError","static","loading","timeout","_loadingCounter","_loadingTimeout","clearTimeout","_loadingEl","document","createElement","className","innerHTML","appendChild","msgEl","querySelector","textContent","requestAnimationFrame","classList","add","setTimeout","console","error","hideLoading","force","remove","showBusy","hideBusy"],"mappings":"yCA0BA,MAAMA,MAgBF,iBAAaC,CAAKC,EAAMC,EAAU,IAC9B,OAAOC,EAAOC,WAAW,CACrBC,YAA0B,IAAlBH,EAAQI,SAAwBJ,EAAQI,MAChDA,MAAOJ,EAAQI,YAAS,EACxBC,KAAMN,EACNO,KAAM,KACNC,UAAU,EACVC,QAAS,CAAC,CAAEC,KAAM,QAASC,MAAO,gBAAiBC,SAAS,OACzDX,GAEX,CAeA,sBAAaY,CAAUC,EAAOb,EAAU,IACpC,MAAMc,EAAaD,EAAME,YACnBC,EAAYF,GAAYG,WAE9B,IAAKD,EACD,MAAM,IAAIE,MACN,6CAA6CJ,GAAYK,MAAQ,gBAC1DL,GAAYK,MAAQ,qDAInC,MAAMpB,EAAO,IAAIiB,EAAU,CAAEH,UAC7B,OAAOhB,MAAMC,KAAKC,EAAMC,EAC5B,CAeA,0BAAaoB,CAAcN,EAAYO,EAAIrB,EAAU,CAAA,GACjD,MAAMa,EAAQ,IAAIC,EAAW,CAAEO,OAG/B,aAFMR,EAAMS,QAEPT,EAAMQ,GAQJxB,MAAMe,UAAUC,EAAOb,IAP1BC,EAAOsB,MAAM,CACTC,QAAS,kBAAkBV,EAAWK,MAAQ,qBAAqBE,IACnEI,KAAM,YAEH,KAIf,CAMA,cAAOC,CAAQF,EAASpB,EAAOJ,GAC3B,OAAOC,EAAOyB,QAAQF,EAASpB,EAAOJ,EAC1C,CAGA,YAAOuB,CAAMC,EAASpB,EAAOJ,GACzB,OAAOC,EAAOsB,MAAMC,EAASpB,EAAOJ,EACxC,CAGA,aAAO2B,CAAOH,EAASpB,EAAOJ,GAC1B,OAAOC,EAAO0B,OAAOH,EAASpB,EAAOJ,EACzC,CAGA,WAAO4B,CAAK5B,GACR,OAAOC,EAAO4B,SAAS7B,EAC3B,CAGA,gBAAO8B,CAAU9B,GACb,OAAOC,EAAO8B,cAAc/B,EAChC,CAGA,WAAOgC,CAAKhC,GACR,OAAOC,EAAOgC,SAASjC,EAC3B,CAGA,aAAOkC,CAAOlC,GACV,OAAOC,EAAOC,WAAWF,EAC7B,CAGA,gBAAOmC,CAAUX,GACb,OAAOvB,EAAOsB,MAAMC,EAAS,QAAS,CAAEC,KAAM,UAClD,CAOAW,kBAAoB,KACpBA,uBAAyB,EACzBA,uBAAyB,KAezB,cAAOC,CAAQrC,GACY,iBAAZA,IAAsBA,EAAU,CAAEwB,QAASxB,IACtD,MAAMwB,QAAEA,EAAU,aAAAc,QAAcA,EAAU,KAAUtC,GAAW,CAAA,EAI/D,GAFAH,MAAM0C,kBAEwB,IAA1B1C,MAAM0C,gBAAuB,CACzB1C,MAAM2C,iBACNC,aAAa5C,MAAM2C,iBAGlB3C,MAAM6C,aACP7C,MAAM6C,WAAaC,SAASC,cAAc,OAC1C/C,MAAM6C,WAAWG,UAAY,uBAC7BhD,MAAM6C,WAAWI,UAAY,sLAGetB,k+DAwC5CmB,SAAStC,KAAK0C,YAAYlD,MAAM6C,aAIpC,MAAMM,EAAQnD,MAAM6C,WAAWO,cAAc,yBACzCD,MAAaE,YAAc1B,GAG/B2B,sBAAsB,KACdtD,MAAM6C,YAAY7C,MAAM6C,WAAWU,UAAUC,IAAI,UAIrDf,EAAU,IACVzC,MAAM2C,gBAAkBc,WAAW,KAC/BC,QAAQC,MAAM,4BACd3D,MAAM4D,aAAY,IACnBnB,GAEX,MAEI,GAAIzC,MAAM6C,WAAY,CAClB,MAAMM,EAAQnD,MAAM6C,WAAWO,cAAc,yBACzCD,MAAaE,YAAc1B,EACnC,CAER,CASA,kBAAOiC,CAAYC,GACXA,EACA7D,MAAM0C,gBAAkB,EAExB1C,MAAM0C,kBAGN1C,MAAM0C,iBAAmB,IACzB1C,MAAM0C,gBAAkB,EAEpB1C,MAAM2C,kBACNC,aAAa5C,MAAM2C,iBACnB3C,MAAM2C,gBAAkB,MAGxB3C,MAAM6C,aACN7C,MAAM6C,WAAWU,UAAUO,OAAO,QAClCL,WAAW,KACHzD,MAAM6C,YAAwC,IAA1B7C,MAAM0C,kBAC1B1C,MAAM6C,WAAWiB,SACjB9D,MAAM6C,WAAa,OAExB,MAGf,CAGA,eAAOkB,CAAS5D,GAAW,OAAOH,MAAMwC,QAAQrC,EAAU,CAE1D,eAAO6D,CAASH,GAAS,OAAO7D,MAAM4D,YAAYC,EAAQ"}
1
+ {"version":3,"file":"Modal-DoIYUAKi.js","sources":["../../src/core/views/feedback/Modal.js"],"sourcesContent":["/**\n * Modal — AI-friendly static API for showing views in modal dialogs.\n *\n * Thin wrapper around Dialog's static helpers. All methods are static —\n * there is no instance API. Use Modal instead of `new Dialog()`.\n *\n * @example\n * import Modal from '@core/views/feedback/Modal.js';\n *\n * // Show any View in a modal\n * await Modal.show(new MyView({ model }));\n *\n * // Show a model's VIEW_CLASS automatically\n * await Modal.showModel(userModel);\n *\n * // Fetch a model by ID, then show its VIEW_CLASS\n * await Modal.showModelById(User, 42);\n *\n * // Convenience — no need to import Dialog separately\n * const yes = await Modal.confirm('Delete this?', 'Confirm');\n * await Modal.alert('Done!');\n * const data = await Modal.form({ title: 'Add', fields: [...] });\n * await Modal.modelForm({ model, formConfig: MyForms.edit });\n */\nimport Dialog from './Dialog.js';\n\nclass Modal {\n\n /**\n * Show a View instance in a modal dialog.\n *\n * @param {View} view - The view to display (rendered automatically)\n * @param {object} [options] - Dialog options (size, title, buttons, etc.)\n * @param {string} [options.size='lg'] - Dialog size: sm, md, lg, xl, xxl, fullscreen\n * @param {string|false} [options.title=false] - Dialog title (false = no header)\n * @param {Array} [options.buttons] - Footer buttons\n * @returns {Promise<*>} Resolves with button value or null if dismissed\n *\n * @example\n * await Modal.show(new DeviceView({ model: device }));\n * await Modal.show(new GroupView({ model }), { size: 'xl' });\n */\n static async show(view, options = {}) {\n return Dialog.showDialog({\n header: options.title !== undefined ? !!options.title : false,\n title: options.title || undefined,\n body: view,\n size: 'lg',\n centered: false,\n buttons: [{ text: 'Close', class: 'btn-secondary', dismiss: true }],\n ...options\n });\n }\n\n /**\n * Show a model's VIEW_CLASS in a modal dialog.\n * Looks up `model.constructor.VIEW_CLASS` and instantiates it.\n *\n * @param {Model} model - Model instance (must have VIEW_CLASS on its constructor)\n * @param {object} [options] - Dialog options passed to Modal.show()\n * @returns {Promise<*>} Resolves with button value or null if dismissed\n * @throws {Error} If no VIEW_CLASS is defined on the model's constructor\n *\n * @example\n * await Modal.showModel(userModel);\n * await Modal.showModel(groupModel, { size: 'xl' });\n */\n static async showModel(model, options = {}) {\n const ModelClass = model.constructor;\n const ViewClass = ModelClass?.VIEW_CLASS;\n\n if (!ViewClass) {\n throw new Error(\n `Modal.showModel: No VIEW_CLASS defined on ${ModelClass?.name || 'model'}. ` +\n `Set ${ModelClass?.name || 'Model'}.VIEW_CLASS = YourView to use this method.`\n );\n }\n\n const view = new ViewClass({ model });\n return Modal.show(view, options);\n }\n\n /**\n * Fetch a model by ID, then show its VIEW_CLASS in a modal.\n * Handles the common fetch-then-display pattern.\n *\n * @param {Function} ModelClass - The Model class constructor (e.g., User, Group)\n * @param {string|number} id - The model ID to fetch\n * @param {object} [options] - Dialog options passed to Modal.showModel()\n * @returns {Promise<*>} Resolves with button value, or null if model not found\n *\n * @example\n * await Modal.showModelById(User, 42);\n * await Modal.showModelById(Group, parentId, { size: 'xl' });\n */\n static async showModelById(ModelClass, id, options = {}) {\n const model = new ModelClass({ id });\n await model.fetch();\n\n if (!model.id) {\n Dialog.alert({\n message: `Could not find ${ModelClass.name || 'record'} with ID: ${id}`,\n type: 'warning'\n });\n return null;\n }\n\n return Modal.showModel(model, options);\n }\n\n // ── Convenience aliases ─────────────────────────\n // Re-exported so you never need to import Dialog separately.\n\n /** @see Dialog.confirm */\n static confirm(message, title, options) {\n return Dialog.confirm(message, title, options);\n }\n\n /** @see Dialog.alert */\n static alert(message, title, options) {\n return Dialog.alert(message, title, options);\n }\n\n /** @see Dialog.prompt */\n static prompt(message, title, options) {\n return Dialog.prompt(message, title, options);\n }\n\n /** @see Dialog.showForm */\n static form(options) {\n return Dialog.showForm(options);\n }\n\n /** @see Dialog.showModelForm */\n static modelForm(options) {\n return Dialog.showModelForm(options);\n }\n\n /** @see Dialog.showData */\n static data(options) {\n return Dialog.showData(options);\n }\n\n /** @see Dialog.showDialog — generic dialog with full options control */\n static dialog(options) {\n return Dialog.showDialog(options);\n }\n\n /** @see Dialog.showError */\n static showError(message) {\n return Dialog.alert(message, 'Error', { type: 'danger' });\n }\n\n // ── Loading indicator ───────────────────────\n // Full-screen overlay with spinner for blocking operations.\n // Supports nested calls (counter-based) — call hide for each show.\n // Modern frosted-glass card design with configurable message.\n\n static _loadingEl = null;\n static _loadingCounter = 0;\n static _loadingTimeout = null;\n\n /**\n * Show full-screen loading overlay.\n * @param {string|object} [options] - Message string or { message, timeout }\n * @param {string} [options.message='Loading...'] - Message to display\n * @param {number} [options.timeout=30000] - Auto-hide timeout in ms (0 = no timeout)\n * @example\n * Modal.loading('Saving...');\n * await someApiCall();\n * Modal.hideLoading();\n *\n * // With timeout\n * Modal.loading({ message: 'Processing...', timeout: 60000 });\n */\n static loading(options) {\n if (typeof options === 'string') options = { message: options };\n const { message = 'Loading...', timeout = 30000 } = options || {};\n\n Modal._loadingCounter++;\n\n if (Modal._loadingCounter === 1) {\n if (Modal._loadingTimeout) {\n clearTimeout(Modal._loadingTimeout);\n }\n\n if (!Modal._loadingEl) {\n Modal._loadingEl = document.createElement('div');\n Modal._loadingEl.className = 'mojo-loading-overlay';\n Modal._loadingEl.innerHTML = `\n <div class=\"mojo-loading-card\">\n <div class=\"mojo-loading-spinner\"></div>\n <div class=\"mojo-loading-message\">${message}</div>\n </div>\n <style>\n .mojo-loading-overlay {\n position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;\n background: rgba(255, 255, 255, 0.4);\n backdrop-filter: blur(2px);\n -webkit-backdrop-filter: blur(2px);\n z-index: 99999;\n display: flex; align-items: center; justify-content: center;\n opacity: 0; transition: opacity 0.2s ease;\n }\n .mojo-loading-overlay.show { opacity: 1; }\n .mojo-loading-card {\n display: flex; align-items: center; gap: 0.85rem;\n background: #fff;\n border: 1px solid #e9ecef;\n border-radius: 12px;\n padding: 1rem 1.5rem;\n box-shadow: 0 4px 24px rgba(0,0,0,0.08), 0 1px 4px rgba(0,0,0,0.04);\n }\n .mojo-loading-spinner {\n width: 22px; height: 22px;\n border: 2.5px solid #e9ecef;\n border-top-color: #0d6efd;\n border-radius: 50%;\n animation: mojo-spin 0.7s linear infinite;\n flex-shrink: 0;\n }\n .mojo-loading-message {\n font-size: 0.88rem;\n font-weight: 500;\n color: #495057;\n white-space: nowrap;\n }\n @keyframes mojo-spin {\n to { transform: rotate(360deg); }\n }\n </style>\n `;\n document.body.appendChild(Modal._loadingEl);\n }\n\n // Update message\n const msgEl = Modal._loadingEl.querySelector('.mojo-loading-message');\n if (msgEl) msgEl.textContent = message;\n\n // Trigger fade-in\n requestAnimationFrame(() => {\n if (Modal._loadingEl) Modal._loadingEl.classList.add('show');\n });\n\n // Auto-timeout\n if (timeout > 0) {\n Modal._loadingTimeout = setTimeout(() => {\n console.error('Modal.loading timed out.');\n Modal.hideLoading(true);\n }, timeout);\n }\n } else {\n // Counter > 1: just update the message\n if (Modal._loadingEl) {\n const msgEl = Modal._loadingEl.querySelector('.mojo-loading-message');\n if (msgEl) msgEl.textContent = message;\n }\n }\n }\n\n /**\n * Hide the loading overlay.\n * @param {boolean} [force=false] - Force-hide regardless of counter\n * @example\n * Modal.hideLoading(); // decrement counter\n * Modal.hideLoading(true); // force-hide immediately\n */\n static hideLoading(force) {\n if (force) {\n Modal._loadingCounter = 0;\n } else {\n Modal._loadingCounter--;\n }\n\n if (Modal._loadingCounter <= 0) {\n Modal._loadingCounter = 0;\n\n if (Modal._loadingTimeout) {\n clearTimeout(Modal._loadingTimeout);\n Modal._loadingTimeout = null;\n }\n\n if (Modal._loadingEl) {\n Modal._loadingEl.classList.remove('show');\n setTimeout(() => {\n if (Modal._loadingEl && Modal._loadingCounter === 0) {\n Modal._loadingEl.remove();\n Modal._loadingEl = null;\n }\n }, 200);\n }\n }\n }\n\n /** @alias loading — backward compat with Dialog.showBusy */\n static showBusy(options) { return Modal.loading(options); }\n /** @alias hideLoading — backward compat with Dialog.hideBusy */\n static hideBusy(force) { return Modal.hideLoading(force); }\n}\n\nexport default Modal;\n"],"names":["Modal","show","view","options","Dialog","showDialog","header","title","body","size","centered","buttons","text","class","dismiss","showModel","model","ModelClass","constructor","ViewClass","VIEW_CLASS","Error","name","showModelById","id","fetch","alert","message","type","confirm","prompt","form","showForm","modelForm","showModelForm","data","showData","dialog","showError","static","loading","timeout","_loadingCounter","_loadingTimeout","clearTimeout","_loadingEl","document","createElement","className","innerHTML","appendChild","msgEl","querySelector","textContent","requestAnimationFrame","classList","add","setTimeout","console","error","hideLoading","force","remove","showBusy","hideBusy"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./Dialog-DB9A3lyf.js");class Modal{static async show(a,e={}){return o.Dialog.showDialog({header:void 0!==e.title&&!!e.title,title:e.title||void 0,body:a,size:"lg",centered:!1,buttons:[{text:"Close",class:"btn-secondary",dismiss:!0}],...e})}static async showModel(o,a={}){const e=o.constructor,n=e?.VIEW_CLASS;if(!n)throw new Error(`Modal.showModel: No VIEW_CLASS defined on ${e?.name||"model"}. Set ${e?.name||"Model"}.VIEW_CLASS = YourView to use this method.`);const l=new n({model:o});return Modal.show(l,a)}static async showModelById(a,e,n={}){const l=new a({id:e});return await l.fetch(),l.id?Modal.showModel(l,n):(o.Dialog.alert({message:`Could not find ${a.name||"record"} with ID: ${e}`,type:"warning"}),null)}static confirm(a,e,n){return o.Dialog.confirm(a,e,n)}static alert(a,e,n){return o.Dialog.alert(a,e,n)}static prompt(a,e,n){return o.Dialog.prompt(a,e,n)}static form(a){return o.Dialog.showForm(a)}static modelForm(a){return o.Dialog.showModelForm(a)}static data(a){return o.Dialog.showData(a)}static dialog(a){return o.Dialog.showDialog(a)}static showError(a){return o.Dialog.alert(a,"Error",{type:"danger"})}static _loadingEl=null;static _loadingCounter=0;static _loadingTimeout=null;static loading(o){"string"==typeof o&&(o={message:o});const{message:a="Loading...",timeout:e=3e4}=o||{};if(Modal._loadingCounter++,1===Modal._loadingCounter){Modal._loadingTimeout&&clearTimeout(Modal._loadingTimeout),Modal._loadingEl||(Modal._loadingEl=document.createElement("div"),Modal._loadingEl.className="mojo-loading-overlay",Modal._loadingEl.innerHTML=`\n <div class="mojo-loading-card">\n <div class="mojo-loading-spinner"></div>\n <div class="mojo-loading-message">${a}</div>\n </div>\n <style>\n .mojo-loading-overlay {\n position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;\n background: rgba(255, 255, 255, 0.4);\n backdrop-filter: blur(2px);\n -webkit-backdrop-filter: blur(2px);\n z-index: 99999;\n display: flex; align-items: center; justify-content: center;\n opacity: 0; transition: opacity 0.2s ease;\n }\n .mojo-loading-overlay.show { opacity: 1; }\n .mojo-loading-card {\n display: flex; align-items: center; gap: 0.85rem;\n background: #fff;\n border: 1px solid #e9ecef;\n border-radius: 12px;\n padding: 1rem 1.5rem;\n box-shadow: 0 4px 24px rgba(0,0,0,0.08), 0 1px 4px rgba(0,0,0,0.04);\n }\n .mojo-loading-spinner {\n width: 22px; height: 22px;\n border: 2.5px solid #e9ecef;\n border-top-color: #0d6efd;\n border-radius: 50%;\n animation: mojo-spin 0.7s linear infinite;\n flex-shrink: 0;\n }\n .mojo-loading-message {\n font-size: 0.88rem;\n font-weight: 500;\n color: #495057;\n white-space: nowrap;\n }\n @keyframes mojo-spin {\n to { transform: rotate(360deg); }\n }\n </style>\n `,document.body.appendChild(Modal._loadingEl));const o=Modal._loadingEl.querySelector(".mojo-loading-message");o&&(o.textContent=a),requestAnimationFrame(()=>{Modal._loadingEl&&Modal._loadingEl.classList.add("show")}),e>0&&(Modal._loadingTimeout=setTimeout(()=>{console.error("Modal.loading timed out."),Modal.hideLoading(!0)},e))}else if(Modal._loadingEl){const o=Modal._loadingEl.querySelector(".mojo-loading-message");o&&(o.textContent=a)}}static hideLoading(o){o?Modal._loadingCounter=0:Modal._loadingCounter--,Modal._loadingCounter<=0&&(Modal._loadingCounter=0,Modal._loadingTimeout&&(clearTimeout(Modal._loadingTimeout),Modal._loadingTimeout=null),Modal._loadingEl&&(Modal._loadingEl.classList.remove("show"),setTimeout(()=>{Modal._loadingEl&&0===Modal._loadingCounter&&(Modal._loadingEl.remove(),Modal._loadingEl=null)},200)))}static showBusy(o){return Modal.loading(o)}static hideBusy(o){return Modal.hideLoading(o)}}exports.default=Modal;
2
- //# sourceMappingURL=Modal-AFggmQ5t.js.map
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./Dialog-2gXM2UcO.js");class Modal{static async show(a,e={}){return o.Dialog.showDialog({header:void 0!==e.title&&!!e.title,title:e.title||void 0,body:a,size:"lg",centered:!1,buttons:[{text:"Close",class:"btn-secondary",dismiss:!0}],...e})}static async showModel(o,a={}){const e=o.constructor,n=e?.VIEW_CLASS;if(!n)throw new Error(`Modal.showModel: No VIEW_CLASS defined on ${e?.name||"model"}. Set ${e?.name||"Model"}.VIEW_CLASS = YourView to use this method.`);const l=new n({model:o});return Modal.show(l,a)}static async showModelById(a,e,n={}){const l=new a({id:e});return await l.fetch(),l.id?Modal.showModel(l,n):(o.Dialog.alert({message:`Could not find ${a.name||"record"} with ID: ${e}`,type:"warning"}),null)}static confirm(a,e,n){return o.Dialog.confirm(a,e,n)}static alert(a,e,n){return o.Dialog.alert(a,e,n)}static prompt(a,e,n){return o.Dialog.prompt(a,e,n)}static form(a){return o.Dialog.showForm(a)}static modelForm(a){return o.Dialog.showModelForm(a)}static data(a){return o.Dialog.showData(a)}static dialog(a){return o.Dialog.showDialog(a)}static showError(a){return o.Dialog.alert(a,"Error",{type:"danger"})}static _loadingEl=null;static _loadingCounter=0;static _loadingTimeout=null;static loading(o){"string"==typeof o&&(o={message:o});const{message:a="Loading...",timeout:e=3e4}=o||{};if(Modal._loadingCounter++,1===Modal._loadingCounter){Modal._loadingTimeout&&clearTimeout(Modal._loadingTimeout),Modal._loadingEl||(Modal._loadingEl=document.createElement("div"),Modal._loadingEl.className="mojo-loading-overlay",Modal._loadingEl.innerHTML=`\n <div class="mojo-loading-card">\n <div class="mojo-loading-spinner"></div>\n <div class="mojo-loading-message">${a}</div>\n </div>\n <style>\n .mojo-loading-overlay {\n position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;\n background: rgba(255, 255, 255, 0.4);\n backdrop-filter: blur(2px);\n -webkit-backdrop-filter: blur(2px);\n z-index: 99999;\n display: flex; align-items: center; justify-content: center;\n opacity: 0; transition: opacity 0.2s ease;\n }\n .mojo-loading-overlay.show { opacity: 1; }\n .mojo-loading-card {\n display: flex; align-items: center; gap: 0.85rem;\n background: #fff;\n border: 1px solid #e9ecef;\n border-radius: 12px;\n padding: 1rem 1.5rem;\n box-shadow: 0 4px 24px rgba(0,0,0,0.08), 0 1px 4px rgba(0,0,0,0.04);\n }\n .mojo-loading-spinner {\n width: 22px; height: 22px;\n border: 2.5px solid #e9ecef;\n border-top-color: #0d6efd;\n border-radius: 50%;\n animation: mojo-spin 0.7s linear infinite;\n flex-shrink: 0;\n }\n .mojo-loading-message {\n font-size: 0.88rem;\n font-weight: 500;\n color: #495057;\n white-space: nowrap;\n }\n @keyframes mojo-spin {\n to { transform: rotate(360deg); }\n }\n </style>\n `,document.body.appendChild(Modal._loadingEl));const o=Modal._loadingEl.querySelector(".mojo-loading-message");o&&(o.textContent=a),requestAnimationFrame(()=>{Modal._loadingEl&&Modal._loadingEl.classList.add("show")}),e>0&&(Modal._loadingTimeout=setTimeout(()=>{console.error("Modal.loading timed out."),Modal.hideLoading(!0)},e))}else if(Modal._loadingEl){const o=Modal._loadingEl.querySelector(".mojo-loading-message");o&&(o.textContent=a)}}static hideLoading(o){o?Modal._loadingCounter=0:Modal._loadingCounter--,Modal._loadingCounter<=0&&(Modal._loadingCounter=0,Modal._loadingTimeout&&(clearTimeout(Modal._loadingTimeout),Modal._loadingTimeout=null),Modal._loadingEl&&(Modal._loadingEl.classList.remove("show"),setTimeout(()=>{Modal._loadingEl&&0===Modal._loadingCounter&&(Modal._loadingEl.remove(),Modal._loadingEl=null)},200)))}static showBusy(o){return Modal.loading(o)}static hideBusy(o){return Modal.hideLoading(o)}}exports.default=Modal;
2
+ //# sourceMappingURL=Modal-Y1PW_Fmf.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Modal-AFggmQ5t.js","sources":["../../src/core/views/feedback/Modal.js"],"sourcesContent":["/**\n * Modal — AI-friendly static API for showing views in modal dialogs.\n *\n * Thin wrapper around Dialog's static helpers. All methods are static —\n * there is no instance API. Use Modal instead of `new Dialog()`.\n *\n * @example\n * import Modal from '@core/views/feedback/Modal.js';\n *\n * // Show any View in a modal\n * await Modal.show(new MyView({ model }));\n *\n * // Show a model's VIEW_CLASS automatically\n * await Modal.showModel(userModel);\n *\n * // Fetch a model by ID, then show its VIEW_CLASS\n * await Modal.showModelById(User, 42);\n *\n * // Convenience — no need to import Dialog separately\n * const yes = await Modal.confirm('Delete this?', 'Confirm');\n * await Modal.alert('Done!');\n * const data = await Modal.form({ title: 'Add', fields: [...] });\n * await Modal.modelForm({ model, formConfig: MyForms.edit });\n */\nimport Dialog from './Dialog.js';\n\nclass Modal {\n\n /**\n * Show a View instance in a modal dialog.\n *\n * @param {View} view - The view to display (rendered automatically)\n * @param {object} [options] - Dialog options (size, title, buttons, etc.)\n * @param {string} [options.size='lg'] - Dialog size: sm, md, lg, xl, xxl, fullscreen\n * @param {string|false} [options.title=false] - Dialog title (false = no header)\n * @param {Array} [options.buttons] - Footer buttons\n * @returns {Promise<*>} Resolves with button value or null if dismissed\n *\n * @example\n * await Modal.show(new DeviceView({ model: device }));\n * await Modal.show(new GroupView({ model }), { size: 'xl' });\n */\n static async show(view, options = {}) {\n return Dialog.showDialog({\n header: options.title !== undefined ? !!options.title : false,\n title: options.title || undefined,\n body: view,\n size: 'lg',\n centered: false,\n buttons: [{ text: 'Close', class: 'btn-secondary', dismiss: true }],\n ...options\n });\n }\n\n /**\n * Show a model's VIEW_CLASS in a modal dialog.\n * Looks up `model.constructor.VIEW_CLASS` and instantiates it.\n *\n * @param {Model} model - Model instance (must have VIEW_CLASS on its constructor)\n * @param {object} [options] - Dialog options passed to Modal.show()\n * @returns {Promise<*>} Resolves with button value or null if dismissed\n * @throws {Error} If no VIEW_CLASS is defined on the model's constructor\n *\n * @example\n * await Modal.showModel(userModel);\n * await Modal.showModel(groupModel, { size: 'xl' });\n */\n static async showModel(model, options = {}) {\n const ModelClass = model.constructor;\n const ViewClass = ModelClass?.VIEW_CLASS;\n\n if (!ViewClass) {\n throw new Error(\n `Modal.showModel: No VIEW_CLASS defined on ${ModelClass?.name || 'model'}. ` +\n `Set ${ModelClass?.name || 'Model'}.VIEW_CLASS = YourView to use this method.`\n );\n }\n\n const view = new ViewClass({ model });\n return Modal.show(view, options);\n }\n\n /**\n * Fetch a model by ID, then show its VIEW_CLASS in a modal.\n * Handles the common fetch-then-display pattern.\n *\n * @param {Function} ModelClass - The Model class constructor (e.g., User, Group)\n * @param {string|number} id - The model ID to fetch\n * @param {object} [options] - Dialog options passed to Modal.showModel()\n * @returns {Promise<*>} Resolves with button value, or null if model not found\n *\n * @example\n * await Modal.showModelById(User, 42);\n * await Modal.showModelById(Group, parentId, { size: 'xl' });\n */\n static async showModelById(ModelClass, id, options = {}) {\n const model = new ModelClass({ id });\n await model.fetch();\n\n if (!model.id) {\n Dialog.alert({\n message: `Could not find ${ModelClass.name || 'record'} with ID: ${id}`,\n type: 'warning'\n });\n return null;\n }\n\n return Modal.showModel(model, options);\n }\n\n // ── Convenience aliases ─────────────────────────\n // Re-exported so you never need to import Dialog separately.\n\n /** @see Dialog.confirm */\n static confirm(message, title, options) {\n return Dialog.confirm(message, title, options);\n }\n\n /** @see Dialog.alert */\n static alert(message, title, options) {\n return Dialog.alert(message, title, options);\n }\n\n /** @see Dialog.prompt */\n static prompt(message, title, options) {\n return Dialog.prompt(message, title, options);\n }\n\n /** @see Dialog.showForm */\n static form(options) {\n return Dialog.showForm(options);\n }\n\n /** @see Dialog.showModelForm */\n static modelForm(options) {\n return Dialog.showModelForm(options);\n }\n\n /** @see Dialog.showData */\n static data(options) {\n return Dialog.showData(options);\n }\n\n /** @see Dialog.showDialog — generic dialog with full options control */\n static dialog(options) {\n return Dialog.showDialog(options);\n }\n\n /** @see Dialog.showError */\n static showError(message) {\n return Dialog.alert(message, 'Error', { type: 'danger' });\n }\n\n // ── Loading indicator ───────────────────────\n // Full-screen overlay with spinner for blocking operations.\n // Supports nested calls (counter-based) — call hide for each show.\n // Modern frosted-glass card design with configurable message.\n\n static _loadingEl = null;\n static _loadingCounter = 0;\n static _loadingTimeout = null;\n\n /**\n * Show full-screen loading overlay.\n * @param {string|object} [options] - Message string or { message, timeout }\n * @param {string} [options.message='Loading...'] - Message to display\n * @param {number} [options.timeout=30000] - Auto-hide timeout in ms (0 = no timeout)\n * @example\n * Modal.loading('Saving...');\n * await someApiCall();\n * Modal.hideLoading();\n *\n * // With timeout\n * Modal.loading({ message: 'Processing...', timeout: 60000 });\n */\n static loading(options) {\n if (typeof options === 'string') options = { message: options };\n const { message = 'Loading...', timeout = 30000 } = options || {};\n\n Modal._loadingCounter++;\n\n if (Modal._loadingCounter === 1) {\n if (Modal._loadingTimeout) {\n clearTimeout(Modal._loadingTimeout);\n }\n\n if (!Modal._loadingEl) {\n Modal._loadingEl = document.createElement('div');\n Modal._loadingEl.className = 'mojo-loading-overlay';\n Modal._loadingEl.innerHTML = `\n <div class=\"mojo-loading-card\">\n <div class=\"mojo-loading-spinner\"></div>\n <div class=\"mojo-loading-message\">${message}</div>\n </div>\n <style>\n .mojo-loading-overlay {\n position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;\n background: rgba(255, 255, 255, 0.4);\n backdrop-filter: blur(2px);\n -webkit-backdrop-filter: blur(2px);\n z-index: 99999;\n display: flex; align-items: center; justify-content: center;\n opacity: 0; transition: opacity 0.2s ease;\n }\n .mojo-loading-overlay.show { opacity: 1; }\n .mojo-loading-card {\n display: flex; align-items: center; gap: 0.85rem;\n background: #fff;\n border: 1px solid #e9ecef;\n border-radius: 12px;\n padding: 1rem 1.5rem;\n box-shadow: 0 4px 24px rgba(0,0,0,0.08), 0 1px 4px rgba(0,0,0,0.04);\n }\n .mojo-loading-spinner {\n width: 22px; height: 22px;\n border: 2.5px solid #e9ecef;\n border-top-color: #0d6efd;\n border-radius: 50%;\n animation: mojo-spin 0.7s linear infinite;\n flex-shrink: 0;\n }\n .mojo-loading-message {\n font-size: 0.88rem;\n font-weight: 500;\n color: #495057;\n white-space: nowrap;\n }\n @keyframes mojo-spin {\n to { transform: rotate(360deg); }\n }\n </style>\n `;\n document.body.appendChild(Modal._loadingEl);\n }\n\n // Update message\n const msgEl = Modal._loadingEl.querySelector('.mojo-loading-message');\n if (msgEl) msgEl.textContent = message;\n\n // Trigger fade-in\n requestAnimationFrame(() => {\n if (Modal._loadingEl) Modal._loadingEl.classList.add('show');\n });\n\n // Auto-timeout\n if (timeout > 0) {\n Modal._loadingTimeout = setTimeout(() => {\n console.error('Modal.loading timed out.');\n Modal.hideLoading(true);\n }, timeout);\n }\n } else {\n // Counter > 1: just update the message\n if (Modal._loadingEl) {\n const msgEl = Modal._loadingEl.querySelector('.mojo-loading-message');\n if (msgEl) msgEl.textContent = message;\n }\n }\n }\n\n /**\n * Hide the loading overlay.\n * @param {boolean} [force=false] - Force-hide regardless of counter\n * @example\n * Modal.hideLoading(); // decrement counter\n * Modal.hideLoading(true); // force-hide immediately\n */\n static hideLoading(force) {\n if (force) {\n Modal._loadingCounter = 0;\n } else {\n Modal._loadingCounter--;\n }\n\n if (Modal._loadingCounter <= 0) {\n Modal._loadingCounter = 0;\n\n if (Modal._loadingTimeout) {\n clearTimeout(Modal._loadingTimeout);\n Modal._loadingTimeout = null;\n }\n\n if (Modal._loadingEl) {\n Modal._loadingEl.classList.remove('show');\n setTimeout(() => {\n if (Modal._loadingEl && Modal._loadingCounter === 0) {\n Modal._loadingEl.remove();\n Modal._loadingEl = null;\n }\n }, 200);\n }\n }\n }\n\n /** @alias loading — backward compat with Dialog.showBusy */\n static showBusy(options) { return Modal.loading(options); }\n /** @alias hideLoading — backward compat with Dialog.hideBusy */\n static hideBusy(force) { return Modal.hideLoading(force); }\n}\n\nexport default Modal;\n"],"names":["Modal","show","view","options","Dialog","showDialog","header","title","body","size","centered","buttons","text","class","dismiss","showModel","model","ModelClass","constructor","ViewClass","VIEW_CLASS","Error","name","showModelById","id","fetch","alert","message","type","confirm","prompt","form","showForm","modelForm","showModelForm","data","showData","dialog","showError","static","loading","timeout","_loadingCounter","_loadingTimeout","clearTimeout","_loadingEl","document","createElement","className","innerHTML","appendChild","msgEl","querySelector","textContent","requestAnimationFrame","classList","add","setTimeout","console","error","hideLoading","force","remove","showBusy","hideBusy"],"mappings":"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"}
1
+ {"version":3,"file":"Modal-Y1PW_Fmf.js","sources":["../../src/core/views/feedback/Modal.js"],"sourcesContent":["/**\n * Modal — AI-friendly static API for showing views in modal dialogs.\n *\n * Thin wrapper around Dialog's static helpers. All methods are static —\n * there is no instance API. Use Modal instead of `new Dialog()`.\n *\n * @example\n * import Modal from '@core/views/feedback/Modal.js';\n *\n * // Show any View in a modal\n * await Modal.show(new MyView({ model }));\n *\n * // Show a model's VIEW_CLASS automatically\n * await Modal.showModel(userModel);\n *\n * // Fetch a model by ID, then show its VIEW_CLASS\n * await Modal.showModelById(User, 42);\n *\n * // Convenience — no need to import Dialog separately\n * const yes = await Modal.confirm('Delete this?', 'Confirm');\n * await Modal.alert('Done!');\n * const data = await Modal.form({ title: 'Add', fields: [...] });\n * await Modal.modelForm({ model, formConfig: MyForms.edit });\n */\nimport Dialog from './Dialog.js';\n\nclass Modal {\n\n /**\n * Show a View instance in a modal dialog.\n *\n * @param {View} view - The view to display (rendered automatically)\n * @param {object} [options] - Dialog options (size, title, buttons, etc.)\n * @param {string} [options.size='lg'] - Dialog size: sm, md, lg, xl, xxl, fullscreen\n * @param {string|false} [options.title=false] - Dialog title (false = no header)\n * @param {Array} [options.buttons] - Footer buttons\n * @returns {Promise<*>} Resolves with button value or null if dismissed\n *\n * @example\n * await Modal.show(new DeviceView({ model: device }));\n * await Modal.show(new GroupView({ model }), { size: 'xl' });\n */\n static async show(view, options = {}) {\n return Dialog.showDialog({\n header: options.title !== undefined ? !!options.title : false,\n title: options.title || undefined,\n body: view,\n size: 'lg',\n centered: false,\n buttons: [{ text: 'Close', class: 'btn-secondary', dismiss: true }],\n ...options\n });\n }\n\n /**\n * Show a model's VIEW_CLASS in a modal dialog.\n * Looks up `model.constructor.VIEW_CLASS` and instantiates it.\n *\n * @param {Model} model - Model instance (must have VIEW_CLASS on its constructor)\n * @param {object} [options] - Dialog options passed to Modal.show()\n * @returns {Promise<*>} Resolves with button value or null if dismissed\n * @throws {Error} If no VIEW_CLASS is defined on the model's constructor\n *\n * @example\n * await Modal.showModel(userModel);\n * await Modal.showModel(groupModel, { size: 'xl' });\n */\n static async showModel(model, options = {}) {\n const ModelClass = model.constructor;\n const ViewClass = ModelClass?.VIEW_CLASS;\n\n if (!ViewClass) {\n throw new Error(\n `Modal.showModel: No VIEW_CLASS defined on ${ModelClass?.name || 'model'}. ` +\n `Set ${ModelClass?.name || 'Model'}.VIEW_CLASS = YourView to use this method.`\n );\n }\n\n const view = new ViewClass({ model });\n return Modal.show(view, options);\n }\n\n /**\n * Fetch a model by ID, then show its VIEW_CLASS in a modal.\n * Handles the common fetch-then-display pattern.\n *\n * @param {Function} ModelClass - The Model class constructor (e.g., User, Group)\n * @param {string|number} id - The model ID to fetch\n * @param {object} [options] - Dialog options passed to Modal.showModel()\n * @returns {Promise<*>} Resolves with button value, or null if model not found\n *\n * @example\n * await Modal.showModelById(User, 42);\n * await Modal.showModelById(Group, parentId, { size: 'xl' });\n */\n static async showModelById(ModelClass, id, options = {}) {\n const model = new ModelClass({ id });\n await model.fetch();\n\n if (!model.id) {\n Dialog.alert({\n message: `Could not find ${ModelClass.name || 'record'} with ID: ${id}`,\n type: 'warning'\n });\n return null;\n }\n\n return Modal.showModel(model, options);\n }\n\n // ── Convenience aliases ─────────────────────────\n // Re-exported so you never need to import Dialog separately.\n\n /** @see Dialog.confirm */\n static confirm(message, title, options) {\n return Dialog.confirm(message, title, options);\n }\n\n /** @see Dialog.alert */\n static alert(message, title, options) {\n return Dialog.alert(message, title, options);\n }\n\n /** @see Dialog.prompt */\n static prompt(message, title, options) {\n return Dialog.prompt(message, title, options);\n }\n\n /** @see Dialog.showForm */\n static form(options) {\n return Dialog.showForm(options);\n }\n\n /** @see Dialog.showModelForm */\n static modelForm(options) {\n return Dialog.showModelForm(options);\n }\n\n /** @see Dialog.showData */\n static data(options) {\n return Dialog.showData(options);\n }\n\n /** @see Dialog.showDialog — generic dialog with full options control */\n static dialog(options) {\n return Dialog.showDialog(options);\n }\n\n /** @see Dialog.showError */\n static showError(message) {\n return Dialog.alert(message, 'Error', { type: 'danger' });\n }\n\n // ── Loading indicator ───────────────────────\n // Full-screen overlay with spinner for blocking operations.\n // Supports nested calls (counter-based) — call hide for each show.\n // Modern frosted-glass card design with configurable message.\n\n static _loadingEl = null;\n static _loadingCounter = 0;\n static _loadingTimeout = null;\n\n /**\n * Show full-screen loading overlay.\n * @param {string|object} [options] - Message string or { message, timeout }\n * @param {string} [options.message='Loading...'] - Message to display\n * @param {number} [options.timeout=30000] - Auto-hide timeout in ms (0 = no timeout)\n * @example\n * Modal.loading('Saving...');\n * await someApiCall();\n * Modal.hideLoading();\n *\n * // With timeout\n * Modal.loading({ message: 'Processing...', timeout: 60000 });\n */\n static loading(options) {\n if (typeof options === 'string') options = { message: options };\n const { message = 'Loading...', timeout = 30000 } = options || {};\n\n Modal._loadingCounter++;\n\n if (Modal._loadingCounter === 1) {\n if (Modal._loadingTimeout) {\n clearTimeout(Modal._loadingTimeout);\n }\n\n if (!Modal._loadingEl) {\n Modal._loadingEl = document.createElement('div');\n Modal._loadingEl.className = 'mojo-loading-overlay';\n Modal._loadingEl.innerHTML = `\n <div class=\"mojo-loading-card\">\n <div class=\"mojo-loading-spinner\"></div>\n <div class=\"mojo-loading-message\">${message}</div>\n </div>\n <style>\n .mojo-loading-overlay {\n position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;\n background: rgba(255, 255, 255, 0.4);\n backdrop-filter: blur(2px);\n -webkit-backdrop-filter: blur(2px);\n z-index: 99999;\n display: flex; align-items: center; justify-content: center;\n opacity: 0; transition: opacity 0.2s ease;\n }\n .mojo-loading-overlay.show { opacity: 1; }\n .mojo-loading-card {\n display: flex; align-items: center; gap: 0.85rem;\n background: #fff;\n border: 1px solid #e9ecef;\n border-radius: 12px;\n padding: 1rem 1.5rem;\n box-shadow: 0 4px 24px rgba(0,0,0,0.08), 0 1px 4px rgba(0,0,0,0.04);\n }\n .mojo-loading-spinner {\n width: 22px; height: 22px;\n border: 2.5px solid #e9ecef;\n border-top-color: #0d6efd;\n border-radius: 50%;\n animation: mojo-spin 0.7s linear infinite;\n flex-shrink: 0;\n }\n .mojo-loading-message {\n font-size: 0.88rem;\n font-weight: 500;\n color: #495057;\n white-space: nowrap;\n }\n @keyframes mojo-spin {\n to { transform: rotate(360deg); }\n }\n </style>\n `;\n document.body.appendChild(Modal._loadingEl);\n }\n\n // Update message\n const msgEl = Modal._loadingEl.querySelector('.mojo-loading-message');\n if (msgEl) msgEl.textContent = message;\n\n // Trigger fade-in\n requestAnimationFrame(() => {\n if (Modal._loadingEl) Modal._loadingEl.classList.add('show');\n });\n\n // Auto-timeout\n if (timeout > 0) {\n Modal._loadingTimeout = setTimeout(() => {\n console.error('Modal.loading timed out.');\n Modal.hideLoading(true);\n }, timeout);\n }\n } else {\n // Counter > 1: just update the message\n if (Modal._loadingEl) {\n const msgEl = Modal._loadingEl.querySelector('.mojo-loading-message');\n if (msgEl) msgEl.textContent = message;\n }\n }\n }\n\n /**\n * Hide the loading overlay.\n * @param {boolean} [force=false] - Force-hide regardless of counter\n * @example\n * Modal.hideLoading(); // decrement counter\n * Modal.hideLoading(true); // force-hide immediately\n */\n static hideLoading(force) {\n if (force) {\n Modal._loadingCounter = 0;\n } else {\n Modal._loadingCounter--;\n }\n\n if (Modal._loadingCounter <= 0) {\n Modal._loadingCounter = 0;\n\n if (Modal._loadingTimeout) {\n clearTimeout(Modal._loadingTimeout);\n Modal._loadingTimeout = null;\n }\n\n if (Modal._loadingEl) {\n Modal._loadingEl.classList.remove('show');\n setTimeout(() => {\n if (Modal._loadingEl && Modal._loadingCounter === 0) {\n Modal._loadingEl.remove();\n Modal._loadingEl = null;\n }\n }, 200);\n }\n }\n }\n\n /** @alias loading — backward compat with Dialog.showBusy */\n static showBusy(options) { return Modal.loading(options); }\n /** @alias hideLoading — backward compat with Dialog.hideBusy */\n static hideBusy(force) { return Modal.hideLoading(force); }\n}\n\nexport default Modal;\n"],"names":["Modal","show","view","options","Dialog","showDialog","header","title","body","size","centered","buttons","text","class","dismiss","showModel","model","ModelClass","constructor","ViewClass","VIEW_CLASS","Error","name","showModelById","id","fetch","alert","message","type","confirm","prompt","form","showForm","modelForm","showModelForm","data","showData","dialog","showError","static","loading","timeout","_loadingCounter","_loadingTimeout","clearTimeout","_loadingEl","document","createElement","className","innerHTML","appendChild","msgEl","querySelector","textContent","requestAnimationFrame","classList","add","setTimeout","console","error","hideLoading","force","remove","showBusy","hideBusy"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";const t=require("./Collection-Bgp386gn.js"),e=require("./Dialog-DB9A3lyf.js");class LightboxGallery extends t.View{constructor(t={}){super({...t,className:`lightbox-gallery ${t.className||""}`,tagName:"div"});const e=Array.isArray(t.images)?t.images:[t.images||t.src].filter(Boolean);this.images=e.map(t=>"string"==typeof t?{src:t,alt:""}:{src:t.src,alt:t.alt||""}),this.currentIndex=t.startIndex||0,this.showNavigation=!1!==t.showNavigation&&this.images.length>1,this.showCounter=!1!==t.showCounter&&this.images.length>1,this.allowKeyboard=!1!==t.allowKeyboard,this.closeOnBackdrop=!1!==t.closeOnBackdrop,this.fitToScreen=!1!==t.fitToScreen,this._keyboardHandler=this.handleKeyboard.bind(this),this.updateTemplateProperties()}updateTemplateProperties(){this.currentImage=this.images[this.currentIndex]||{src:"",alt:""},this.currentNumber=this.currentIndex+1,this.total=this.images.length,this.isFirst=0===this.currentIndex,this.isLast=this.currentIndex===this.images.length-1,this.imageStyle=this.fitToScreen?"width: 90vw; max-height: 100%; object-fit: contain; user-select: none; cursor: zoom-in;":"max-width: none; max-height: none; user-select: none; cursor: zoom-out;",this.containerStyle=this.fitToScreen?"":"overflow: auto;",this.modeIndicator=this.fitToScreen?"Fit to Screen":"Original Size"}async getTemplate(){return this.images[this.currentIndex],this.images.length,'\n <div class="lightbox-overlay position-fixed top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-center"\n style="background: rgba(0,0,0,0.9); z-index: 9999;"\n data-action="backdrop-click">\n\n \x3c!-- Close button --\x3e\n <button type="button" class="btn-close btn-close-white position-absolute top-0 end-0 m-4"\n data-action="close"\n style="z-index: 10001;"\n title="Close"></button>\n\n \x3c!-- Counter --\x3e\n {{#showCounter}}\n <div class="lightbox-counter position-absolute top-0 start-50 translate-middle-x mt-4 text-white"\n style="z-index: 10001; font-size: 1.1rem;">\n {{currentNumber}} of {{total}}\n </div>\n {{/showCounter}}\n\n \x3c!-- Mode Indicator --\x3e\n <div class="lightbox-mode-indicator position-absolute bottom-0 start-50 translate-middle-x mb-4 text-white bg-dark bg-opacity-75 px-3 py-2 rounded"\n style="z-index: 10001; font-size: 0.9rem;">\n {{modeIndicator}} • Click image to toggle\n </div>\n\n \x3c!-- Navigation --\x3e\n {{#showNavigation}}\n <button type="button" class="btn btn-light btn-lg position-absolute start-0 top-50 translate-middle-y ms-4"\n data-action="prev"\n style="z-index: 10001;"\n title="Previous"\n {{#isFirst}}disabled{{/isFirst}}>\n <i class="bi bi-chevron-left"></i>\n </button>\n\n <button type="button" class="btn btn-light btn-lg position-absolute end-0 top-50 translate-middle-y me-4"\n data-action="next"\n style="z-index: 10001;"\n title="Next"\n {{#isLast}}disabled{{/isLast}}>\n <i class="bi bi-chevron-right"></i>\n </button>\n {{/showNavigation}}\n\n \x3c!-- Image container --\x3e\n <div class="lightbox-image-container w-100 h-100 d-flex align-items-center justify-content-center p-5"\n style="{{containerStyle}}">\n {{#currentImage}}\n <img src="{{src}}"\n alt="{{alt}}"\n class="lightbox-image img-fluid"\n style="{{imageStyle}}"\n data-action="image-click">\n {{/currentImage}}\n </div>\n\n \x3c!-- Loading spinner --\x3e\n <div class="lightbox-loading position-absolute top-50 start-50 translate-middle text-white"\n style="display: none;">\n <div class="spinner-border" role="status">\n <span class="visually-hidden">Loading...</span>\n </div>\n </div>\n </div>\n '}async onAfterRender(){document.body.appendChild(this.element),document.body.style.overflow="hidden",this.allowKeyboard&&document.addEventListener("keydown",this._keyboardHandler),this.preloadAdjacentImages()}async handleActionClose(){this.close()}async handleActionBackdropClick(t){this.closeOnBackdrop&&t.target===t.currentTarget&&this.close()}async handleActionPrev(){this.showPrevious()}async handleActionNext(){this.showNext()}async handleActionImageClick(){this.toggleImageMode()}showPrevious(){this.currentIndex>0&&(this.currentIndex--,this.updateImage(),this.preloadAdjacentImages())}showNext(){this.currentIndex<this.images.length-1&&(this.currentIndex++,this.updateImage(),this.preloadAdjacentImages())}goToImage(t){t>=0&&t<this.images.length&&t!==this.currentIndex&&(this.currentIndex=t,this.updateImage(),this.preloadAdjacentImages())}async updateImage(){const t=this.images[this.currentIndex],e=this.element.querySelector(".lightbox-image"),s=this.element.querySelector(".lightbox-counter"),i=this.element.querySelector('[data-action="prev"]'),n=this.element.querySelector('[data-action="next"]');e&&(this.showLoading(),e.src=t.src,e.alt=t.alt,await this.waitForImageLoad(e),this.hideLoading()),s&&(s.textContent=`${this.currentIndex+1} of ${this.images.length}`),i&&(i.disabled=0===this.currentIndex),n&&(n.disabled=this.currentIndex===this.images.length-1),this.updateTemplateProperties(),this.updateImageDisplay();const a=this.getApp()?.events;a&&a.emit("lightbox:image-changed",{gallery:this,index:this.currentIndex,image:t})}showLoading(){const t=this.element.querySelector(".lightbox-loading");t&&(t.style.display="block")}hideLoading(){const t=this.element.querySelector(".lightbox-loading");t&&(t.style.display="none")}waitForImageLoad(t){return new Promise(e=>{t.complete?e():(t.onload=e,t.onerror=e)})}preloadAdjacentImages(){const t=[];this.currentIndex>0&&t.push(this.currentIndex-1),this.currentIndex<this.images.length-1&&t.push(this.currentIndex+1),t.forEach(t=>{const e=this.images[t];(new Image).src=e.src})}handleKeyboard(t){switch(t.key){case"Escape":t.preventDefault(),this.close();break;case"ArrowLeft":t.preventDefault(),this.showPrevious();break;case"ArrowRight":t.preventDefault(),this.showNext();break;case"Home":t.preventDefault(),this.goToImage(0);break;case"End":t.preventDefault(),this.goToImage(this.images.length-1)}}toggleImageMode(){this.fitToScreen=!this.fitToScreen,this.updateTemplateProperties(),this.updateImageDisplay();const t=this.getApp()?.events;t&&t.emit("lightbox:mode-changed",{gallery:this,fitToScreen:this.fitToScreen})}updateImageDisplay(){const t=this.element.querySelector(".lightbox-image"),e=this.element.querySelector(".lightbox-image-container"),s=this.element.querySelector(".lightbox-mode-indicator");t&&(this.fitToScreen?(t.style.maxWidth="100%",t.style.maxHeight="100%",t.style.objectFit="contain",t.style.cursor="zoom-in"):(t.style.maxWidth="none",t.style.maxHeight="none",t.style.objectFit="none",t.style.cursor="zoom-out"),t.style.userSelect="none"),e&&(e.style.overflow=this.fitToScreen?"":"auto"),s&&(s.textContent=`${this.modeIndicator} • Click image to toggle`)}close(){const t=this.getApp()?.events;t&&t.emit("lightbox:closed",{gallery:this}),this.destroy()}async onBeforeDestroy(){document.body.style.overflow="",this.allowKeyboard&&document.removeEventListener("keydown",this._keyboardHandler),this.element.parentNode===document.body&&document.body.removeChild(this.element)}static show(t,e={}){const s=new LightboxGallery({images:t,...e});return s.render().then(()=>{s.mount()}),s}}window.LightboxGallery=LightboxGallery;class PDFViewer extends t.View{constructor(t={}){super({...t,className:`pdf-viewer ${t.className||""}`,tagName:"div"}),this.pdfUrl=t.pdfUrl||t.src||"",this.title=t.title||"PDF Document",this.pdfDoc=null,this.currentPage=1,this.totalPages=0,this.pageRendering=!1,this.pageNumPending=null,this.scale=1,this.minScale=.25,this.maxScale=5,this.scaleStep=.25,this.fitMode="page",this.canvas=null,this.ctx=null,this.showControls=!1!==t.showControls,this.allowZoom=!1!==t.allowZoom,this.allowNavigation=!1!==t.allowNavigation,this.showPageNumbers=!1!==t.showPageNumbers,this.isLoaded=!1,this.isLoading=!1,this.pdfjsWorkerPath=t.pdfjsWorkerPath||"https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.0.379/pdf.worker.min.js",this.pdfjsCMapUrl=t.pdfjsCMapUrl||"https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.0.379/cmaps/",this.canvasContainer=null,this.controlsElement=null,this.statusElement=null,this.pageInput=null}async getTemplate(){return'\n <div class="pdf-viewer-container">\n {{#showControls}}\n <div class="pdf-viewer-toolbar" data-container="toolbar">\n <div class="btn-toolbar" role="toolbar">\n \x3c!-- Navigation Controls --\x3e\n {{#allowNavigation}}\n <div class="btn-group me-2" role="group" aria-label="Navigation">\n <button type="button" class="btn btn-outline-secondary btn-sm" data-action="first-page" title="First Page">\n <i class="bi bi-chevron-double-left"></i>\n </button>\n <button type="button" class="btn btn-outline-secondary btn-sm" data-action="prev-page" title="Previous Page">\n <i class="bi bi-chevron-left"></i>\n </button>\n \n {{#showPageNumbers}}\n <div class="input-group input-group-sm" style="width: 120px;">\n <input type="number" class="form-control text-center page-input" min="1" value="1" data-change-action="page-input">\n <span class="input-group-text page-total">/ 0</span>\n </div>\n {{/showPageNumbers}}\n \n <button type="button" class="btn btn-outline-secondary btn-sm" data-action="next-page" title="Next Page">\n <i class="bi bi-chevron-right"></i>\n </button>\n <button type="button" class="btn btn-outline-secondary btn-sm" data-action="last-page" title="Last Page">\n <i class="bi bi-chevron-double-right"></i>\n </button>\n </div>\n {{/allowNavigation}}\n\n \x3c!-- Zoom Controls --\x3e\n {{#allowZoom}}\n <div class="btn-group me-2" role="group" aria-label="Zoom">\n <button type="button" class="btn btn-outline-secondary btn-sm" data-action="zoom-out" title="Zoom Out">\n <i class="bi bi-zoom-out"></i>\n </button>\n <button type="button" class="btn btn-outline-secondary btn-sm" data-action="zoom-in" title="Zoom In">\n <i class="bi bi-zoom-in"></i>\n </button>\n \n <div class="btn-group" role="group">\n <button type="button" class="btn btn-outline-secondary btn-sm dropdown-toggle" data-bs-toggle="dropdown" title="Fit">\n <i class="bi bi-arrows-fullscreen"></i>\n </button>\n <ul class="dropdown-menu">\n <li><a class="dropdown-item" href="#" data-action="fit-page">Fit Page</a></li>\n <li><a class="dropdown-item" href="#" data-action="fit-width">Fit Width</a></li>\n <li><a class="dropdown-item" href="#" data-action="actual-size">Actual Size</a></li>\n </ul>\n </div>\n </div>\n {{/allowZoom}}\n\n \x3c!-- Utility Controls --\x3e\n <div class="btn-group me-2" role="group" aria-label="Utilities">\n <button type="button" class="btn btn-outline-secondary btn-sm" data-action="download" title="Download">\n <i class="bi bi-download"></i>\n </button>\n <button type="button" class="btn btn-outline-secondary btn-sm" data-action="print" title="Print">\n <i class="bi bi-printer"></i>\n </button>\n </div>\n </div>\n </div>\n {{/showControls}}\n\n \x3c!-- PDF Content Area --\x3e\n <div class="pdf-viewer-content" data-container="content">\n <div class="pdf-canvas-container" data-container="canvasContainer">\n <canvas class="pdf-canvas" data-container="canvas"></canvas>\n </div>\n\n <div class="pdf-viewer-overlay">\n <div class="pdf-viewer-loading">\n <div class="spinner-border text-primary" role="status">\n <span class="visually-hidden">Loading PDF...</span>\n </div>\n <div class="mt-2">Loading PDF...</div>\n </div>\n </div>\n\n <div class="pdf-viewer-error" style="display: none;">\n <div class="alert alert-danger" role="alert">\n <i class="bi bi-exclamation-triangle"></i>\n <strong>Error:</strong> <span class="error-message">Failed to load PDF</span>\n </div>\n </div>\n </div>\n\n \x3c!-- Status Bar --\x3e\n <div class="pdf-viewer-status" data-container="status">\n <small class="text-muted">\n <span class="current-page">0</span> of <span class="total-pages">0</span> pages |\n <span class="zoom-level">100%</span> |\n <span class="document-title">{{title}}</span>\n </small>\n </div>\n </div>\n '}get(){return{pdfUrl:this.pdfUrl,title:this.title,showControls:this.showControls,allowZoom:this.allowZoom,allowNavigation:this.allowNavigation,showPageNumbers:this.showPageNumbers}}async onAfterRender(){this.canvas=this.element.querySelector(".pdf-canvas"),this.canvasContainer=this.element.querySelector(".pdf-canvas-container"),this.controlsElement=this.element.querySelector(".pdf-viewer-toolbar"),this.statusElement=this.element.querySelector(".pdf-viewer-status"),this.pageInput=this.element.querySelector(".page-input"),this.overlayElement=this.element.querySelector(".pdf-viewer-overlay"),this.errorElement=this.element.querySelector(".pdf-viewer-error"),this.canvas&&(this.ctx=this.canvas.getContext("2d")),this.setupEssentialEventListeners(),await this.initializePDFJS(),this.pdfUrl&&await this.loadPDF()}setupEssentialEventListeners(){const t=t=>this.handleKeyDown(t);document.addEventListener("keydown",t);let e=null;this.canvasContainer&&(e=new ResizeObserver(()=>{"auto"!==this.fitMode&&this.applyFitMode()}),e.observe(this.canvasContainer)),this._essentialListeners=[{el:document,type:"keydown",fn:t}],this._resizeObserver=e}async initializePDFJS(){try{return void 0===window.pdfjsLib&&await this.loadPDFJSLibrary(),window.pdfjsLib.GlobalWorkerOptions.workerSrc=this.pdfjsWorkerPath,!0}catch(t){return console.error("Failed to initialize PDF.js:",t),this.showError("Failed to initialize PDF viewer"),!1}}async loadPDFJSLibrary(){return new Promise((t,e)=>{const s=document.createElement("script");s.src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.0.379/pdf.min.js",s.onload=t,s.onerror=e,document.head.appendChild(s)})}async handleActionFirstPage(){await this.goToPage(1)}async handleActionPrevPage(){await this.goToPage(this.currentPage-1)}async handleActionNextPage(){await this.goToPage(this.currentPage+1)}async handleActionLastPage(){await this.goToPage(this.totalPages)}async onChangePageInput(t,e){const s=parseInt(e.value,10);s>=1&&s<=this.totalPages?await this.goToPage(s):e.value=this.currentPage}async handleActionZoomIn(){this.setScale(this.scale+this.scaleStep)}async handleActionZoomOut(){this.setScale(this.scale-this.scaleStep)}async handleActionFitPage(){this.setFitMode("page")}async handleActionFitWidth(){this.setFitMode("width")}async handleActionActualSize(){this.setScale(1),this.fitMode="auto"}async handleActionDownload(){this.downloadPDF()}async handleActionPrint(){window.print()}async loadPDF(){if(!this.pdfUrl||!window.pdfjsLib)return this.showError("PDF URL or PDF.js library not available"),!1;this.isLoading=!0,this.showLoading();try{const t=window.pdfjsLib.getDocument({url:this.pdfUrl,cMapUrl:this.pdfjsCMapUrl,cMapPacked:!0});this.pdfDoc=await t.promise,this.totalPages=this.pdfDoc.numPages,this.currentPage=1,this.updatePageControls(),this.updateStatus(),await this.renderPage(1),this.isLoaded=!0,this.isLoading=!1,this.element.classList.add("loaded"),this.hideLoading(),this.applyFitMode();const e=this.getApp()?.events;return e&&e.emit("pdfviewer:loaded",{viewer:this,pdfUrl:this.pdfUrl,totalPages:this.totalPages}),!0}catch(t){console.error("Error loading PDF:",t),this.isLoading=!1,this.showError("Failed to load PDF document");const e=this.getApp()?.events;return e&&e.emit("pdfviewer:error",{viewer:this,pdfUrl:this.pdfUrl,error:t.message}),!1}}async renderPage(t){if(this.pageRendering)this.pageNumPending=t;else if(this.pdfDoc&&this.canvas&&this.ctx){this.pageRendering=!0,this.currentPage=t;try{const e=await this.pdfDoc.getPage(t),s=e.getViewport({scale:this.scale});this.canvas.height=s.height,this.canvas.width=s.width,this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height);const i={canvasContext:this.ctx,viewport:s},n=e.render(i);if(await n.promise,this.pageRendering=!1,null!==this.pageNumPending){const t=this.pageNumPending;this.pageNumPending=null,await this.renderPage(t)}this.updatePageControls(),this.updateStatus();const a=this.getApp()?.events;a&&a.emit("pdfviewer:page-changed",{viewer:this,currentPage:this.currentPage,totalPages:this.totalPages})}catch(e){console.error("Error rendering page:",e),this.pageRendering=!1,this.showError("Failed to render PDF page")}}}async goToPage(t){!this.pdfDoc||t<1||t>this.totalPages||await this.renderPage(t)}setScale(t){const e=this.scale;this.scale=Math.max(this.minScale,Math.min(this.maxScale,t)),this.fitMode="auto",this.isLoaded&&this.renderPage(this.currentPage);const s=this.getApp()?.events;s&&e!==this.scale&&s.emit("pdfviewer:scale-changed",{viewer:this,oldScale:e,newScale:this.scale})}setFitMode(t){const e=this.fitMode;this.fitMode=t,this.applyFitMode();const s=this.getApp()?.events;s&&s.emit("pdfviewer:fit-mode-changed",{viewer:this,oldMode:e,newMode:t})}applyFitMode(){this.isLoaded&&this.pdfDoc&&this.canvasContainer&&this.pdfDoc.getPage(this.currentPage).then(t=>{const e=this.canvasContainer.getBoundingClientRect(),s=t.getViewport({scale:1});let i;if("page"===this.fitMode){const t=(e.width-40)/s.width,n=(e.height-40)/s.height;i=Math.min(t,n)}else{if("width"!==this.fitMode)return;i=(e.width-40)/s.width}this.scale=Math.max(this.minScale,Math.min(this.maxScale,i)),this.renderPage(this.currentPage)})}handleKeyDown(t){if("INPUT"!==t.target.tagName||t.target===this.pageInput)switch(t.key){case"ArrowLeft":case"PageUp":t.preventDefault(),this.goToPage(this.currentPage-1);break;case"ArrowRight":case"PageDown":t.preventDefault(),this.goToPage(this.currentPage+1);break;case"Home":t.preventDefault(),this.goToPage(1);break;case"End":t.preventDefault(),this.goToPage(this.totalPages);break;case"+":case"=":(t.ctrlKey||t.metaKey)&&(t.preventDefault(),this.setScale(this.scale+this.scaleStep));break;case"-":(t.ctrlKey||t.metaKey)&&(t.preventDefault(),this.setScale(this.scale-this.scaleStep));break;case"0":(t.ctrlKey||t.metaKey)&&(t.preventDefault(),this.setFitMode("page"))}}updatePageControls(){this.pageInput&&(this.pageInput.value=this.currentPage);const t=this.element.querySelector(".page-total");t&&(t.textContent=`/ ${this.totalPages}`);const e=this.element.querySelector('[data-action="first-page"]'),s=this.element.querySelector('[data-action="prev-page"]'),i=this.element.querySelector('[data-action="next-page"]'),n=this.element.querySelector('[data-action="last-page"]');e&&(e.disabled=this.currentPage<=1),s&&(s.disabled=this.currentPage<=1),i&&(i.disabled=this.currentPage>=this.totalPages),n&&(n.disabled=this.currentPage>=this.totalPages);const a=this.element.querySelector('[data-action="zoom-in"]'),o=this.element.querySelector('[data-action="zoom-out"]');a&&(a.disabled=this.scale>=this.maxScale),o&&(o.disabled=this.scale<=this.minScale)}updateStatus(){if(!this.statusElement)return;const t=this.statusElement.querySelector(".current-page"),e=this.statusElement.querySelector(".total-pages"),s=this.statusElement.querySelector(".zoom-level");t&&(t.textContent=this.currentPage),e&&(e.textContent=this.totalPages),s&&(s.textContent=`${Math.round(100*this.scale)}%`)}showLoading(){this.overlayElement&&(this.overlayElement.style.display="flex")}hideLoading(){this.overlayElement&&(this.overlayElement.style.display="none")}showError(t){if(this.hideLoading(),this.errorElement){const e=this.errorElement.querySelector(".error-message");e&&(e.textContent=t),this.errorElement.style.display="block"}console.error("PDF Viewer Error:",t)}downloadPDF(){if(!this.pdfUrl)return;const t=document.createElement("a");t.href=this.pdfUrl,t.download=this.title+".pdf",t.target="_blank",t.click()}setPDF(t,e=""){const s=this.pdfUrl;this.pdfUrl=t,this.title=e||"PDF Document",this.isLoaded=!1,this.element.classList.remove("loaded"),this.pdfDoc&&(this.pdfDoc.destroy(),this.pdfDoc=null),this.currentPage=1,this.totalPages=0,this.scale=1,t&&this.loadPDF();const i=this.getApp()?.events;i&&i.emit("pdfviewer:pdf-changed",{viewer:this,oldPdfUrl:s,newPdfUrl:t})}getCurrentPage(){return this.currentPage}getTotalPages(){return this.totalPages}getCurrentScale(){return this.scale}async onBeforeDestroy(){this.pdfDoc&&(this.pdfDoc.destroy(),this.pdfDoc=null),this.pageRendering=!1,this.pageNumPending=null,this._essentialListeners&&(this._essentialListeners.forEach(({el:t,type:e,fn:s})=>{t&&t.removeEventListener(e,s)}),this._essentialListeners=null),this._resizeObserver&&(this._resizeObserver.disconnect(),this._resizeObserver=null);const t=this.getApp()?.events;t&&t.emit("pdfviewer:destroyed",{viewer:this})}static async showDialog(t,s={}){const{title:i="PDF Viewer",size:n="fullscreen",showControls:a=!0,allowZoom:o=!0,allowNavigation:r=!0,showPageNumbers:l=!0,...c}=s,d=new PDFViewer({pdfUrl:t,title:i,showControls:a,allowZoom:o,allowNavigation:r,showPageNumbers:l}),h=new e.Dialog({title:i,body:d,size:n,centered:!0,backdrop:"static",keyboard:!0,buttons:[{text:"Download",action:"download",class:"btn btn-outline-primary"},{text:"Close",action:"close",class:"btn btn-secondary",dismiss:!0}],...c});return await h.render(),document.body.appendChild(h.element),await h.mount(),h.show(),new Promise(t=>{h.on("hidden",()=>{h.destroy(),t(d)}),h.on("action:download",()=>{d.downloadPDF()}),h.on("action:close",()=>{h.hide()})})}}exports.LightboxGallery=LightboxGallery,exports.PDFViewer=PDFViewer;
2
- //# sourceMappingURL=PDFViewer-DTWJyqLN.js.map
1
+ "use strict";const t=require("./Collection-Bgp386gn.js"),e=require("./Dialog-2gXM2UcO.js");class LightboxGallery extends t.View{constructor(t={}){super({...t,className:`lightbox-gallery ${t.className||""}`,tagName:"div"});const e=Array.isArray(t.images)?t.images:[t.images||t.src].filter(Boolean);this.images=e.map(t=>"string"==typeof t?{src:t,alt:""}:{src:t.src,alt:t.alt||""}),this.currentIndex=t.startIndex||0,this.showNavigation=!1!==t.showNavigation&&this.images.length>1,this.showCounter=!1!==t.showCounter&&this.images.length>1,this.allowKeyboard=!1!==t.allowKeyboard,this.closeOnBackdrop=!1!==t.closeOnBackdrop,this.fitToScreen=!1!==t.fitToScreen,this._keyboardHandler=this.handleKeyboard.bind(this),this.updateTemplateProperties()}updateTemplateProperties(){this.currentImage=this.images[this.currentIndex]||{src:"",alt:""},this.currentNumber=this.currentIndex+1,this.total=this.images.length,this.isFirst=0===this.currentIndex,this.isLast=this.currentIndex===this.images.length-1,this.imageStyle=this.fitToScreen?"width: 90vw; max-height: 100%; object-fit: contain; user-select: none; cursor: zoom-in;":"max-width: none; max-height: none; user-select: none; cursor: zoom-out;",this.containerStyle=this.fitToScreen?"":"overflow: auto;",this.modeIndicator=this.fitToScreen?"Fit to Screen":"Original Size"}async getTemplate(){return this.images[this.currentIndex],this.images.length,'\n <div class="lightbox-overlay position-fixed top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-center"\n style="background: rgba(0,0,0,0.9); z-index: 9999;"\n data-action="backdrop-click">\n\n \x3c!-- Close button --\x3e\n <button type="button" class="btn-close btn-close-white position-absolute top-0 end-0 m-4"\n data-action="close"\n style="z-index: 10001;"\n title="Close"></button>\n\n \x3c!-- Counter --\x3e\n {{#showCounter}}\n <div class="lightbox-counter position-absolute top-0 start-50 translate-middle-x mt-4 text-white"\n style="z-index: 10001; font-size: 1.1rem;">\n {{currentNumber}} of {{total}}\n </div>\n {{/showCounter}}\n\n \x3c!-- Mode Indicator --\x3e\n <div class="lightbox-mode-indicator position-absolute bottom-0 start-50 translate-middle-x mb-4 text-white bg-dark bg-opacity-75 px-3 py-2 rounded"\n style="z-index: 10001; font-size: 0.9rem;">\n {{modeIndicator}} • Click image to toggle\n </div>\n\n \x3c!-- Navigation --\x3e\n {{#showNavigation}}\n <button type="button" class="btn btn-light btn-lg position-absolute start-0 top-50 translate-middle-y ms-4"\n data-action="prev"\n style="z-index: 10001;"\n title="Previous"\n {{#isFirst}}disabled{{/isFirst}}>\n <i class="bi bi-chevron-left"></i>\n </button>\n\n <button type="button" class="btn btn-light btn-lg position-absolute end-0 top-50 translate-middle-y me-4"\n data-action="next"\n style="z-index: 10001;"\n title="Next"\n {{#isLast}}disabled{{/isLast}}>\n <i class="bi bi-chevron-right"></i>\n </button>\n {{/showNavigation}}\n\n \x3c!-- Image container --\x3e\n <div class="lightbox-image-container w-100 h-100 d-flex align-items-center justify-content-center p-5"\n style="{{containerStyle}}">\n {{#currentImage}}\n <img src="{{src}}"\n alt="{{alt}}"\n class="lightbox-image img-fluid"\n style="{{imageStyle}}"\n data-action="image-click">\n {{/currentImage}}\n </div>\n\n \x3c!-- Loading spinner --\x3e\n <div class="lightbox-loading position-absolute top-50 start-50 translate-middle text-white"\n style="display: none;">\n <div class="spinner-border" role="status">\n <span class="visually-hidden">Loading...</span>\n </div>\n </div>\n </div>\n '}async onAfterRender(){document.body.appendChild(this.element),document.body.style.overflow="hidden",this.allowKeyboard&&document.addEventListener("keydown",this._keyboardHandler),this.preloadAdjacentImages()}async handleActionClose(){this.close()}async handleActionBackdropClick(t){this.closeOnBackdrop&&t.target===t.currentTarget&&this.close()}async handleActionPrev(){this.showPrevious()}async handleActionNext(){this.showNext()}async handleActionImageClick(){this.toggleImageMode()}showPrevious(){this.currentIndex>0&&(this.currentIndex--,this.updateImage(),this.preloadAdjacentImages())}showNext(){this.currentIndex<this.images.length-1&&(this.currentIndex++,this.updateImage(),this.preloadAdjacentImages())}goToImage(t){t>=0&&t<this.images.length&&t!==this.currentIndex&&(this.currentIndex=t,this.updateImage(),this.preloadAdjacentImages())}async updateImage(){const t=this.images[this.currentIndex],e=this.element.querySelector(".lightbox-image"),s=this.element.querySelector(".lightbox-counter"),i=this.element.querySelector('[data-action="prev"]'),n=this.element.querySelector('[data-action="next"]');e&&(this.showLoading(),e.src=t.src,e.alt=t.alt,await this.waitForImageLoad(e),this.hideLoading()),s&&(s.textContent=`${this.currentIndex+1} of ${this.images.length}`),i&&(i.disabled=0===this.currentIndex),n&&(n.disabled=this.currentIndex===this.images.length-1),this.updateTemplateProperties(),this.updateImageDisplay();const a=this.getApp()?.events;a&&a.emit("lightbox:image-changed",{gallery:this,index:this.currentIndex,image:t})}showLoading(){const t=this.element.querySelector(".lightbox-loading");t&&(t.style.display="block")}hideLoading(){const t=this.element.querySelector(".lightbox-loading");t&&(t.style.display="none")}waitForImageLoad(t){return new Promise(e=>{t.complete?e():(t.onload=e,t.onerror=e)})}preloadAdjacentImages(){const t=[];this.currentIndex>0&&t.push(this.currentIndex-1),this.currentIndex<this.images.length-1&&t.push(this.currentIndex+1),t.forEach(t=>{const e=this.images[t];(new Image).src=e.src})}handleKeyboard(t){switch(t.key){case"Escape":t.preventDefault(),this.close();break;case"ArrowLeft":t.preventDefault(),this.showPrevious();break;case"ArrowRight":t.preventDefault(),this.showNext();break;case"Home":t.preventDefault(),this.goToImage(0);break;case"End":t.preventDefault(),this.goToImage(this.images.length-1)}}toggleImageMode(){this.fitToScreen=!this.fitToScreen,this.updateTemplateProperties(),this.updateImageDisplay();const t=this.getApp()?.events;t&&t.emit("lightbox:mode-changed",{gallery:this,fitToScreen:this.fitToScreen})}updateImageDisplay(){const t=this.element.querySelector(".lightbox-image"),e=this.element.querySelector(".lightbox-image-container"),s=this.element.querySelector(".lightbox-mode-indicator");t&&(this.fitToScreen?(t.style.maxWidth="100%",t.style.maxHeight="100%",t.style.objectFit="contain",t.style.cursor="zoom-in"):(t.style.maxWidth="none",t.style.maxHeight="none",t.style.objectFit="none",t.style.cursor="zoom-out"),t.style.userSelect="none"),e&&(e.style.overflow=this.fitToScreen?"":"auto"),s&&(s.textContent=`${this.modeIndicator} • Click image to toggle`)}close(){const t=this.getApp()?.events;t&&t.emit("lightbox:closed",{gallery:this}),this.destroy()}async onBeforeDestroy(){document.body.style.overflow="",this.allowKeyboard&&document.removeEventListener("keydown",this._keyboardHandler),this.element.parentNode===document.body&&document.body.removeChild(this.element)}static show(t,e={}){const s=new LightboxGallery({images:t,...e});return s.render().then(()=>{s.mount()}),s}}window.LightboxGallery=LightboxGallery;class PDFViewer extends t.View{constructor(t={}){super({...t,className:`pdf-viewer ${t.className||""}`,tagName:"div"}),this.pdfUrl=t.pdfUrl||t.src||"",this.title=t.title||"PDF Document",this.pdfDoc=null,this.currentPage=1,this.totalPages=0,this.pageRendering=!1,this.pageNumPending=null,this.scale=1,this.minScale=.25,this.maxScale=5,this.scaleStep=.25,this.fitMode="page",this.canvas=null,this.ctx=null,this.showControls=!1!==t.showControls,this.allowZoom=!1!==t.allowZoom,this.allowNavigation=!1!==t.allowNavigation,this.showPageNumbers=!1!==t.showPageNumbers,this.isLoaded=!1,this.isLoading=!1,this.pdfjsWorkerPath=t.pdfjsWorkerPath||"https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.0.379/pdf.worker.min.js",this.pdfjsCMapUrl=t.pdfjsCMapUrl||"https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.0.379/cmaps/",this.canvasContainer=null,this.controlsElement=null,this.statusElement=null,this.pageInput=null}async getTemplate(){return'\n <div class="pdf-viewer-container">\n {{#showControls}}\n <div class="pdf-viewer-toolbar" data-container="toolbar">\n <div class="btn-toolbar" role="toolbar">\n \x3c!-- Navigation Controls --\x3e\n {{#allowNavigation}}\n <div class="btn-group me-2" role="group" aria-label="Navigation">\n <button type="button" class="btn btn-outline-secondary btn-sm" data-action="first-page" title="First Page">\n <i class="bi bi-chevron-double-left"></i>\n </button>\n <button type="button" class="btn btn-outline-secondary btn-sm" data-action="prev-page" title="Previous Page">\n <i class="bi bi-chevron-left"></i>\n </button>\n \n {{#showPageNumbers}}\n <div class="input-group input-group-sm" style="width: 120px;">\n <input type="number" class="form-control text-center page-input" min="1" value="1" data-change-action="page-input">\n <span class="input-group-text page-total">/ 0</span>\n </div>\n {{/showPageNumbers}}\n \n <button type="button" class="btn btn-outline-secondary btn-sm" data-action="next-page" title="Next Page">\n <i class="bi bi-chevron-right"></i>\n </button>\n <button type="button" class="btn btn-outline-secondary btn-sm" data-action="last-page" title="Last Page">\n <i class="bi bi-chevron-double-right"></i>\n </button>\n </div>\n {{/allowNavigation}}\n\n \x3c!-- Zoom Controls --\x3e\n {{#allowZoom}}\n <div class="btn-group me-2" role="group" aria-label="Zoom">\n <button type="button" class="btn btn-outline-secondary btn-sm" data-action="zoom-out" title="Zoom Out">\n <i class="bi bi-zoom-out"></i>\n </button>\n <button type="button" class="btn btn-outline-secondary btn-sm" data-action="zoom-in" title="Zoom In">\n <i class="bi bi-zoom-in"></i>\n </button>\n \n <div class="btn-group" role="group">\n <button type="button" class="btn btn-outline-secondary btn-sm dropdown-toggle" data-bs-toggle="dropdown" title="Fit">\n <i class="bi bi-arrows-fullscreen"></i>\n </button>\n <ul class="dropdown-menu">\n <li><a class="dropdown-item" href="#" data-action="fit-page">Fit Page</a></li>\n <li><a class="dropdown-item" href="#" data-action="fit-width">Fit Width</a></li>\n <li><a class="dropdown-item" href="#" data-action="actual-size">Actual Size</a></li>\n </ul>\n </div>\n </div>\n {{/allowZoom}}\n\n \x3c!-- Utility Controls --\x3e\n <div class="btn-group me-2" role="group" aria-label="Utilities">\n <button type="button" class="btn btn-outline-secondary btn-sm" data-action="download" title="Download">\n <i class="bi bi-download"></i>\n </button>\n <button type="button" class="btn btn-outline-secondary btn-sm" data-action="print" title="Print">\n <i class="bi bi-printer"></i>\n </button>\n </div>\n </div>\n </div>\n {{/showControls}}\n\n \x3c!-- PDF Content Area --\x3e\n <div class="pdf-viewer-content" data-container="content">\n <div class="pdf-canvas-container" data-container="canvasContainer">\n <canvas class="pdf-canvas" data-container="canvas"></canvas>\n </div>\n\n <div class="pdf-viewer-overlay">\n <div class="pdf-viewer-loading">\n <div class="spinner-border text-primary" role="status">\n <span class="visually-hidden">Loading PDF...</span>\n </div>\n <div class="mt-2">Loading PDF...</div>\n </div>\n </div>\n\n <div class="pdf-viewer-error" style="display: none;">\n <div class="alert alert-danger" role="alert">\n <i class="bi bi-exclamation-triangle"></i>\n <strong>Error:</strong> <span class="error-message">Failed to load PDF</span>\n </div>\n </div>\n </div>\n\n \x3c!-- Status Bar --\x3e\n <div class="pdf-viewer-status" data-container="status">\n <small class="text-muted">\n <span class="current-page">0</span> of <span class="total-pages">0</span> pages |\n <span class="zoom-level">100%</span> |\n <span class="document-title">{{title}}</span>\n </small>\n </div>\n </div>\n '}get(){return{pdfUrl:this.pdfUrl,title:this.title,showControls:this.showControls,allowZoom:this.allowZoom,allowNavigation:this.allowNavigation,showPageNumbers:this.showPageNumbers}}async onAfterRender(){this.canvas=this.element.querySelector(".pdf-canvas"),this.canvasContainer=this.element.querySelector(".pdf-canvas-container"),this.controlsElement=this.element.querySelector(".pdf-viewer-toolbar"),this.statusElement=this.element.querySelector(".pdf-viewer-status"),this.pageInput=this.element.querySelector(".page-input"),this.overlayElement=this.element.querySelector(".pdf-viewer-overlay"),this.errorElement=this.element.querySelector(".pdf-viewer-error"),this.canvas&&(this.ctx=this.canvas.getContext("2d")),this.setupEssentialEventListeners(),await this.initializePDFJS(),this.pdfUrl&&await this.loadPDF()}setupEssentialEventListeners(){const t=t=>this.handleKeyDown(t);document.addEventListener("keydown",t);let e=null;this.canvasContainer&&(e=new ResizeObserver(()=>{"auto"!==this.fitMode&&this.applyFitMode()}),e.observe(this.canvasContainer)),this._essentialListeners=[{el:document,type:"keydown",fn:t}],this._resizeObserver=e}async initializePDFJS(){try{return void 0===window.pdfjsLib&&await this.loadPDFJSLibrary(),window.pdfjsLib.GlobalWorkerOptions.workerSrc=this.pdfjsWorkerPath,!0}catch(t){return console.error("Failed to initialize PDF.js:",t),this.showError("Failed to initialize PDF viewer"),!1}}async loadPDFJSLibrary(){return new Promise((t,e)=>{const s=document.createElement("script");s.src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.0.379/pdf.min.js",s.onload=t,s.onerror=e,document.head.appendChild(s)})}async handleActionFirstPage(){await this.goToPage(1)}async handleActionPrevPage(){await this.goToPage(this.currentPage-1)}async handleActionNextPage(){await this.goToPage(this.currentPage+1)}async handleActionLastPage(){await this.goToPage(this.totalPages)}async onChangePageInput(t,e){const s=parseInt(e.value,10);s>=1&&s<=this.totalPages?await this.goToPage(s):e.value=this.currentPage}async handleActionZoomIn(){this.setScale(this.scale+this.scaleStep)}async handleActionZoomOut(){this.setScale(this.scale-this.scaleStep)}async handleActionFitPage(){this.setFitMode("page")}async handleActionFitWidth(){this.setFitMode("width")}async handleActionActualSize(){this.setScale(1),this.fitMode="auto"}async handleActionDownload(){this.downloadPDF()}async handleActionPrint(){window.print()}async loadPDF(){if(!this.pdfUrl||!window.pdfjsLib)return this.showError("PDF URL or PDF.js library not available"),!1;this.isLoading=!0,this.showLoading();try{const t=window.pdfjsLib.getDocument({url:this.pdfUrl,cMapUrl:this.pdfjsCMapUrl,cMapPacked:!0});this.pdfDoc=await t.promise,this.totalPages=this.pdfDoc.numPages,this.currentPage=1,this.updatePageControls(),this.updateStatus(),await this.renderPage(1),this.isLoaded=!0,this.isLoading=!1,this.element.classList.add("loaded"),this.hideLoading(),this.applyFitMode();const e=this.getApp()?.events;return e&&e.emit("pdfviewer:loaded",{viewer:this,pdfUrl:this.pdfUrl,totalPages:this.totalPages}),!0}catch(t){console.error("Error loading PDF:",t),this.isLoading=!1,this.showError("Failed to load PDF document");const e=this.getApp()?.events;return e&&e.emit("pdfviewer:error",{viewer:this,pdfUrl:this.pdfUrl,error:t.message}),!1}}async renderPage(t){if(this.pageRendering)this.pageNumPending=t;else if(this.pdfDoc&&this.canvas&&this.ctx){this.pageRendering=!0,this.currentPage=t;try{const e=await this.pdfDoc.getPage(t),s=e.getViewport({scale:this.scale});this.canvas.height=s.height,this.canvas.width=s.width,this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height);const i={canvasContext:this.ctx,viewport:s},n=e.render(i);if(await n.promise,this.pageRendering=!1,null!==this.pageNumPending){const t=this.pageNumPending;this.pageNumPending=null,await this.renderPage(t)}this.updatePageControls(),this.updateStatus();const a=this.getApp()?.events;a&&a.emit("pdfviewer:page-changed",{viewer:this,currentPage:this.currentPage,totalPages:this.totalPages})}catch(e){console.error("Error rendering page:",e),this.pageRendering=!1,this.showError("Failed to render PDF page")}}}async goToPage(t){!this.pdfDoc||t<1||t>this.totalPages||await this.renderPage(t)}setScale(t){const e=this.scale;this.scale=Math.max(this.minScale,Math.min(this.maxScale,t)),this.fitMode="auto",this.isLoaded&&this.renderPage(this.currentPage);const s=this.getApp()?.events;s&&e!==this.scale&&s.emit("pdfviewer:scale-changed",{viewer:this,oldScale:e,newScale:this.scale})}setFitMode(t){const e=this.fitMode;this.fitMode=t,this.applyFitMode();const s=this.getApp()?.events;s&&s.emit("pdfviewer:fit-mode-changed",{viewer:this,oldMode:e,newMode:t})}applyFitMode(){this.isLoaded&&this.pdfDoc&&this.canvasContainer&&this.pdfDoc.getPage(this.currentPage).then(t=>{const e=this.canvasContainer.getBoundingClientRect(),s=t.getViewport({scale:1});let i;if("page"===this.fitMode){const t=(e.width-40)/s.width,n=(e.height-40)/s.height;i=Math.min(t,n)}else{if("width"!==this.fitMode)return;i=(e.width-40)/s.width}this.scale=Math.max(this.minScale,Math.min(this.maxScale,i)),this.renderPage(this.currentPage)})}handleKeyDown(t){if("INPUT"!==t.target.tagName||t.target===this.pageInput)switch(t.key){case"ArrowLeft":case"PageUp":t.preventDefault(),this.goToPage(this.currentPage-1);break;case"ArrowRight":case"PageDown":t.preventDefault(),this.goToPage(this.currentPage+1);break;case"Home":t.preventDefault(),this.goToPage(1);break;case"End":t.preventDefault(),this.goToPage(this.totalPages);break;case"+":case"=":(t.ctrlKey||t.metaKey)&&(t.preventDefault(),this.setScale(this.scale+this.scaleStep));break;case"-":(t.ctrlKey||t.metaKey)&&(t.preventDefault(),this.setScale(this.scale-this.scaleStep));break;case"0":(t.ctrlKey||t.metaKey)&&(t.preventDefault(),this.setFitMode("page"))}}updatePageControls(){this.pageInput&&(this.pageInput.value=this.currentPage);const t=this.element.querySelector(".page-total");t&&(t.textContent=`/ ${this.totalPages}`);const e=this.element.querySelector('[data-action="first-page"]'),s=this.element.querySelector('[data-action="prev-page"]'),i=this.element.querySelector('[data-action="next-page"]'),n=this.element.querySelector('[data-action="last-page"]');e&&(e.disabled=this.currentPage<=1),s&&(s.disabled=this.currentPage<=1),i&&(i.disabled=this.currentPage>=this.totalPages),n&&(n.disabled=this.currentPage>=this.totalPages);const a=this.element.querySelector('[data-action="zoom-in"]'),o=this.element.querySelector('[data-action="zoom-out"]');a&&(a.disabled=this.scale>=this.maxScale),o&&(o.disabled=this.scale<=this.minScale)}updateStatus(){if(!this.statusElement)return;const t=this.statusElement.querySelector(".current-page"),e=this.statusElement.querySelector(".total-pages"),s=this.statusElement.querySelector(".zoom-level");t&&(t.textContent=this.currentPage),e&&(e.textContent=this.totalPages),s&&(s.textContent=`${Math.round(100*this.scale)}%`)}showLoading(){this.overlayElement&&(this.overlayElement.style.display="flex")}hideLoading(){this.overlayElement&&(this.overlayElement.style.display="none")}showError(t){if(this.hideLoading(),this.errorElement){const e=this.errorElement.querySelector(".error-message");e&&(e.textContent=t),this.errorElement.style.display="block"}console.error("PDF Viewer Error:",t)}downloadPDF(){if(!this.pdfUrl)return;const t=document.createElement("a");t.href=this.pdfUrl,t.download=this.title+".pdf",t.target="_blank",t.click()}setPDF(t,e=""){const s=this.pdfUrl;this.pdfUrl=t,this.title=e||"PDF Document",this.isLoaded=!1,this.element.classList.remove("loaded"),this.pdfDoc&&(this.pdfDoc.destroy(),this.pdfDoc=null),this.currentPage=1,this.totalPages=0,this.scale=1,t&&this.loadPDF();const i=this.getApp()?.events;i&&i.emit("pdfviewer:pdf-changed",{viewer:this,oldPdfUrl:s,newPdfUrl:t})}getCurrentPage(){return this.currentPage}getTotalPages(){return this.totalPages}getCurrentScale(){return this.scale}async onBeforeDestroy(){this.pdfDoc&&(this.pdfDoc.destroy(),this.pdfDoc=null),this.pageRendering=!1,this.pageNumPending=null,this._essentialListeners&&(this._essentialListeners.forEach(({el:t,type:e,fn:s})=>{t&&t.removeEventListener(e,s)}),this._essentialListeners=null),this._resizeObserver&&(this._resizeObserver.disconnect(),this._resizeObserver=null);const t=this.getApp()?.events;t&&t.emit("pdfviewer:destroyed",{viewer:this})}static async showDialog(t,s={}){const{title:i="PDF Viewer",size:n="fullscreen",showControls:a=!0,allowZoom:o=!0,allowNavigation:r=!0,showPageNumbers:l=!0,...c}=s,d=new PDFViewer({pdfUrl:t,title:i,showControls:a,allowZoom:o,allowNavigation:r,showPageNumbers:l}),h=new e.Dialog({title:i,body:d,size:n,centered:!0,backdrop:"static",keyboard:!0,buttons:[{text:"Download",action:"download",class:"btn btn-outline-primary"},{text:"Close",action:"close",class:"btn btn-secondary",dismiss:!0}],...c});return await h.render(),document.body.appendChild(h.element),await h.mount(),h.show(),new Promise(t=>{h.on("hidden",()=>{h.destroy(),t(d)}),h.on("action:download",()=>{d.downloadPDF()}),h.on("action:close",()=>{h.hide()})})}}exports.LightboxGallery=LightboxGallery,exports.PDFViewer=PDFViewer;
2
+ //# sourceMappingURL=PDFViewer-Cg_tbqb7.js.map