web-mojo 2.2.69 → 2.2.70

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 (157) hide show
  1. package/dist/admin.cjs.js +1 -1
  2. package/dist/admin.cjs.js.map +1 -1
  3. package/dist/admin.es.js +1 -1
  4. package/dist/admin.es.js.map +1 -1
  5. package/dist/auth.cjs.js +1 -1
  6. package/dist/auth.es.js +1 -1
  7. package/dist/charts.cjs.js +1 -1
  8. package/dist/charts.cjs.js.map +1 -1
  9. package/dist/charts.es.js +1 -1
  10. package/dist/charts.es.js.map +1 -1
  11. package/dist/chunks/ChatView-DH42WXgV.js +2 -0
  12. package/dist/chunks/{ChatView-Dw-iVmht.js.map → ChatView-DH42WXgV.js.map} +1 -1
  13. package/dist/chunks/ChatView-_8eQTETQ.js +2 -0
  14. package/dist/chunks/{ChatView-CZ3Key2k.js.map → ChatView-_8eQTETQ.js.map} +1 -1
  15. package/dist/chunks/Collection-BUv4E9op.js +2 -0
  16. package/dist/chunks/Collection-BUv4E9op.js.map +1 -0
  17. package/dist/chunks/Collection-r1ACzUeh.js +2 -0
  18. package/dist/chunks/Collection-r1ACzUeh.js.map +1 -0
  19. package/dist/chunks/ContextMenu-BFxliZ03.js +2 -0
  20. package/dist/chunks/{ContextMenu-8vTiZZQV.js.map → ContextMenu-BFxliZ03.js.map} +1 -1
  21. package/dist/chunks/ContextMenu-BwJJ4QJE.js +2 -0
  22. package/dist/chunks/{ContextMenu-DBw0WMTO.js.map → ContextMenu-BwJJ4QJE.js.map} +1 -1
  23. package/dist/chunks/DataView-DMpNXerv.js +2 -0
  24. package/dist/chunks/{DataView-DyJKgOn3.js.map → DataView-DMpNXerv.js.map} +1 -1
  25. package/dist/chunks/DataView-_CACqzRt.js +2 -0
  26. package/dist/chunks/{DataView-BEovBggn.js.map → DataView-_CACqzRt.js.map} +1 -1
  27. package/dist/chunks/Dialog-BVCCpLPw.js +3 -0
  28. package/dist/chunks/Dialog-BVCCpLPw.js.map +1 -0
  29. package/dist/chunks/Dialog-BYiynSW-.js +3 -0
  30. package/dist/chunks/Dialog-BYiynSW-.js.map +1 -0
  31. package/dist/chunks/FormView-Dw7HDwzy.js +3 -0
  32. package/dist/chunks/{FormView-BRHAIawp.js.map → FormView-Dw7HDwzy.js.map} +1 -1
  33. package/dist/chunks/FormView-OgrZ7x0z.js +3 -0
  34. package/dist/chunks/{FormView-B1CXO2t8.js.map → FormView-OgrZ7x0z.js.map} +1 -1
  35. package/dist/chunks/ListView-2M4I8KHF.js +2 -0
  36. package/dist/chunks/{ListView-CMZpwyyC.js.map → ListView-2M4I8KHF.js.map} +1 -1
  37. package/dist/chunks/ListView-B0QbqSPv.js +2 -0
  38. package/dist/chunks/{ListView-BLFFK_Ir.js.map → ListView-B0QbqSPv.js.map} +1 -1
  39. package/dist/chunks/MetricsCountryMapView-DDdDJQFA.js +2 -0
  40. package/dist/chunks/{MetricsCountryMapView-B0kWK-Js.js.map → MetricsCountryMapView-DDdDJQFA.js.map} +1 -1
  41. package/dist/chunks/MetricsCountryMapView-DIlezla0.js +2 -0
  42. package/dist/chunks/{MetricsCountryMapView-DuBKO7gz.js.map → MetricsCountryMapView-DIlezla0.js.map} +1 -1
  43. package/dist/chunks/MetricsMiniChartWidget-Dt2V0eXP.js +2 -0
  44. package/dist/chunks/{MetricsMiniChartWidget-Dg1e6EQJ.js.map → MetricsMiniChartWidget-Dt2V0eXP.js.map} +1 -1
  45. package/dist/chunks/MetricsMiniChartWidget-_N4kzNY_.js +2 -0
  46. package/dist/chunks/{MetricsMiniChartWidget-D1w608Jy.js.map → MetricsMiniChartWidget-_N4kzNY_.js.map} +1 -1
  47. package/dist/chunks/PDFViewer-BruR1RFn.js +2 -0
  48. package/dist/chunks/{PDFViewer-D_3V8QJe.js.map → PDFViewer-BruR1RFn.js.map} +1 -1
  49. package/dist/chunks/PDFViewer-CyGFVcvX.js +2 -0
  50. package/dist/chunks/{PDFViewer-CDeV9OBs.js.map → PDFViewer-CyGFVcvX.js.map} +1 -1
  51. package/dist/chunks/TableView-CxYpxZvr.js +2 -0
  52. package/dist/chunks/{TableView-CI_7a-kD.js.map → TableView-CxYpxZvr.js.map} +1 -1
  53. package/dist/chunks/TableView-DemRVhnX.js +2 -0
  54. package/dist/chunks/{TableView-CWk5k4LQ.js.map → TableView-DemRVhnX.js.map} +1 -1
  55. package/dist/chunks/TokenManager-BFaxNsXO.js +2 -0
  56. package/dist/chunks/{TokenManager-sZgt--C9.js.map → TokenManager-BFaxNsXO.js.map} +1 -1
  57. package/dist/chunks/TokenManager-IlBEFXqZ.js +2 -0
  58. package/dist/chunks/{TokenManager-ien2XzwO.js.map → TokenManager-IlBEFXqZ.js.map} +1 -1
  59. package/dist/chunks/UserProfileView-9vkfCPsp.js +2 -0
  60. package/dist/chunks/{UserProfileView-kupeq2rN.js.map → UserProfileView-9vkfCPsp.js.map} +1 -1
  61. package/dist/chunks/UserProfileView-tcBT6XcE.js +2 -0
  62. package/dist/chunks/{UserProfileView-DnVMHcLH.js.map → UserProfileView-tcBT6XcE.js.map} +1 -1
  63. package/dist/chunks/WebApp-BFR1zozS.js +2 -0
  64. package/dist/chunks/{WebApp-CcVF73yg.js.map → WebApp-BFR1zozS.js.map} +1 -1
  65. package/dist/chunks/WebApp-C82womPC.js +2 -0
  66. package/dist/chunks/{WebApp-Bti0Gqqo.js.map → WebApp-C82womPC.js.map} +1 -1
  67. package/dist/chunks/WebSocketClient-Ibi7mLQu.js +2 -0
  68. package/dist/chunks/{WebSocketClient-Bh0Mmtje.js.map → WebSocketClient-Ibi7mLQu.js.map} +1 -1
  69. package/dist/chunks/WebSocketClient-QaCUN3EQ.js +2 -0
  70. package/dist/chunks/{WebSocketClient-CLgYPxWX.js.map → WebSocketClient-QaCUN3EQ.js.map} +1 -1
  71. package/dist/chunks/index-BaPQHxbL.js +2 -0
  72. package/dist/chunks/{index-Da9sT-tE.js.map → index-BaPQHxbL.js.map} +1 -1
  73. package/dist/chunks/index-BdfwxVMZ.js +2 -0
  74. package/dist/chunks/{index-Aq9ke4vg.js.map → index-BdfwxVMZ.js.map} +1 -1
  75. package/dist/chunks/{version-XmirKYWA.js → version-C2yYRyPn.js} +2 -2
  76. package/dist/chunks/{version-XmirKYWA.js.map → version-C2yYRyPn.js.map} +1 -1
  77. package/dist/chunks/{version-D8JjsPW0.js → version-CaiqhdME.js} +2 -2
  78. package/dist/chunks/{version-D8JjsPW0.js.map → version-CaiqhdME.js.map} +1 -1
  79. package/dist/docit.cjs.js +1 -1
  80. package/dist/docit.cjs.js.map +1 -1
  81. package/dist/docit.es.js +1 -1
  82. package/dist/docit.es.js.map +1 -1
  83. package/dist/index.cjs.js +1 -1
  84. package/dist/index.cjs.js.map +1 -1
  85. package/dist/index.es.js +1 -1
  86. package/dist/index.es.js.map +1 -1
  87. package/dist/lightbox.cjs.js +1 -1
  88. package/dist/lightbox.cjs.js.map +1 -1
  89. package/dist/lightbox.es.js +1 -1
  90. package/dist/lightbox.es.js.map +1 -1
  91. package/dist/map.cjs.js +1 -1
  92. package/dist/map.cjs.js.map +1 -1
  93. package/dist/map.es.js +1 -1
  94. package/dist/map.es.js.map +1 -1
  95. package/dist/timeline.cjs.js +1 -1
  96. package/dist/timeline.cjs.js.map +1 -1
  97. package/dist/timeline.es.js +1 -1
  98. package/dist/timeline.es.js.map +1 -1
  99. package/dist/user-profile.cjs.js +1 -1
  100. package/dist/user-profile.es.js +1 -1
  101. package/dist/web-mojo.lite.iife.js +5435 -5435
  102. package/dist/web-mojo.lite.iife.js.map +1 -1
  103. package/dist/web-mojo.lite.iife.min.js +68 -68
  104. package/dist/web-mojo.lite.iife.min.js.map +1 -1
  105. package/package.json +1 -1
  106. package/dist/chunks/ChatView-CZ3Key2k.js +0 -2
  107. package/dist/chunks/ChatView-Dw-iVmht.js +0 -2
  108. package/dist/chunks/Collection-BWKmydl5.js +0 -2
  109. package/dist/chunks/Collection-BWKmydl5.js.map +0 -1
  110. package/dist/chunks/Collection-CmjTsmrP.js +0 -2
  111. package/dist/chunks/Collection-CmjTsmrP.js.map +0 -1
  112. package/dist/chunks/ContextMenu-8vTiZZQV.js +0 -2
  113. package/dist/chunks/ContextMenu-DBw0WMTO.js +0 -2
  114. package/dist/chunks/DataView-BEovBggn.js +0 -2
  115. package/dist/chunks/DataView-DyJKgOn3.js +0 -2
  116. package/dist/chunks/Dialog-Dhqtd9Yz.js +0 -2
  117. package/dist/chunks/Dialog-Dhqtd9Yz.js.map +0 -1
  118. package/dist/chunks/Dialog-t_9l2Mou.js +0 -2
  119. package/dist/chunks/Dialog-t_9l2Mou.js.map +0 -1
  120. package/dist/chunks/Files-6eRT5k3r.js +0 -2
  121. package/dist/chunks/Files-6eRT5k3r.js.map +0 -1
  122. package/dist/chunks/Files-Dh_5PFBn.js +0 -2
  123. package/dist/chunks/Files-Dh_5PFBn.js.map +0 -1
  124. package/dist/chunks/FormView-B1CXO2t8.js +0 -3
  125. package/dist/chunks/FormView-BRHAIawp.js +0 -3
  126. package/dist/chunks/ListView-BLFFK_Ir.js +0 -2
  127. package/dist/chunks/ListView-CMZpwyyC.js +0 -2
  128. package/dist/chunks/MetricsCountryMapView-B0kWK-Js.js +0 -2
  129. package/dist/chunks/MetricsCountryMapView-DuBKO7gz.js +0 -2
  130. package/dist/chunks/MetricsMiniChartWidget-D1w608Jy.js +0 -2
  131. package/dist/chunks/MetricsMiniChartWidget-Dg1e6EQJ.js +0 -2
  132. package/dist/chunks/PDFViewer-CDeV9OBs.js +0 -2
  133. package/dist/chunks/PDFViewer-D_3V8QJe.js +0 -2
  134. package/dist/chunks/Rest-B1eUyLX5.js +0 -2
  135. package/dist/chunks/Rest-B1eUyLX5.js.map +0 -1
  136. package/dist/chunks/Rest-BJ3Mvx1L.js +0 -2
  137. package/dist/chunks/Rest-BJ3Mvx1L.js.map +0 -1
  138. package/dist/chunks/TableView-CI_7a-kD.js +0 -2
  139. package/dist/chunks/TableView-CWk5k4LQ.js +0 -2
  140. package/dist/chunks/ToastService-C2tTooFn.js +0 -3
  141. package/dist/chunks/ToastService-C2tTooFn.js.map +0 -1
  142. package/dist/chunks/ToastService-nUaGVpSl.js +0 -3
  143. package/dist/chunks/ToastService-nUaGVpSl.js.map +0 -1
  144. package/dist/chunks/TokenManager-ien2XzwO.js +0 -2
  145. package/dist/chunks/TokenManager-sZgt--C9.js +0 -2
  146. package/dist/chunks/User-BL9M_PWB.js +0 -2
  147. package/dist/chunks/User-BL9M_PWB.js.map +0 -1
  148. package/dist/chunks/User-DqHG5Gr1.js +0 -2
  149. package/dist/chunks/User-DqHG5Gr1.js.map +0 -1
  150. package/dist/chunks/UserProfileView-DnVMHcLH.js +0 -2
  151. package/dist/chunks/UserProfileView-kupeq2rN.js +0 -2
  152. package/dist/chunks/WebApp-Bti0Gqqo.js +0 -2
  153. package/dist/chunks/WebApp-CcVF73yg.js +0 -2
  154. package/dist/chunks/WebSocketClient-Bh0Mmtje.js +0 -2
  155. package/dist/chunks/WebSocketClient-CLgYPxWX.js +0 -2
  156. package/dist/chunks/index-Aq9ke4vg.js +0 -2
  157. package/dist/chunks/index-Da9sT-tE.js +0 -2
@@ -1,2 +0,0 @@
1
- "use strict";const t=require("./Collection-CmjTsmrP.js"),e=require("./ListView-CMZpwyyC.js"),i=require("./Rest-B1eUyLX5.js"),n=require("./Dialog-Dhqtd9Yz.js"),s=require("./FormView-B1CXO2t8.js");class Log extends t.Model{constructor(t={}){super(t,{endpoint:"/api/logs"})}}class LogList extends t.Collection{constructor(t={}){super({ModelClass:Log,endpoint:"/api/logs",size:10,...t})}}class Member extends t.Model{constructor(t={}){super(t,{endpoint:"/api/group/member"})}hasPermission(t){if(Array.isArray(t))return t.some(t=>this.hasPermission(t));const e=this.get("permissions");return!!e&&1==e[t]}async fetchForGroup(t){return await this.fetch({url:`/api/group/${t}/member`})}}class MemberList extends t.Collection{constructor(t={}){super({ModelClass:Member,endpoint:"/api/group/member",size:10,...t})}}const l={edit:{title:"Edit Membership",fields:[{name:"user.display_name",type:"text",label:"Display Name",placeholder:"Enter Display Name"},{name:"metadata.role",type:"text",label:"Role",placeholder:"Enter role"},{name:"is_active",type:"switch",label:"Is Enabled",columns:12}]}};Member.EDIT_FORM=l.edit,Member.ADD_FORM=l.create;class TableRow extends e.ListViewItem{constructor(t={}){super({tagName:"tr",className:"table-row",enableTooltips:!0,...t}),this.columns=t.columns||[],this.actions=t.actions||null,this.contextMenu=t.contextMenu||null,this.batchActions=t.batchActions||null,this.tableView=t.tableView||t.listView||null,this.editingCells=/* @__PURE__ */new Set,this.template=this.buildRowTemplate()}getResponsiveClasses(t){if(!t)return"";const e=["sm","md","lg","xl","xxl"];if("string"==typeof t)return e.includes(t)?`d-none d-${t}-table-cell`:(console.warn(`Invalid visibility breakpoint: ${t}. Valid options are: ${e.join(", ")}`),"");if("object"==typeof t){const i=[];if(t.hide){if(!e.includes(t.hide))return console.warn(`Invalid hide breakpoint: ${t.hide}. Valid options are: ${e.join(", ")}`),"";i.push(`d-table-cell d-${t.hide}-none`)}if(t.show){if(!e.includes(t.show))return console.warn(`Invalid show breakpoint: ${t.show}. Valid options are: ${e.join(", ")}`),"";t.hide?i.push(`d-${t.show}-table-cell`):i.push(`d-none d-${t.show}-table-cell`)}return i.join(" ")}return""}buildRowTemplate(){let t="";return this.tableView&&this.tableView.isSelectable()&&(t+='\n <td style="padding: 0;">\n <div class="mojo-select-cell {{#selected}}selected{{/selected}}"\n data-action="select">\n <div class="mojo-checkbox">\n <i class="bi bi-check"></i>\n </div>\n </div>\n </td>\n '),this.columns.forEach((e,i)=>{const n=[e.class||e.className||"",this.getResponsiveClasses(e.visibility),e.editable?"editable-cell":""].filter(t=>t).join(" "),s=this.buildCellTemplate(e,i);let l=e.action;!l&&e.editable?l="edit-cell":!l&&this.tableView.rowAction&&(l=this.tableView.rowAction),t+=l?`<td class="${n}" data-action="${l}" data-column="${e.key}">${s}</td>`:`<td class="${n}" data-column="${e.key}">${s}</td>`}),this.actions?t+=this.buildActionsTemplate():this.contextMenu&&(t+=this.buildContextMenuTemplate()),t}buildCellTemplate(t,e=0){const i=`model.${t.key}`,n=t.formatter||t.format;if(n){if("string"==typeof n)return`{{{${i}|${n}}}}`;if("function"==typeof n)return`<span data-formatter="${t.key}" data-formatter-id="${e}">{{${i}}}</span>`}return t.template?t.template:t.editable?`<span class="cell-content" data-field="${t.key}">{{{${i}}}}</span>`:`{{{${i}}}}`}buildActionsTemplate(){return this.actions&&0!==this.actions.length?`<td><div class="btn-group btn-group-sm">${this.actions.map(t=>{if("string"==typeof t)switch(t){case"view":return'\n <button class="btn btn-sm btn-outline-primary"\n data-action="view"\n title="View">\n <i class="bi bi-eye"></i>\n </button>\n ';case"edit":return'\n <button class="btn btn-sm btn-outline-secondary"\n data-action="edit"\n title="Edit">\n <i class="bi bi-pencil"></i>\n </button>\n ';case"delete":return'\n <button class="btn btn-sm btn-outline-danger"\n data-action="delete"\n title="Delete">\n <i class="bi bi-trash"></i>\n </button>\n ';default:return""}else if("object"==typeof t)return`\n <button class="btn btn-sm ${t.class||"btn-outline-primary"}"\n data-id="${this.model.id}"\n data-action="${t.action}"\n title="${t.label||""}">\n ${t.icon?`<i class="${t.icon}"></i>`:""}\n ${t.label&&!t.icon?t.label:""}\n </button>\n `;return""}).join("")}</div></td>`:""}buildContextMenuTemplate(){return this.contextMenu&&0!==this.contextMenu.length?`\n <td class="text-end" style="width: 1px;">\n <div class="dropdown">\n <button class="btn btn-sm btn-link border-0"\n type="button"\n data-bs-toggle="dropdown"\n aria-expanded="false"\n style="color: #6c757d;">\n <i class="bi bi-three-dots-vertical"></i>\n </button>\n <ul class="dropdown-menu dropdown-menu-end shadow-sm">\n ${this.buildContextMenuItems()}\n </ul>\n </div>\n </td>\n `:""}buildContextMenuItems(){return this.contextMenu.map(t=>{if(t.separator||t.divider)return'<li><hr class="dropdown-divider"></li>';let e="dropdown-item";return("delete"===t.action||t.danger)&&(e+=" text-danger"),t.disabled&&(e+=" disabled"),`\n <li>\n <a class="${e}" href="#"\n data-id="{{model.id}}"\n data-action="${t.action}"\n ${t.disabled?'aria-disabled="true" tabindex="-1"':""}>\n ${t.icon?`<i class="${t.icon} me-2"></i>`:""}\n ${t.label}\n </a>\n </li>\n `}).join("")}async onAfterRender(){await super.onAfterRender(),this.columns.forEach((t,e)=>{if(t.formatter&&"function"==typeof t.formatter){let n=this.element.querySelector(`[data-formatter-id="${e}"]`);if(n||(n=this.element.querySelector(`[data-formatter="${t.key}"]`)),n){const e=this.model.get?this.model.get(t.key):this.model[t.key],s={value:e,row:this.model,model:this.model,column:t,table:this.tableView,index:this.index};try{n.innerHTML=t.formatter(e,s)}catch(i){console.error(`Error formatting cell for column ${t.key}:`,i)}}}}),this.selected&&this.element.classList.add("selected");const t=this.model.get?this.model.get("id"):this.model.id;t&&this.element.setAttribute("data-id",t)}async onActionEditCell(t,e){t.stopPropagation();const i=e.getAttribute("data-column"),n=this.columns.find(t=>t.key===i);n&&n.editable&&(this.editingCells.has(i)||await this.enterEditMode(i,n,e))}async onActionRowClick(t,e){t.target.closest(".btn-group")||t.target.closest(".dropdown")||t.target.closest(".cell-editor")||(this.emit("row:click",{row:this,model:this.model,column:e.getAttribute("data-column"),event:t}),this.tableView&&this.tableView.emit("row:click",{row:this,model:this.model,column:e.getAttribute("data-column"),event:t}))}async onActionView(t,e){t.stopPropagation(),this.emit("row:view",{row:this,model:this.model,event:t}),this.tableView&&this.tableView.emit("row:view",{row:this,model:this.model,event:t})}async onActionEdit(t,e){return t.stopPropagation(),this.emit("row:edit",{row:this,model:this.model,event:t}),this.tableView&&this.tableView.emit("row:edit",{row:this,model:this.model,event:t}),!0}async onActionDelete(t,e){t.stopPropagation(),this.emit("row:delete",{row:this,model:this.model,event:t}),this.tableView&&this.tableView.emit("row:delete",{row:this,model:this.model,event:t})}async enterEditMode(t,e,i){const n=i.querySelector(".cell-content");if(!n)return;this.editingCells.add(t);const s=this.model.get?this.model.get(t):this.model[t],l=this.createCellEditor(e,s),a=n.innerHTML;n.style.display="none";const o=document.createElement("div");o.className="cell-editor",o.innerHTML=l,i.appendChild(o);const r=o.querySelector("input, select, .form-check-input");r&&(r.focus(),"text"!==r.type&&"textarea"!==r.type||r.select()),o.dataset.originalContent=a,o.dataset.columnKey=t,this.setupEditorEvents(o,t,e),this.emit("cell:edit",{row:this,model:this.model,column:t,originalValue:s})}createCellEditor(t,e){const i=t.editableOptions||{};switch(i.type){case"select":return this.createSelectEditor(i,e);case"switch":case"checkbox":return this.createSwitchEditor(i,e);case"textarea":return this.createTextareaEditor(i,e);default:return this.createTextEditor(i,e)}}createTextEditor(t,e){const i=t.placeholder||"";return`\n <div class="d-flex gap-1 align-items-center">\n <input type="${t.inputType||"text"}"\n class="form-control form-control-sm cell-input"\n value="${this.escapeHtml(e||"")}"\n placeholder="${i}">\n <button type="button" class="btn btn-sm btn-success cell-save" title="Save">\n <i class="bi bi-check"></i>\n </button>\n <button type="button" class="btn btn-sm btn-outline-secondary cell-cancel" title="Cancel">\n <i class="bi bi-x"></i>\n </button>\n </div>\n `}createTextareaEditor(t,e){const i=t.placeholder||"";return`\n <div class="d-flex gap-1">\n <textarea class="form-control form-control-sm cell-input"\n rows="${t.rows||2}"\n placeholder="${i}">${this.escapeHtml(e||"")}</textarea>\n <div class="d-flex flex-column gap-1">\n <button type="button" class="btn btn-sm btn-success cell-save" title="Save">\n <i class="bi bi-check"></i>\n </button>\n <button type="button" class="btn btn-sm btn-outline-secondary cell-cancel" title="Cancel">\n <i class="bi bi-x"></i>\n </button>\n </div>\n </div>\n `}createSelectEditor(t,e){const i=t.options||[];let n="";return i.forEach(t=>{if("string"==typeof t)n+=`<option value="${t}" ${t===e?"selected":""}>${t}</option>`;else if("object"==typeof t&&void 0!==t.value){const i=t.value===e?"selected":"";n+=`<option value="${t.value}" ${i}>${t.label||t.value}</option>`}}),`\n <div class="d-flex gap-1 align-items-center">\n <select class="form-select form-select-sm cell-input">\n ${n}\n </select>\n <button type="button" class="btn btn-sm btn-success cell-save" title="Save">\n <i class="bi bi-check"></i>\n </button>\n <button type="button" class="btn btn-sm btn-outline-secondary cell-cancel" title="Cancel">\n <i class="bi bi-x"></i>\n </button>\n </div>\n `}createSwitchEditor(t,e){const i=e?"checked":"";return`\n <div class="d-flex gap-2 align-items-center">\n <div class="form-check ${"switch"===t.type?"form-switch":""}">\n <input class="form-check-input cell-input" type="checkbox" ${i}>\n </div>\n <div class="d-flex gap-1">\n <button type="button" class="btn btn-sm btn-success cell-save" title="Save">\n <i class="bi bi-check"></i>\n </button>\n <button type="button" class="btn btn-sm btn-outline-secondary cell-cancel" title="Cancel">\n <i class="bi bi-x"></i>\n </button>\n </div>\n </div>\n `}setupEditorEvents(t,e,i){const n=t.querySelector(".cell-input"),s=t.querySelector(".cell-save"),l=t.querySelector(".cell-cancel");!n||"text"!==n.type&&"email"!==n.type&&"number"!==n.type||n.addEventListener("keydown",n=>{"Enter"===n.key?(n.preventDefault(),this.saveCellEdit(t,e,i)):"Escape"===n.key&&(n.preventDefault(),this.cancelCellEdit(t,e))}),!n||"checkbox"!==n.type&&"SELECT"!==n.tagName||!1===i.autoSave||n.addEventListener("change",()=>{this.saveCellEdit(t,e,i)}),s?.addEventListener("click",()=>{this.saveCellEdit(t,e,i)}),l?.addEventListener("click",()=>{this.cancelCellEdit(t,e)})}async saveCellEdit(t,e,i){const n=t.querySelector(".cell-input");if(!n)return;let s;s="checkbox"===n.type?n.checked:(n.tagName,n.value);const l=this.model.get?this.model.get(e):this.model[e];try{this.model.save?await this.model.save({[e]:s}):this.model[e]=s,this.exitEditMode(t,e,s),this.emit("cell:save",{row:this,model:this.model,column:e,oldValue:l,newValue:s})}catch(a){console.error("Failed to save cell edit:",a),this.emit("cell:save:error",{row:this,model:this.model,column:e,oldValue:l,newValue:s,error:a}),t.classList.add("saving-error"),setTimeout(()=>t.classList.remove("saving-error"),3e3)}}cancelCellEdit(t,e){const i=t.dataset.originalContent;this.exitEditMode(t,e,null,i),this.emit("cell:cancel",{row:this,model:this.model,column:e})}exitEditMode(t,e,n=null,s=null){const l=t.closest("td").querySelector(".cell-content");if(l){if(null!==n){const t=this.columns.find(t=>t.key===e);let s=n;t&&t.formatter&&"string"==typeof t.formatter&&(s=i.dataFormatter.pipe(n,t.formatter)),l.innerHTML=this.escapeHtml(s)}else s&&(l.innerHTML=s);l.style.display=""}t.remove(),this.editingCells.delete(e)}escapeHtml(t){if(null==t)return"";const e=document.createElement("div");return e.textContent=t,e.innerHTML}select(){super.select(),this.addClass("selected");const t=this.element?.querySelector(".mojo-select-cell");t&&t.classList.add("selected")}deselect(){super.deselect(),this.removeClass("selected");const t=this.element?.querySelector(".mojo-select-cell");t&&t.classList.remove("selected")}}const a={exact:{display:"is",description:"Exact match"},in:{display:"in",description:"Match any of the values (comma-separated)"},not:{display:"is not",description:"Does not match"},not_in:{display:"not in",description:"Does not match any of the values"},gt:{display:">",description:"Greater than"},gte:{display:">=",description:"Greater than or equal to"},lt:{display:"<",description:"Less than"},lte:{display:"<=",description:"Less than or equal to"},contains:{display:"contains",description:"Contains substring (case-sensitive)"},icontains:{display:"contains",description:"Contains substring (case-insensitive)"},startswith:{display:"starts with",description:"Starts with substring (case-sensitive)"},istartswith:{display:"starts with",description:"Starts with substring (case-insensitive)"},endswith:{display:"ends with",description:"Ends with substring (case-sensitive)"},iendswith:{display:"ends with",description:"Ends with substring (case-insensitive)"},isnull:{display:t=>"true"===t||!0===t?"is null":"is not null",description:"Check if value is null or not"},range:{display:"between",description:"Between two values (comma-separated)"}};function o(t){if(!t||"string"!=typeof t)return{field:t,lookup:null};const e=t.split("__");if(1===e.length)return{field:t,lookup:null};const i=e[e.length-1];return a[i]?{field:e.slice(0,-1).join("__"),lookup:i}:{field:t,lookup:null}}function r(t,e,i){if(!t||null==e)return"";const{field:n,lookup:s}=o(t),l=a[s];if(e&&"object"==typeof e&&!Array.isArray(e)){const t=void 0!==e.start&&null!==e.start&&""!==e.start,n=void 0!==e.end&&null!==e.end&&""!==e.end;return t||n?t&&n?`${i} between '${e.start}' and '${e.end}'`:t?`${i} from '${e.start}'`:`${i} until '${e.end}'`:`${i} is '${JSON.stringify(e)}'`}const r=Array.isArray(e)?e.join(","):String(e);if(!s||"exact"===s)return`${i} is '${r}'`;if("in"===s||"not_in"===s){const t=r.split(",").map(t=>t.trim()).filter(t=>t);if(0===t.length)return`${i} ${l.display}`;const e=t.map(t=>`'${t}'`).join(", ");return`${i} ${l.display} ${e}`}if("range"===s){const t=r.split(",").map(t=>t.trim()).filter(t=>t);return 2===t.length?`${i} between '${t[0]}' and '${t[1]}'`:`${i} ${l.display} '${r}'`}return"isnull"===s?`${i} ${"function"==typeof l.display?l.display(r):l.display}`:l?`${i} ${l.display} '${r}'`:`${i} is '${r}'`}const c={LOOKUPS:a,parseFilterKey:o,formatFilterDisplay:r,getLookupDescription:function(t){const e=a[t];return e?e.description:"Exact match"},isValidLookup:function(t){return t&&a.hasOwnProperty(t)},getAvailableLookups:function(){return Object.keys(a)},buildFilterKey:function(t,e=null){return t?e?`${t}__${e}`:t:""}};class TableView extends e.ListView{constructor(t={}){super({className:"table-view-component",itemClass:t.itemClass||TableRow,selectionMode:t.selectable?"multiple":"none",emptyMessage:t.emptyMessage||"No data available",addButtonIcon:t.addButtonIcon||"bi bi-plus-circle",...t}),this.isFullscreen=!1,this.columns=t.columns||[],this.actions=t.actions||null,this.contextMenu=t.contextMenu||null,this.batchActions=t.batchActions||null,this.searchable=!1!==t.searchable,this.sortable=!1!==t.sortable,this.filterable=!1!==t.filterable,this.paginated=!1!==t.paginated,this.clickAction=t.clickAction||"view",this.itemView=t.itemView,this.addForm=t.addForm,this.editForm=t.editForm,this.deleteTemplate=t.deleteTemplate,this.formDialogConfig=t.formDialogConfig||{},this.viewDialogOptions=t.viewDialogOptions||{},this.exportOptions=t.exportOptions||null,this.options.showExport&&!this.exportOptions&&(this.exportOptions=[{format:"csv",label:"Export as CSV",icon:"bi bi-file-earmark-spreadsheet"},{format:"json",label:"Export as JSON",icon:"bi bi-file-earmark-code"}]),this.exportSource=t.exportSource||"remote",this.filters={},this.additionalFilters=t.filters||[],this.hideActivePills=t.hideActivePills||!1,this.hideActivePillNames=t.hideActivePillNames||[],this.rowAction=t.rowAction||"row-click",this.batchBarLocation=t.batchBarLocation||"bottom",this.options.addButtonLabel=t.addButtonLabel||"Add",this.toolbarButtons=t.toolbarButtons||[],this.tableOptions={striped:!0,bordered:!1,hover:!0,responsive:!1,size:null,...t.tableOptions},this.searchPlacement=t.searchPlacement||"toolbar",this.searchPlaceholder=t.searchPlaceholder||"Search...",this.initializeColumns(),this.extractColumnFilters(),this.footerTotalColumns=this.columns.filter(t=>!0===t.footer_total),this.hasFooterTotals=this.footerTotalColumns.length>0,this.template=this.buildTableTemplate(),this.setupCollectionListeners()}setupCollectionListeners(){this.hasFooterTotals&&this.collection&&this.collection.on("reset add remove change",()=>{this.updateFooterTotals()})}initializeColumns(){this.columns.forEach(t=>{!t.key&&t.name&&(t.key=t.name),t.label||t.title||(t.label=t.key.charAt(0).toUpperCase()+t.key.slice(1))})}getResponsiveClasses(t){if(!t)return"";const e=["sm","md","lg","xl","xxl"];if("string"==typeof t)return e.includes(t)?`d-none d-${t}-table-cell`:(console.warn(`Invalid visibility breakpoint: ${t}. Valid options are: ${e.join(", ")}`),"");if("object"==typeof t){const i=[];if(t.hide){if(!e.includes(t.hide))return console.warn(`Invalid hide breakpoint: ${t.hide}. Valid options are: ${e.join(", ")}`),"";i.push(`d-table-cell d-${t.hide}-none`)}if(t.show){if(!e.includes(t.show))return console.warn(`Invalid show breakpoint: ${t.show}. Valid options are: ${e.join(", ")}`),"";t.hide?i.push(`d-${t.show}-table-cell`):i.push(`d-none d-${t.show}-table-cell`)}return i.join(" ")}return""}parseColumnKey(t){const e=t.split("|");return{fieldKey:e[0],formatter:e[1]||null}}updateFooterTotals(){if(!this.hasFooterTotals||!this.element)return;const t=this.calculateFooterTotals();let e=0;this.columns.forEach(i=>{if(i.footer_total){const n=`col_${e}`,s=this.element.querySelector(`[data-total-column="${n}"]`);if(s&&t[n]){const e=this.parseColumnKey(i.key).formatter||i.formatter;let l;l=e&&"string"==typeof e?this.formatValue(t[n].value,e):t[n].value,s.textContent=l}e++}})}formatValue(t,e){try{return i.dataFormatter.pipe(t,e)}catch(n){return console.warn("Error formatting value:",n),t}}calculateFooterTotals(){if(!this.hasFooterTotals||!this.collection||0===this.collection.length)return{};const t={};return this.footerTotalColumns.forEach((e,i)=>{const{fieldKey:n,formatter:s}=this.parseColumnKey(e.key);let l=0;this.collection.forEach(t=>{const e=t.get?t.get(n):t[n],i=parseFloat(e)||0;l+=i}),e.key,this.collection.length,t[`col_${i}`]={value:l,formatter:s||e.formatter,fieldKey:n,originalKey:e.key}}),t}extractColumnFilters(){this.filters={},this.columns.forEach(t=>{if(t.filter){const{fieldKey:e}=this.parseColumnKey(t.key);this.filters[e]=t.filter}})}isSelectable(){return this.batchActions&&this.batchActions.length>0&&"multiple"==this.selectionMode}buildTableTemplate(){const t="top"===this.batchBarLocation?this.buildBatchActionsPanel():"",e="bottom"===this.batchBarLocation?this.buildBatchActionsPanel():"";return`\n <div class="mojo-table-wrapper">\n ${this.buildToolbarTemplate()}\n ${t}\n <div class="table-container"${(()=>{const t=this.tableOptions&&null!=this.tableOptions.fontSize?this.tableOptions.fontSize:this.options&&this.options.fontSize,e="sm"===t?"0.9rem":"xs"===t?"0.8rem":t?String(t):null;return e?` style="font-size: ${e};"`:""})()}>\n {{#loading}}\n <div class="mojo-table-loading d-flex justify-content-center align-items-center py-5">\n <div class="spinner-border" role="status">\n <span class="visually-hidden">Loading...</span>\n </div>\n </div>\n {{/loading}}\n {{^loading}}\n {{#isEmpty}}\n <div class="table-empty text-center py-5">\n <i class="bi bi-inbox fa-2x mb-2 text-muted"></i>\n <p class="text-muted">{{emptyMessage}}</p>\n </div>\n {{/isEmpty}}\n {{^isEmpty}}\n <table class="${this.buildTableClasses()}">\n ${this.buildTableHeaderTemplate()}\n <tbody data-container="items"></tbody>\n ${this.hasFooterTotals?this.buildTableFooterTemplate():""}\n </table>\n {{/isEmpty}}\n {{/loading}}\n </div>\n ${e}\n ${this.buildPaginationTemplate()}\n </div>\n `}buildTableClasses(){let t=["table"];return this.tableOptions.striped&&t.push("table-striped"),this.tableOptions.bordered&&t.push("table-bordered"),this.tableOptions.hover&&t.push("table-hover"),this.tableOptions.responsive&&t.push("table-responsive"),this.tableOptions.background&&t.push(`table-${this.tableOptions.background}`),"sm"===this.tableOptions.size&&t.push("table-sm"),"lg"===this.tableOptions.size&&t.push("table-lg"),t.join(" ")}buildToolbarTemplate(){return this.searchable||this.filterable?`\n <div class="table-action-buttons mb-3">\n <div class="d-flex align-items-center gap-2">\n ${this.buildActionButtonsTemplate()}\n ${this.filterable?this.buildFilterDropdownTemplate():""}\n ${this.searchable&&"toolbar"===this.searchPlacement?this.buildSearchTemplate():""}\n\n </div>\n <div data-container="filter-pills"></div>\n </div>\n `:""}buildActionButtonsTemplate(){let t=[];if(t.push('\n <button class="btn btn-sm btn-outline-secondary btn-refresh"\n data-action="refresh"\n title="Refresh">\n <i class="bi bi-arrow-clockwise"></i>\n </button>\n '),this.isFullscreenSupported()&&t.push('\n <button class="btn btn-sm btn-outline-secondary btn-fullscreen"\n data-action="toggle-fullscreen"\n title="Toggle Fullscreen">\n <i class="bi bi-fullscreen"></i>\n </button>\n '),this.options.showAdd&&t.push(`\n <button class="btn btn-sm btn-success btn-add"\n data-action="add"\n title="${this.options.addButtonLabel}">\n <i class="${this.options.addButtonIcon} me-1"></i>\n <span class="d-none d-lg-inline">${this.options.addButtonLabel}</span>\n </button>\n `),this.options.showExport)if(this.exportOptions&&this.exportOptions.length>1){const e=this.exportOptions.map(t=>`\n <li>\n <a class="dropdown-item" href="#" data-action="export" data-format="${t.format}">\n <i class="${t.icon||"bi bi-file-earmark-arrow-down"} me-2"></i>${t.label}\n </a>\n </li>\n `).join("");t.push(`\n <div class="dropdown">\n <button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button"\n data-bs-toggle="dropdown" aria-expanded="false" title="Export">\n <i class="bi bi-download me-1"></i>\n <span class="d-none d-lg-inline">Export</span>\n </button>\n <ul class="dropdown-menu">\n ${e}\n </ul>\n </div>\n `)}else{const e=this.exportOptions&&1===this.exportOptions.length?this.exportOptions[0].format:"json";t.push(`\n <button class="btn btn-sm btn-outline-secondary btn-export"\n data-action="export"\n data-format="${e}"\n title="Export">\n <i class="bi bi-download me-1"></i>\n <span class="d-none d-lg-inline">Export</span>\n </button>\n `)}return this.toolbarButtons&&this.toolbarButtons.length>0&&this.toolbarButtons.forEach((e,i)=>{const{label:n="Button",icon:s="",action:l="",handler:a=null,variant:o="outline-secondary",title:r=n,className:c="",permissions:d=null}=e;if(d&&!this.checkPermissions(d))return;const h=s?`<i class="${s} me-1"></i>`:"",u=`<span class="d-none d-lg-inline">${n}</span>`;let m="";a?m=`data-action="custom-toolbar-button" data-button-index="${i}"`:l&&(m=`data-action="${l}"`);const p=`btn btn-sm btn-${o} ${c}`.trim();t.push(`\n <button class="${p}"\n ${m}\n title="${r}">\n ${h}${u}\n </button>\n `)}),t.join("")}buildSearchTemplate(){return'\n <div class="flex-grow-1" style="max-width: 400px;">\n <div class="input-group input-group-sm">\n <span class="input-group-text">\n <i class="bi bi-search"></i>\n </span>\n <input type="search"\n class="form-control"\n placeholder="{{searchPlaceholder}}"\n data-filter="search"\n data-change-action="apply-search"\n value="{{collection.params.search}}"\n aria-label="Search">\n {{#searchValue}}\n <button class="btn btn-outline-secondary" type="button"\n data-action="clear-search"\n title="Clear search">\n <i class="bi bi-x"></i>\n </button>\n {{/searchValue}}\n </div>\n </div>\n '}buildFilterDropdownTemplate(){return this.filters&&Object.keys(this.filters).length>0||this.additionalFilters&&this.additionalFilters.length>0?`\n <div class="dropdown">\n <button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button"\n data-bs-toggle="dropdown" aria-expanded="false">\n <i class="bi bi-filter me-1"></i>\n <span class="d-none d-lg-inline">Add Filter</span>\n </button>\n <div class="dropdown-menu" style="min-width: 250px;">\n ${this.buildFilterList()}\n </div>\n </div>\n `:""}buildFilterList(){const t=this.getAllAvailableFilters(),e=this.getActiveFilters();return 0===t.length?'<div class="dropdown-item-text text-muted">No filters available</div>':`\n ${t.map(t=>{const i=e.hasOwnProperty(t.key),n=i?"active":"",s=this.getFilterIcon(t.type||t.config?.type);return`\n <button class="dropdown-item ${n}"\n data-action="add-filter"\n data-filter-key="${t.key}">\n <i class="bi bi-${s} me-2"></i>\n ${t.label}\n ${i?'<i class="bi bi-check-circle ms-auto"></i>':""}\n </button>\n `}).join("")}\n ${Object.keys(e).length>0?'\n <div class="dropdown-divider"></div>\n <button class="dropdown-item text-danger" data-action="clear-all-filters">\n <i class="bi bi-x-circle me-2"></i>Clear All Filters\n </button>\n ':""}\n `}updateFilterPills(){const t=this.element?.querySelector('[data-container="filter-pills"]');if(!t)return;this.getActiveFilters();const e=this.buildActivePills();t.innerHTML=e}updateSearchInputs(t){const e=this.element?.querySelectorAll('[data-filter="search"]');e&&e.forEach(e=>{e.value=t||""})}buildActivePills(){if(this.hideActivePills)return"";const t=this.getActiveFilters(),e=t.search&&""!==t.search.toString().trim();let i=Object.entries(t).filter(([t,e])=>e&&""!==e.toString().trim()&&"search"!==t);return this.hideActivePillNames&&this.hideActivePillNames.length>0&&(i=i.filter(([t])=>!this.hideActivePillNames.includes(t))),0!==i.length||e?`\n <div class="row mt-2">\n <div class="col-12">\n <div class="d-flex flex-wrap align-items-center">\n ${i.map(([t,e])=>{const{field:i}=o(t);return`\n <span class="badge bg-primary me-1 mb-1 py-1 px-2 position-relative" style="font-size: 0.75rem;">\n <i class="bi bi-filter me-1" style="font-size: 0.65rem;"></i>\n\n <button type="button" class="btn btn-link text-white p-0 ms-1"\n style="font-size: 0.65rem; line-height: 1;"\n data-action="edit-filter"\n data-filter="${t}"\n title="Edit filter">\n ${r(t,e,this.getFilterLabel(i))}\n </button>\n\n <button type="button" class="btn-close btn-close-white ms-1"\n style="font-size: 0.6rem; width: 0.5rem; height: 0.5rem;"\n data-action="remove-filter"\n data-filter="${t}"\n title="Remove filter">\n </button>\n </span>\n `}).join("")}\n ${i.length>1||i.length>0&&e||0===i.length&&e?'\n <button class="btn btn-sm btn-outline-secondary mb-1 py-0 px-2" style="font-size: 0.75rem;" data-action="clear-all-filters">\n <i class="bi bi-x-circle me-1" style="font-size: 0.7rem;"></i>\n <small>Clear All</small>\n </button>\n ':""}\n </div>\n </div>\n </div>\n `:""}buildTableHeaderTemplate(){let t="";return this.isSelectable()&&(t+='\n <th style="width: 40px; padding: 0;">\n <div class="mojo-select-all-cell" data-action="select-all">\n <div class="mojo-checkbox">\n <i class="bi bi-check"></i>\n </div>\n </div>\n </th>\n '),this.columns.forEach(e=>{const{fieldKey:i}=this.parseColumnKey(e.key),n=this.sortable&&!1!==e.sortable,s=this.getSortBy()===i?this.getSortDirection():null,l=this.getSortIcon(s),a=e.label||e.title||i,o=this.getResponsiveClasses(e.visibility);t+=`\n <th class="${n?"sortable":""} ${o}">\n <div class="d-flex align-items-center">\n <span>${a}</span>\n ${n?`\n <div class="dropdown d-inline-block ms-2">\n <button class="btn btn-sm btn-link p-0 text-decoration-none" type="button"\n data-bs-toggle="dropdown" aria-expanded="false"\n data-column="${i}">\n ${l}\n </button>\n <ul class="dropdown-menu dropdown-menu-end">\n <li><a class="dropdown-item ${"asc"===s?"active":""}"\n data-action="sort" data-field="${i}" data-direction="asc">\n <i class="bi bi-sort-alpha-down me-2"></i>Sort A-Z\n </a></li>\n <li><a class="dropdown-item ${"desc"===s?"active":""}"\n data-action="sort" data-field="${i}" data-direction="desc">\n <i class="bi bi-sort-alpha-down-alt me-2"></i>Sort Z-A\n </a></li>\n <li><a class="dropdown-item ${null===s?"active":""}"\n data-action="sort" data-field="${i}" data-direction="none">\n <i class="bi bi-x-circle me-2"></i>No Sort\n </a></li>\n </ul>\n </div>\n `:""}\n </div>\n </th>\n `}),this.actions?t+="<th>Actions</th>":this.contextMenu&&(t+='<th style="width: 1px;"></th>'),`\n <thead>\n <tr>\n ${t}\n </tr>\n </thead>\n `}buildTableFooterTemplate(){let t="";this.isSelectable()&&(t+="<td></td>");let e=0;return this.columns.forEach((i,n)=>{const s=this.getResponsiveClasses(i.visibility);if(i.footer_total){const n=`col_${e}`,l=this.parseColumnKey(i.key).formatter||i.formatter;let a;a=l&&"string"==typeof l?`{{{footerTotals.${n}.value|${l}}}}`:`{{footerTotals.${n}.value}}`,t+=`<td class="table-footer-total ${s}" data-total-column="${n}">${a}</td>`,e++}else t+=0===n?`<td class="table-footer-label ${s}"><strong>Totals</strong></td>`:`<td class="${s}"></td>`}),(this.actions||this.contextMenu)&&(t+="<td></td>"),`\n <tfoot>\n <tr class="table-totals-row">\n ${t}\n </tr>\n </tfoot>\n `}buildBatchActionsPanel(){if(!this.batchActions||0===this.batchActions.length)return"";if("top"===this.batchBarLocation){let t="";return this.batchActions.forEach(e=>{t+=`\n <button class="btn btn-sm btn-outline-secondary" data-action="batch-${e.action}" title="${e.label}">\n <i class="${e.icon} me-1"></i>\n <span class="d-none d-lg-inline">${e.label}</span>\n </button>\n `}),`\n <div class="batch-actions-panel-top alert alert-info d-none mb-3" role="alert">\n <div class="d-flex justify-content-between align-items-center">\n <div class="d-flex align-items-center">\n <strong class="me-2">\n <span class="batch-select-count">0</span> ${this.options.batchPanelTitle||"items"} selected\n </strong>\n </div>\n <div class="d-flex gap-2 align-items-center">\n ${t}\n <button class="btn btn-sm btn-outline-secondary" data-action="clear-selection" title="Clear Selection">\n <i class="bi bi-x-circle me-1"></i>\n <span class="d-none d-lg-inline">Clear</span>\n </button>\n </div>\n </div>\n </div>\n `}{let t="";return this.batchActions.forEach(e=>{t+=`\n <div class="batch-select-action text-center px-2" data-action="batch-${e.action}">\n <div class="batch-action-icon fs-3">\n <i class="${e.icon}"></i>\n </div>\n <div class="batch-action-title small">${e.label}</div>\n </div>\n `}),`\n <div class="batch-actions-panel rounded-start rounded-end" style="display: none;">\n <div class="batch-select-panel rounded-start rounded-end">\n <div class="row g-0">\n <div class="col-auto">\n <div class="batch-select-count rounded-start">0</div>\n </div>\n <div class="col">\n <div class="ps-2 batch-select-title">${this.options.batchPanelTitle||"Rows"}</div>\n </div>\n <div class="col">\n <div class="batch-select-actions d-flex justify-content-end">\n ${t}\n </div>\n </div>\n <div class="col-auto">\n <div class="batch-select-end rounded-end"></div>\n </div>\n </div>\n </div>\n </div>\n `}}buildPaginationTemplate(){return this.paginated?'\n <div class="table-status-bar mt-3">\n <div class="d-flex flex-column flex-lg-row justify-content-center justify-content-lg-between align-items-center gap-3">\n <div class="d-flex flex-column flex-sm-row align-items-center gap-2 gap-sm-3 text-center text-lg-start">\n <span class="text-muted">\n Showing <span data-value="start">0</span> to <span data-value="end">0</span>\n of <span data-value="total">0</span> entries\n </span>\n <div class="d-flex align-items-center">\n <label class="form-label me-2 mb-0">Show:</label>\n <select class="form-select form-select-sm" style="width: auto;" data-change-action="page-size">\n <option value="5">5</option>\n <option value="10">10</option>\n <option value="25">25</option>\n <option value="50">50</option>\n <option value="100">100</option>\n </select>\n </div>\n </div>\n <nav aria-label="Table pagination">\n <ul class="pagination pagination-sm mb-0 justify-content-center" data-container="pagination">\n \x3c!-- Pagination will be rendered here --\x3e\n </ul>\n </nav>\n </div>\n </div>\n ':""}_createItemView(t,e){const i=new this.itemClass({model:t,index:e,listView:this,tableView:this,template:this.itemTemplate,columns:this.columns,actions:this.actions,contextMenu:this.contextMenu,batchActions:this.batchActions,containerId:"items"});return this.itemViews.set(t.id,i),i.on("item:select",t=>{this._onItemSelect(t),this.updateBatchActionsPanel()}),i.on("item:deselect",t=>{this._onItemDeselect(t),this.updateBatchActionsPanel()}),i.on("row:click",this._onRowClick.bind(this)),i.on("row:view",this._onRowView.bind(this)),i.on("row:edit",this._onRowEdit.bind(this)),i.on("row:delete",this._onRowDelete.bind(this)),i.on("cell:edit",this._onCellEdit.bind(this)),i.on("cell:save",this._onCellSave.bind(this)),i.on("cell:cancel",this._onCellCancel.bind(this)),i}async onMounted(){await super.onMounted();const t=this.getActiveFilters();this.collection&&Object.keys(t).length>0&&this.updateFilterPills(),this.setupSearchClearListener()}setupSearchClearListener(){this.element&&this.element.querySelectorAll('input[type="search"][data-filter="search"]').forEach(t=>{t.addEventListener("input",t=>{""===t.target.value&&this.getActiveFilters().search&&this.onActionClearSearch(t,t.target)})})}_onRowClick(t){if(this.emit("row:click",t),this.options.onRowClick)return this.options.onRowClick(t.model,t.event);"view"===this.clickAction?this._onRowView(t):"edit"===this.clickAction&&this._onRowEdit(t)}getModelClass(t){return this.collection?.ModelClass?this.collection.ModelClass:this.collection?.model?this.collection.model:t?.constructor?t.constructor:null}getModelName(t){const e=this.getModelClass(t);return e&&(e.MODEL_NAME||e.name.replace(/Model$/,""))||"Item"}getItemViewClass(t){if(this.itemView)return this.itemView;const e=this.getModelClass(t);return e?.VIEW_CLASS?e.VIEW_CLASS:null}getAddFormConfig(t){return this.addForm||t?.ADD_FORM||this.editForm||t?.EDIT_FORM}getEditFormConfig(t){return this.editForm||t?.EDIT_FORM||this.addForm||t?.ADD_FORM}getFormDialogConfig(t){return{...t?.FORM_DIALOG_CONFIG,...this.formDialogConfig}}renderTemplateString(t,e){return t?i.Mustache.render(t,e):""}async _onRowView(t){if(this.emit("row:view",t),this.options.onItemView)return void(await this.options.onItemView(t.model,t.event));const e=this.getItemViewClass(t.model);if(e){const i=new e({model:t.model,collection:this.collection});await n.default.showDialog({header:!1,body:i,size:"lg",centered:!1,...this.getFormDialogConfig(this.getModelClass(t.model)),...this.viewDialogOptions})}else await n.default.showData({title:`View ${this.getModelName(t.model)} #${t.model.id}`,model:t.model})}async _onRowEdit(t){if(this.emit("row:edit",t),this.options.onItemEdit)return void(await this.options.onItemEdit(t.model,t.event));const e=this.getModelClass(t.model);let i=this.getEditFormConfig(e);if(i){i.fields||(i={title:`Edit ${this.getModelName(t.model)}`,fields:i});const s=await n.default.showModelForm({model:t.model,...i,...this.getFormDialogConfig(e)});if(!s)return;if(!s.success||!s?.result?.data.status)return void n.default.showError(s?.result?.data?.error||s?.result?.message||"An error occurred")}else{const e=await n.default.showDialog({title:`Edit ${this.getModelName(t.model)} #${t.model.id}`,body:new s.FormView({model:t.model,fields:this.options.formFields||[]})});if(e){const i=await t.model.save(e);if(!i.data?.status)return void n.default.showError(i.data.error||"An error occurred");await this.refresh()}}}async _onRowDelete(t){if(this.emit("row:delete",t),this.options.onItemDelete)return void(await this.options.onItemDelete(t.model,t.event));const e=this.getModelClass(t.model),i=this.deleteTemplate||e?.DELETE_TEMPLATE||'Are you sure you want to delete this {{name||"item"}}?',s=this.renderTemplateString(i,t.model);await n.default.confirm({message:s||"Are you sure you want to delete this item?",title:"Confirm Delete",confirmText:"Delete",confirmClass:"btn-danger"})&&(await t.model.destroy(),this.collection.fetch())}_onCellEdit(t){this.emit("cell:edit",t)}async _onCellSave(t){this.emit("cell:save",t)}_onCellCancel(t){this.emit("cell:cancel",t)}isFullscreenSupported(){return!!(document.fullscreenEnabled||document.mozFullScreenEnabled||document.webkitFullscreenEnabled||document.msFullscreenEnabled)}async onActionToggleFullscreen(t,e){this.isFullscreen?await this.exitFullscreen():await this.enterFullscreen()}async enterFullscreen(){try{this.element.requestFullscreen?await this.element.requestFullscreen():this.element.mozRequestFullScreen?await this.element.mozRequestFullScreen():this.element.webkitRequestFullscreen?await this.element.webkitRequestFullscreen():this.element.msRequestFullscreen&&await this.element.msRequestFullscreen(),this.isFullscreen=!0,this.element.classList.add("table-fullscreen"),this.updateFullscreenButton(),this.setupFullscreenListeners(),this.emit("table:fullscreen:enter")}catch(t){console.warn("Could not enter fullscreen:",t)}}async exitFullscreen(){try{document.exitFullscreen?await document.exitFullscreen():document.mozCancelFullScreen?await document.mozCancelFullScreen():document.webkitExitFullscreen?await document.webkitExitFullscreen():document.msExitFullscreen&&await document.msExitFullscreen(),this.isFullscreen=!1,this.element.classList.remove("table-fullscreen"),this.updateFullscreenButton(),this.emit("table:fullscreen:exit")}catch(t){console.warn("Could not exit fullscreen:",t)}}updateFullscreenButton(){const t=this.element?.querySelector(".btn-fullscreen"),e=t?.querySelector("i");t&&e&&(this.isFullscreen?(e.className="bi bi-fullscreen-exit",t.title="Exit Fullscreen"):(e.className="bi bi-fullscreen",t.title="Enter Fullscreen"))}setupFullscreenListeners(){if(this._fullscreenHandler)return;const t=()=>{!(document.fullscreenElement||document.mozFullScreenElement||document.webkitFullscreenElement||document.msFullscreenElement)&&this.isFullscreen&&(this.isFullscreen=!1,this.element.classList.remove("table-fullscreen"),this.updateFullscreenButton(),this.emit("table:fullscreen:exit"))};document.addEventListener("fullscreenchange",t),document.addEventListener("mozfullscreenchange",t),document.addEventListener("webkitfullscreenchange",t),document.addEventListener("msfullscreenchange",t),this._fullscreenHandler=t}cleanupFullscreenListeners(){this._fullscreenHandler&&(document.removeEventListener("fullscreenchange",this._fullscreenHandler),document.removeEventListener("mozfullscreenchange",this._fullscreenHandler),document.removeEventListener("webkitfullscreenchange",this._fullscreenHandler),document.removeEventListener("msfullscreenchange",this._fullscreenHandler),this._fullscreenHandler=null)}destroy(){this.cleanupFullscreenListeners(),super.destroy()}async onActionRefresh(t,e){await this.refresh()}async onActionAdd(t,e){if(this.options.onAdd)return this.emit("table:add",{event:t}),void(await this.options.onAdd(t));this.emit("table:add",{event:t});const i=this.getModelClass();if(!i)return void console.warn("Cannot determine Model class for add operation");let l=this.getAddFormConfig(i);if(l){const t=new i;l.fields||(l={title:`Add ${this.getModelName()}`,fields:l});const e=await n.default.showForm({model:t,...l,...this.getFormDialogConfig(i)});if(e){this.options.addRequiresActiveGroup&&(e.group=this.getApp().activeGroup.id),this.options.addRequiresActiveUser&&(e.user=this.getApp().activeUser.id),this.options.addFormDefaults&&Object.assign(e,this.options.addFormDefaults);const i=await t.save(e);if(!i?.data.status)return void n.default.showError(i?.data.error||"An error occurred");this.collection&&this.collection.add(t),await this.refresh()}}else{const t=new i,e=await n.default.showDialog({title:`Add ${this.getModelName()}`,body:new s.FormView({model:t,fields:this.options.formFields||[]})});if(e){const i=await t.save(e);if(!i?.data.status)return void n.default.showError(i.data.error||"An error occurred");this.collection&&this.collection.add(t),await this.refresh()}}}async onActionExport(t,e){const i=e.getAttribute("data-format")||"json";this.emit("table:export",{format:i,source:this.exportSource,event:t}),"remote"===this.exportSource?this.collection?await this.collection.download(i):console.warn("TableView: Cannot export from remote without a collection."):this.options.onExport?await this.options.onExport(this.collection?.toJSON()||[],i):console.warn("TableView: onExport handler not implemented for local export.")}async onActionApplySearch(t,e){const i=e.value.trim();this.collection&&(this.setFilter("search",i),this.collection.params.start=0,this.collection.restEnabled?await this.collection.fetch():this.render()),this.updateFilterPills(),this.emit("table:search",{searchTerm:i,event:t}),this.emit("params-changed")}async onActionClearSearch(t,e){this.setFilter("search",null),this.collection&&(this.collection.params.start=0,this.collection.restEnabled&&await this.collection.fetch()),await this.render(),this.updateFilterPills(),this.emit("table:search",{searchTerm:"",event:t}),this.emit("params-changed")}getSortBy(){const t=this.collection?.params?.sort;return t?t.startsWith("-")?t.slice(1):t:null}getSortDirection(){const t=this.collection?.params?.sort;return t&&t.startsWith("-")?"desc":"asc"}getSortIcon(t){return"asc"===t?'<i class="bi bi-sort-alpha-down text-primary"></i>':"desc"===t?'<i class="bi bi-sort-alpha-down-alt text-primary"></i>':'<i class="bi bi-three-dots-vertical text-muted"></i>'}async onActionSort(t,e){t.preventDefault();const i=e.getAttribute("data-field"),n=e.getAttribute("data-direction");if(this.collection){let t;if(t="none"===n?void 0:"desc"===n?`-${i}`:i,this.collection.setParams({...this.collection.params,sort:t,start:0}),this.collection.restEnabled)await this.collection.fetch();else{if(t){const e=t.startsWith("-"),i=e?t.slice(1):t;this.collection.sort((t,n)=>{const s=t.get(i),l=n.get(i);return s<l?e?1:-1:s>l?e?-1:1:0})}this.render()}}this.updateSortIcons(),this.emit("table:sort",{field:i,event:t}),this.emit("params-changed")}updateSortIcons(){if(!this.element)return;const t=this.getSortBy(),e=this.getSortDirection();this.columns.forEach(i=>{if(this.sortable&&!1!==i.sortable){const{fieldKey:n}=this.parseColumnKey(i.key),s=this.element.querySelector(`[data-bs-toggle="dropdown"][data-column="${n}"]`);if(s){const i=t===n,l=this.getSortIcon(i?e:null);s.innerHTML=l;const a=s.nextElementSibling;if(a){const s=a.querySelector(`[data-field="${n}"][data-direction="asc"]`),l=a.querySelector(`[data-field="${n}"][data-direction="desc"]`),o=a.querySelector(`[data-field="${n}"][data-direction="none"]`);s&&s.classList.toggle("active",i&&"asc"===e),l&&l.classList.toggle("active",i&&"desc"===e),o&&o.classList.toggle("active",!i||t!==n)}}}})}async onActionSelectAll(t,e){t.stopPropagation();const i=this.itemViews.size>0&&Array.from(this.itemViews.values()).every(t=>t.selected);i?this.clearSelection():this.forEachItem(t=>{t.selected||t.select()});const n=this.element?.querySelector(".mojo-select-all-cell");n&&n.classList.toggle("selected",!i),this.updateBatchActionsPanel()}async onBeforeRender(){this.searchValue=this.getActiveFilters().search||"",this.footerTotals=this.calculateFooterTotals()}async onAfterRender(){if(await super.onAfterRender(),this.hasFooterTotals&&this.updateFooterTotals(),this.paginated&&this.collection){const t=this.collection.meta?.count||this.collection.length(),e=this.collection.params?.start||0,i=this.collection.params?.size||10,n=Math.min(e+i,t),s=this.element.querySelector('[data-value="start"]'),l=this.element.querySelector('[data-value="end"]'),a=this.element.querySelector('[data-value="total"]');s&&(s.textContent=e+1),l&&(l.textContent=n),a&&(a.textContent=t);const o=this.element.querySelector('[data-change-action="page-size"]');o&&(o.value=i),this.renderPagination()}this.updateSortIcons(),this.updateFilterPills(),this.setupSearchClearListener()}renderPagination(){const t=this.element.querySelector('[data-container="pagination"]');if(!t||!this.collection)return;const e=this.collection.meta?.count||this.collection.length(),i=this.collection.params?.size||10,n=this.collection.params?.start||0,s=Math.floor(n/i)+1,l=Math.ceil(e/i);if(l<=1)return void(t.innerHTML="");const a=s>1?s-1:l,o=s<l?s+1:1,r=[];r.push(`\n <li class="page-item">\n <a class="page-link" href="#" data-action="page" data-page="${a}">\n <i class="bi bi-chevron-left"></i>\n </a>\n </li>\n `);const c=/* @__PURE__ */new Set([1,l]);for(let u=s-1;u<=s+1;u++)u>=1&&u<=l&&c.add(u);const d=Array.from(c).sort((t,e)=>t-e);let h=0;for(const u of d)h&&u-h>1&&r.push('\n <li class="page-item disabled"><span class="page-link">…</span></li>\n '),r.push(`\n <li class="page-item ${u===s?"active":""}">\n <a class="page-link" href="#" data-action="page" data-page="${u}">${u}</a>\n </li>\n `),h=u;r.push(`\n <li class="page-item">\n <a class="page-link" href="#" data-action="page" data-page="${o}">\n <i class="bi bi-chevron-right"></i>\n </a>\n </li>\n `),t.innerHTML=r.join("")}async onActionPage(t,e){t.preventDefault();const i=parseInt(e.getAttribute("data-page"),10),n=this.collection.params?.size||10,s=this.collection.meta?.count||this.collection.length(),l=Math.max(1,Math.ceil(s/n));let a=isNaN(i)?1:i;a<1&&(a=l),a>l&&(a=1),this.collection.setParams({...this.collection.params,start:(a-1)*n}),this.collection.restEnabled?await this.collection.fetch():this.render(),this.emit("table:page",{page:a,event:t}),this.emit("params-changed")}async onChangePageSize(t,e){const i=parseInt(e.value);this.collection&&(this.collection.setParams({...this.collection.params,start:0,size:i}),this.collection.restEnabled&&await this.collection.fetch(),this.render()),this.emit("table:pagesize",{size:i,event:t}),this.emit("params-changed")}getActiveFilters(){if(!this.collection?.params)return{};const{start:t,size:e,sort:i,...n}=this.collection.params,s={},l=/* @__PURE__ */new Set;return this.getAllAvailableFilters().forEach(t=>{if("daterange"===t.config.type){const e=t.key,i=t.config.startName||"dr_start",a=t.config.endName||"dr_end",o=t.config.fieldName||"dr_field";n[o]===e&&(n[i]||n[a])&&(s[e]={start:n[i]||"",end:n[a]||""},l.add(i),l.add(a),l.add(o))}}),Object.keys(n).forEach(t=>{l.has(t)||(s[t]=n[t])}),Object.keys(s).forEach(t=>{if(s.hasOwnProperty(t)){const e=`${t}__in`;s.hasOwnProperty(e)&&(delete s[t],s[e]=s[e])}}),s}setFilter(t,e){if(!this.collection)return;const i=this.getFilterConfig(t);if(i&&"daterange"===i.type){const n=i.startName||"dr_start",s=i.endName||"dr_end",l=i.fieldName||"dr_field";delete this.collection.params[n],delete this.collection.params[s],delete this.collection.params[l],e&&"object"==typeof e&&(e.start||e.end)&&(e.start&&(this.collection.params[n]=e.start),e.end&&(this.collection.params[s]=e.end),this.collection.params[l]=t)}else{const{field:i,lookup:n}=o(t);if(delete this.collection.params[t],delete this.collection.params[i],delete this.collection.params[`${i}__in`],!e||Array.isArray(e)&&0===e.length)return;Array.isArray(e)?1===e.length?this.collection.params[i]=e[0]:this.collection.params[`${i}__in`]=e.join(","):this.collection.params[t]=e}}getAllAvailableFilters(){const t=[];return this.columns.forEach(e=>{if(e.filter){const{fieldKey:i}=this.parseColumnKey(e.key);t.push({key:i,label:e.filter.label||e.label||i,type:e.filter.type,config:e.filter})}}),this.additionalFilters&&Array.isArray(this.additionalFilters)&&this.additionalFilters.forEach(e=>{t.push({key:e.name||e.key,label:e.label,type:e.type,config:e})}),t}getFilterConfig(t){const e=this.columns.find(e=>{const{fieldKey:i}=this.parseColumnKey(e.key);return i===t});if(e&&e.filter)return e.filter;if(this.additionalFilters&&Array.isArray(this.additionalFilters)){const e=this.additionalFilters.find(e=>(e.name||e.key)===t);if(e)return e}return null}getFilterLabel(t){if("search"===t)return"Search";const e=this.filters[t];if(e&&e.label)return e.label;const i=this.additionalFilters.find(e=>(e.name||e.key)===t);return i&&i.label?i.label:t.charAt(0).toUpperCase()+t.slice(1)}getFilterDisplayValue(t,e){if("search"===t)return`"${e}"`;const i=this.filters[t]||this.additionalFilters.find(e=>(e.name||e.key)===t);if(i&&"daterange"===i.type&&"object"==typeof e)return`${e.start||""} to ${e.end||""}`;if(i&&"select"===i.type&&i.options){if("object"==typeof i.options[0]){const t=i.options.find(t=>t.value===e);return t?t.label:e}return e}return e}getFilterIcon(t){return{text:"search",select:"funnel",date:"calendar",daterange:"calendar-range",number:"123",boolean:"toggle-on"}[t]||"filter"}async onActionAddFilter(t,e){const i=e.getAttribute("data-filter-key"),s=this.getFilterConfig(i),l=this.getActiveFilters()[i];if(!s)return void console.warn("No filter config found for key:",i);const a=await n.default.showForm({title:`${void 0!==l&&""!==l?"Edit":"Add"} ${this.getFilterLabel(i)} Filter`,size:"md",fields:[this.buildFilterDialogField(s,l,i)]});if(a){const t=this.extractFilterValue(s,a);this.setFilter(i,t),await this.applyFilters()}}buildFilterDialogField(t,e,i){const n={name:"filter_value",label:t.label,value:e,...t,placeholder:t.placeholder||t.placeHolder};if("daterange"===t.type){if(n.startName=n.startName||"dr_start",n.endName=n.endName||"dr_end",n.fieldName=n.fieldName||"dr_field",n.format=n.format||"YYYY-MM-DD",n.displayFormat=n.displayFormat||"MMM DD, YYYY",n.separator=n.separator||" to ",n.label=n.label||"Date Range",e&&"object"==typeof e){const t=t=>{if(!t&&0!==t)return"";if(t instanceof Date&&!isNaN(t))return t.toISOString().slice(0,10);const e=String(t).trim();if(!e)return"";if(/^-?\d+$/.test(e)){const t=Number(e),i=e.length<=10?1e3*t:t,n=new Date(i);if(!isNaN(n))return n.toISOString().slice(0,10)}const i=new Date(e);return isNaN(i)?e:i.toISOString().slice(0,10)};n.startDate=t(e.start||e.from||e.begin||""),n.endDate=t(e.end||e.to||e.finish||"")}}else if("multiselect"===t.type){let i=[];e&&(Array.isArray(e)?i=e:"string"==typeof e&&(i=e.split(",").map(t=>t.trim()).filter(t=>t))),n.value=i,n.placeholder||n.placeHolder||(t.placeholder||t.placeHolder?n.placeholder=t.placeholder||t.placeHolder:t.label&&(n.placeholder=`Select ${t.label}...`))}return n}extractFilterValue(t,e){if("daterange"===t.type){const i=t.startName||"dr_start",n=t.endName||"dr_end";return{start:e[i],end:e[n]}}return t.type,e.filter_value}async applyFilters(){if(this.collection&&(this.collection.params.start=0),this.collection?.restEnabled)try{await this.collection.fetch(),this.render()}catch(t){console.error("Failed to fetch filtered data:",t),this.render()}else this.render();this.updateFilterPills(),this.emit("params-changed")}async onActionEditFilter(t,e){const i=e.getAttribute("data-filter"),{field:s}=o(i);let l=this.getFilterConfig(s)||this.getFilterConfig(i);const a=this.getActiveFilters(),r=a[i]||a[s];if(!l)return void console.warn("No filter config found for key:",i,"or field:",s);const c={filter_value:r};if("daterange"===l.type&&r&&"object"==typeof r){const t=l.startName||"dr_start",e=l.endName||"dr_end";c[t]=r.start||"",c[e]=r.end||""}const d=await n.default.showForm({title:`Edit ${this.getFilterLabel(s)} Filter`,size:"md",data:c,fields:[this.buildFilterDialogField(l,r,s)]});if(d){const t=this.extractFilterValue(l,d);this.setFilter(i,t),await this.applyFilters()}}async onActionRemoveFilter(t,e){const i=e.getAttribute("data-filter"),{field:n}=o(i);this.setFilter(i,null),"search"===i&&this.updateSearchInputs(""),this.collection.restEnabled&&await this.collection.fetch(),this.render(),this.updateFilterPills(),this.emit("filter:remove",{key:i,field:n}),this.emit("params-changed")}async onActionClearAllFilters(t,e){if(!this.collection)return;const{start:i,size:n,sort:s}=this.collection.params;this.collection.params={start:i,size:n},s&&(this.collection.params.sort=s),this.updateSearchInputs(""),this.collection.restEnabled&&await this.collection.fetch(),this.render(),this.updateFilterPills(),this.emit("filters:clear"),this.emit("params-changed")}updateBatchActionsPanel(){if(!this.batchActions||0===this.batchActions.length)return;const t=this.getSelectedItems().length;if("top"===this.batchBarLocation){const e=this.element?.querySelector(".batch-actions-panel-top"),i=this.element?.querySelector(".batch-select-count");e&&i&&(i.textContent=t,t>0?e.classList.remove("d-none"):e.classList.add("d-none"))}else{const e=this.element?.querySelector(".batch-actions-panel"),i=this.element?.querySelector(".batch-select-count");e&&i&&(i.textContent=t,e.style.display=t>0?"block":"none")}const e=this.element?.querySelector(".mojo-select-all-cell");if(e){const t=this.itemViews.size>0&&Array.from(this.itemViews.values()).every(t=>t.selected),i=Array.from(this.itemViews.values()).some(t=>t.selected);e.classList.toggle("selected",t),e.classList.toggle("indeterminate",!t&&i);const n=e.querySelector("i");n&&(n.className=!t&&i?"bi bi-dash":"bi bi-check")}}async onActionBatch(t,e){const i=e.getAttribute("data-action").replace("batch-",""),n=this.getSelectedItems();this.emit("batch:action",{action:i,items:n,event:t})}async onActionClearSelection(t,e){this.clearSelection(),this.updateBatchActionsPanel()}async onActionCustomToolbarButton(t,e){const i=parseInt(e.getAttribute("data-button-index"),10),n=this.toolbarButtons[i];n&&"function"==typeof n.handler&&await n.handler.call(this,t,e)}}exports.DjangoLookups=c,exports.LOOKUPS=a,exports.Log=Log,exports.LogList=LogList,exports.Member=Member,exports.MemberForms=l,exports.MemberList=MemberList,exports.TableRow=TableRow,exports.TableView=TableView,exports.formatFilterDisplay=r,exports.parseFilterKey=o;
2
- //# sourceMappingURL=TableView-CI_7a-kD.js.map
@@ -1,2 +0,0 @@
1
- import{C as t,M as e}from"./Collection-BWKmydl5.js";import{L as i,a as n}from"./ListView-BLFFK_Ir.js";import{d as s,a as l}from"./Rest-BJ3Mvx1L.js";import a from"./Dialog-t_9l2Mou.js";import{F as o}from"./FormView-BRHAIawp.js";class Log extends e{constructor(t={}){super(t,{endpoint:"/api/logs"})}}class LogList extends t{constructor(t={}){super({ModelClass:Log,endpoint:"/api/logs",size:10,...t})}}class Member extends e{constructor(t={}){super(t,{endpoint:"/api/group/member"})}hasPermission(t){if(Array.isArray(t))return t.some(t=>this.hasPermission(t));const e=this.get("permissions");return!!e&&1==e[t]}async fetchForGroup(t){return await this.fetch({url:`/api/group/${t}/member`})}}class MemberList extends t{constructor(t={}){super({ModelClass:Member,endpoint:"/api/group/member",size:10,...t})}}const r={edit:{title:"Edit Membership",fields:[{name:"user.display_name",type:"text",label:"Display Name",placeholder:"Enter Display Name"},{name:"metadata.role",type:"text",label:"Role",placeholder:"Enter role"},{name:"is_active",type:"switch",label:"Is Enabled",columns:12}]}};Member.EDIT_FORM=r.edit,Member.ADD_FORM=r.create;class TableRow extends i{constructor(t={}){super({tagName:"tr",className:"table-row",enableTooltips:!0,...t}),this.columns=t.columns||[],this.actions=t.actions||null,this.contextMenu=t.contextMenu||null,this.batchActions=t.batchActions||null,this.tableView=t.tableView||t.listView||null,this.editingCells=/* @__PURE__ */new Set,this.template=this.buildRowTemplate()}getResponsiveClasses(t){if(!t)return"";const e=["sm","md","lg","xl","xxl"];if("string"==typeof t)return e.includes(t)?`d-none d-${t}-table-cell`:(console.warn(`Invalid visibility breakpoint: ${t}. Valid options are: ${e.join(", ")}`),"");if("object"==typeof t){const i=[];if(t.hide){if(!e.includes(t.hide))return console.warn(`Invalid hide breakpoint: ${t.hide}. Valid options are: ${e.join(", ")}`),"";i.push(`d-table-cell d-${t.hide}-none`)}if(t.show){if(!e.includes(t.show))return console.warn(`Invalid show breakpoint: ${t.show}. Valid options are: ${e.join(", ")}`),"";t.hide?i.push(`d-${t.show}-table-cell`):i.push(`d-none d-${t.show}-table-cell`)}return i.join(" ")}return""}buildRowTemplate(){let t="";return this.tableView&&this.tableView.isSelectable()&&(t+='\n <td style="padding: 0;">\n <div class="mojo-select-cell {{#selected}}selected{{/selected}}"\n data-action="select">\n <div class="mojo-checkbox">\n <i class="bi bi-check"></i>\n </div>\n </div>\n </td>\n '),this.columns.forEach((e,i)=>{const n=[e.class||e.className||"",this.getResponsiveClasses(e.visibility),e.editable?"editable-cell":""].filter(t=>t).join(" "),s=this.buildCellTemplate(e,i);let l=e.action;!l&&e.editable?l="edit-cell":!l&&this.tableView.rowAction&&(l=this.tableView.rowAction),t+=l?`<td class="${n}" data-action="${l}" data-column="${e.key}">${s}</td>`:`<td class="${n}" data-column="${e.key}">${s}</td>`}),this.actions?t+=this.buildActionsTemplate():this.contextMenu&&(t+=this.buildContextMenuTemplate()),t}buildCellTemplate(t,e=0){const i=`model.${t.key}`,n=t.formatter||t.format;if(n){if("string"==typeof n)return`{{{${i}|${n}}}}`;if("function"==typeof n)return`<span data-formatter="${t.key}" data-formatter-id="${e}">{{${i}}}</span>`}return t.template?t.template:t.editable?`<span class="cell-content" data-field="${t.key}">{{{${i}}}}</span>`:`{{{${i}}}}`}buildActionsTemplate(){return this.actions&&0!==this.actions.length?`<td><div class="btn-group btn-group-sm">${this.actions.map(t=>{if("string"==typeof t)switch(t){case"view":return'\n <button class="btn btn-sm btn-outline-primary"\n data-action="view"\n title="View">\n <i class="bi bi-eye"></i>\n </button>\n ';case"edit":return'\n <button class="btn btn-sm btn-outline-secondary"\n data-action="edit"\n title="Edit">\n <i class="bi bi-pencil"></i>\n </button>\n ';case"delete":return'\n <button class="btn btn-sm btn-outline-danger"\n data-action="delete"\n title="Delete">\n <i class="bi bi-trash"></i>\n </button>\n ';default:return""}else if("object"==typeof t)return`\n <button class="btn btn-sm ${t.class||"btn-outline-primary"}"\n data-id="${this.model.id}"\n data-action="${t.action}"\n title="${t.label||""}">\n ${t.icon?`<i class="${t.icon}"></i>`:""}\n ${t.label&&!t.icon?t.label:""}\n </button>\n `;return""}).join("")}</div></td>`:""}buildContextMenuTemplate(){return this.contextMenu&&0!==this.contextMenu.length?`\n <td class="text-end" style="width: 1px;">\n <div class="dropdown">\n <button class="btn btn-sm btn-link border-0"\n type="button"\n data-bs-toggle="dropdown"\n aria-expanded="false"\n style="color: #6c757d;">\n <i class="bi bi-three-dots-vertical"></i>\n </button>\n <ul class="dropdown-menu dropdown-menu-end shadow-sm">\n ${this.buildContextMenuItems()}\n </ul>\n </div>\n </td>\n `:""}buildContextMenuItems(){return this.contextMenu.map(t=>{if(t.separator||t.divider)return'<li><hr class="dropdown-divider"></li>';let e="dropdown-item";return("delete"===t.action||t.danger)&&(e+=" text-danger"),t.disabled&&(e+=" disabled"),`\n <li>\n <a class="${e}" href="#"\n data-id="{{model.id}}"\n data-action="${t.action}"\n ${t.disabled?'aria-disabled="true" tabindex="-1"':""}>\n ${t.icon?`<i class="${t.icon} me-2"></i>`:""}\n ${t.label}\n </a>\n </li>\n `}).join("")}async onAfterRender(){await super.onAfterRender(),this.columns.forEach((t,e)=>{if(t.formatter&&"function"==typeof t.formatter){let n=this.element.querySelector(`[data-formatter-id="${e}"]`);if(n||(n=this.element.querySelector(`[data-formatter="${t.key}"]`)),n){const e=this.model.get?this.model.get(t.key):this.model[t.key],s={value:e,row:this.model,model:this.model,column:t,table:this.tableView,index:this.index};try{n.innerHTML=t.formatter(e,s)}catch(i){console.error(`Error formatting cell for column ${t.key}:`,i)}}}}),this.selected&&this.element.classList.add("selected");const t=this.model.get?this.model.get("id"):this.model.id;t&&this.element.setAttribute("data-id",t)}async onActionEditCell(t,e){t.stopPropagation();const i=e.getAttribute("data-column"),n=this.columns.find(t=>t.key===i);n&&n.editable&&(this.editingCells.has(i)||await this.enterEditMode(i,n,e))}async onActionRowClick(t,e){t.target.closest(".btn-group")||t.target.closest(".dropdown")||t.target.closest(".cell-editor")||(this.emit("row:click",{row:this,model:this.model,column:e.getAttribute("data-column"),event:t}),this.tableView&&this.tableView.emit("row:click",{row:this,model:this.model,column:e.getAttribute("data-column"),event:t}))}async onActionView(t,e){t.stopPropagation(),this.emit("row:view",{row:this,model:this.model,event:t}),this.tableView&&this.tableView.emit("row:view",{row:this,model:this.model,event:t})}async onActionEdit(t,e){return t.stopPropagation(),this.emit("row:edit",{row:this,model:this.model,event:t}),this.tableView&&this.tableView.emit("row:edit",{row:this,model:this.model,event:t}),!0}async onActionDelete(t,e){t.stopPropagation(),this.emit("row:delete",{row:this,model:this.model,event:t}),this.tableView&&this.tableView.emit("row:delete",{row:this,model:this.model,event:t})}async enterEditMode(t,e,i){const n=i.querySelector(".cell-content");if(!n)return;this.editingCells.add(t);const s=this.model.get?this.model.get(t):this.model[t],l=this.createCellEditor(e,s),a=n.innerHTML;n.style.display="none";const o=document.createElement("div");o.className="cell-editor",o.innerHTML=l,i.appendChild(o);const r=o.querySelector("input, select, .form-check-input");r&&(r.focus(),"text"!==r.type&&"textarea"!==r.type||r.select()),o.dataset.originalContent=a,o.dataset.columnKey=t,this.setupEditorEvents(o,t,e),this.emit("cell:edit",{row:this,model:this.model,column:t,originalValue:s})}createCellEditor(t,e){const i=t.editableOptions||{};switch(i.type){case"select":return this.createSelectEditor(i,e);case"switch":case"checkbox":return this.createSwitchEditor(i,e);case"textarea":return this.createTextareaEditor(i,e);default:return this.createTextEditor(i,e)}}createTextEditor(t,e){const i=t.placeholder||"";return`\n <div class="d-flex gap-1 align-items-center">\n <input type="${t.inputType||"text"}"\n class="form-control form-control-sm cell-input"\n value="${this.escapeHtml(e||"")}"\n placeholder="${i}">\n <button type="button" class="btn btn-sm btn-success cell-save" title="Save">\n <i class="bi bi-check"></i>\n </button>\n <button type="button" class="btn btn-sm btn-outline-secondary cell-cancel" title="Cancel">\n <i class="bi bi-x"></i>\n </button>\n </div>\n `}createTextareaEditor(t,e){const i=t.placeholder||"";return`\n <div class="d-flex gap-1">\n <textarea class="form-control form-control-sm cell-input"\n rows="${t.rows||2}"\n placeholder="${i}">${this.escapeHtml(e||"")}</textarea>\n <div class="d-flex flex-column gap-1">\n <button type="button" class="btn btn-sm btn-success cell-save" title="Save">\n <i class="bi bi-check"></i>\n </button>\n <button type="button" class="btn btn-sm btn-outline-secondary cell-cancel" title="Cancel">\n <i class="bi bi-x"></i>\n </button>\n </div>\n </div>\n `}createSelectEditor(t,e){const i=t.options||[];let n="";return i.forEach(t=>{if("string"==typeof t)n+=`<option value="${t}" ${t===e?"selected":""}>${t}</option>`;else if("object"==typeof t&&void 0!==t.value){const i=t.value===e?"selected":"";n+=`<option value="${t.value}" ${i}>${t.label||t.value}</option>`}}),`\n <div class="d-flex gap-1 align-items-center">\n <select class="form-select form-select-sm cell-input">\n ${n}\n </select>\n <button type="button" class="btn btn-sm btn-success cell-save" title="Save">\n <i class="bi bi-check"></i>\n </button>\n <button type="button" class="btn btn-sm btn-outline-secondary cell-cancel" title="Cancel">\n <i class="bi bi-x"></i>\n </button>\n </div>\n `}createSwitchEditor(t,e){const i=e?"checked":"";return`\n <div class="d-flex gap-2 align-items-center">\n <div class="form-check ${"switch"===t.type?"form-switch":""}">\n <input class="form-check-input cell-input" type="checkbox" ${i}>\n </div>\n <div class="d-flex gap-1">\n <button type="button" class="btn btn-sm btn-success cell-save" title="Save">\n <i class="bi bi-check"></i>\n </button>\n <button type="button" class="btn btn-sm btn-outline-secondary cell-cancel" title="Cancel">\n <i class="bi bi-x"></i>\n </button>\n </div>\n </div>\n `}setupEditorEvents(t,e,i){const n=t.querySelector(".cell-input"),s=t.querySelector(".cell-save"),l=t.querySelector(".cell-cancel");!n||"text"!==n.type&&"email"!==n.type&&"number"!==n.type||n.addEventListener("keydown",n=>{"Enter"===n.key?(n.preventDefault(),this.saveCellEdit(t,e,i)):"Escape"===n.key&&(n.preventDefault(),this.cancelCellEdit(t,e))}),!n||"checkbox"!==n.type&&"SELECT"!==n.tagName||!1===i.autoSave||n.addEventListener("change",()=>{this.saveCellEdit(t,e,i)}),s?.addEventListener("click",()=>{this.saveCellEdit(t,e,i)}),l?.addEventListener("click",()=>{this.cancelCellEdit(t,e)})}async saveCellEdit(t,e,i){const n=t.querySelector(".cell-input");if(!n)return;let s;s="checkbox"===n.type?n.checked:(n.tagName,n.value);const l=this.model.get?this.model.get(e):this.model[e];try{this.model.save?await this.model.save({[e]:s}):this.model[e]=s,this.exitEditMode(t,e,s),this.emit("cell:save",{row:this,model:this.model,column:e,oldValue:l,newValue:s})}catch(a){console.error("Failed to save cell edit:",a),this.emit("cell:save:error",{row:this,model:this.model,column:e,oldValue:l,newValue:s,error:a}),t.classList.add("saving-error"),setTimeout(()=>t.classList.remove("saving-error"),3e3)}}cancelCellEdit(t,e){const i=t.dataset.originalContent;this.exitEditMode(t,e,null,i),this.emit("cell:cancel",{row:this,model:this.model,column:e})}exitEditMode(t,e,i=null,n=null){const l=t.closest("td").querySelector(".cell-content");if(l){if(null!==i){const t=this.columns.find(t=>t.key===e);let n=i;t&&t.formatter&&"string"==typeof t.formatter&&(n=s.pipe(i,t.formatter)),l.innerHTML=this.escapeHtml(n)}else n&&(l.innerHTML=n);l.style.display=""}t.remove(),this.editingCells.delete(e)}escapeHtml(t){if(null==t)return"";const e=document.createElement("div");return e.textContent=t,e.innerHTML}select(){super.select(),this.addClass("selected");const t=this.element?.querySelector(".mojo-select-cell");t&&t.classList.add("selected")}deselect(){super.deselect(),this.removeClass("selected");const t=this.element?.querySelector(".mojo-select-cell");t&&t.classList.remove("selected")}}const c={exact:{display:"is",description:"Exact match"},in:{display:"in",description:"Match any of the values (comma-separated)"},not:{display:"is not",description:"Does not match"},not_in:{display:"not in",description:"Does not match any of the values"},gt:{display:">",description:"Greater than"},gte:{display:">=",description:"Greater than or equal to"},lt:{display:"<",description:"Less than"},lte:{display:"<=",description:"Less than or equal to"},contains:{display:"contains",description:"Contains substring (case-sensitive)"},icontains:{display:"contains",description:"Contains substring (case-insensitive)"},startswith:{display:"starts with",description:"Starts with substring (case-sensitive)"},istartswith:{display:"starts with",description:"Starts with substring (case-insensitive)"},endswith:{display:"ends with",description:"Ends with substring (case-sensitive)"},iendswith:{display:"ends with",description:"Ends with substring (case-insensitive)"},isnull:{display:t=>"true"===t||!0===t?"is null":"is not null",description:"Check if value is null or not"},range:{display:"between",description:"Between two values (comma-separated)"}};function d(t){if(!t||"string"!=typeof t)return{field:t,lookup:null};const e=t.split("__");if(1===e.length)return{field:t,lookup:null};const i=e[e.length-1];return c[i]?{field:e.slice(0,-1).join("__"),lookup:i}:{field:t,lookup:null}}function h(t,e,i){if(!t||null==e)return"";const{field:n,lookup:s}=d(t),l=c[s];if(e&&"object"==typeof e&&!Array.isArray(e)){const t=void 0!==e.start&&null!==e.start&&""!==e.start,n=void 0!==e.end&&null!==e.end&&""!==e.end;return t||n?t&&n?`${i} between '${e.start}' and '${e.end}'`:t?`${i} from '${e.start}'`:`${i} until '${e.end}'`:`${i} is '${JSON.stringify(e)}'`}const a=Array.isArray(e)?e.join(","):String(e);if(!s||"exact"===s)return`${i} is '${a}'`;if("in"===s||"not_in"===s){const t=a.split(",").map(t=>t.trim()).filter(t=>t);if(0===t.length)return`${i} ${l.display}`;const e=t.map(t=>`'${t}'`).join(", ");return`${i} ${l.display} ${e}`}if("range"===s){const t=a.split(",").map(t=>t.trim()).filter(t=>t);return 2===t.length?`${i} between '${t[0]}' and '${t[1]}'`:`${i} ${l.display} '${a}'`}return"isnull"===s?`${i} ${"function"==typeof l.display?l.display(a):l.display}`:l?`${i} ${l.display} '${a}'`:`${i} is '${a}'`}const u={LOOKUPS:c,parseFilterKey:d,formatFilterDisplay:h,getLookupDescription:function(t){const e=c[t];return e?e.description:"Exact match"},isValidLookup:function(t){return t&&c.hasOwnProperty(t)},getAvailableLookups:function(){return Object.keys(c)},buildFilterKey:function(t,e=null){return t?e?`${t}__${e}`:t:""}};class TableView extends n{constructor(t={}){super({className:"table-view-component",itemClass:t.itemClass||TableRow,selectionMode:t.selectable?"multiple":"none",emptyMessage:t.emptyMessage||"No data available",addButtonIcon:t.addButtonIcon||"bi bi-plus-circle",...t}),this.isFullscreen=!1,this.columns=t.columns||[],this.actions=t.actions||null,this.contextMenu=t.contextMenu||null,this.batchActions=t.batchActions||null,this.searchable=!1!==t.searchable,this.sortable=!1!==t.sortable,this.filterable=!1!==t.filterable,this.paginated=!1!==t.paginated,this.clickAction=t.clickAction||"view",this.itemView=t.itemView,this.addForm=t.addForm,this.editForm=t.editForm,this.deleteTemplate=t.deleteTemplate,this.formDialogConfig=t.formDialogConfig||{},this.viewDialogOptions=t.viewDialogOptions||{},this.exportOptions=t.exportOptions||null,this.options.showExport&&!this.exportOptions&&(this.exportOptions=[{format:"csv",label:"Export as CSV",icon:"bi bi-file-earmark-spreadsheet"},{format:"json",label:"Export as JSON",icon:"bi bi-file-earmark-code"}]),this.exportSource=t.exportSource||"remote",this.filters={},this.additionalFilters=t.filters||[],this.hideActivePills=t.hideActivePills||!1,this.hideActivePillNames=t.hideActivePillNames||[],this.rowAction=t.rowAction||"row-click",this.batchBarLocation=t.batchBarLocation||"bottom",this.options.addButtonLabel=t.addButtonLabel||"Add",this.toolbarButtons=t.toolbarButtons||[],this.tableOptions={striped:!0,bordered:!1,hover:!0,responsive:!1,size:null,...t.tableOptions},this.searchPlacement=t.searchPlacement||"toolbar",this.searchPlaceholder=t.searchPlaceholder||"Search...",this.initializeColumns(),this.extractColumnFilters(),this.footerTotalColumns=this.columns.filter(t=>!0===t.footer_total),this.hasFooterTotals=this.footerTotalColumns.length>0,this.template=this.buildTableTemplate(),this.setupCollectionListeners()}setupCollectionListeners(){this.hasFooterTotals&&this.collection&&this.collection.on("reset add remove change",()=>{this.updateFooterTotals()})}initializeColumns(){this.columns.forEach(t=>{!t.key&&t.name&&(t.key=t.name),t.label||t.title||(t.label=t.key.charAt(0).toUpperCase()+t.key.slice(1))})}getResponsiveClasses(t){if(!t)return"";const e=["sm","md","lg","xl","xxl"];if("string"==typeof t)return e.includes(t)?`d-none d-${t}-table-cell`:(console.warn(`Invalid visibility breakpoint: ${t}. Valid options are: ${e.join(", ")}`),"");if("object"==typeof t){const i=[];if(t.hide){if(!e.includes(t.hide))return console.warn(`Invalid hide breakpoint: ${t.hide}. Valid options are: ${e.join(", ")}`),"";i.push(`d-table-cell d-${t.hide}-none`)}if(t.show){if(!e.includes(t.show))return console.warn(`Invalid show breakpoint: ${t.show}. Valid options are: ${e.join(", ")}`),"";t.hide?i.push(`d-${t.show}-table-cell`):i.push(`d-none d-${t.show}-table-cell`)}return i.join(" ")}return""}parseColumnKey(t){const e=t.split("|");return{fieldKey:e[0],formatter:e[1]||null}}updateFooterTotals(){if(!this.hasFooterTotals||!this.element)return;const t=this.calculateFooterTotals();let e=0;this.columns.forEach(i=>{if(i.footer_total){const n=`col_${e}`,s=this.element.querySelector(`[data-total-column="${n}"]`);if(s&&t[n]){const e=this.parseColumnKey(i.key).formatter||i.formatter;let l;l=e&&"string"==typeof e?this.formatValue(t[n].value,e):t[n].value,s.textContent=l}e++}})}formatValue(t,e){try{return s.pipe(t,e)}catch(i){return console.warn("Error formatting value:",i),t}}calculateFooterTotals(){if(!this.hasFooterTotals||!this.collection||0===this.collection.length)return{};const t={};return this.footerTotalColumns.forEach((e,i)=>{const{fieldKey:n,formatter:s}=this.parseColumnKey(e.key);let l=0;this.collection.forEach(t=>{const e=t.get?t.get(n):t[n],i=parseFloat(e)||0;l+=i}),e.key,this.collection.length,t[`col_${i}`]={value:l,formatter:s||e.formatter,fieldKey:n,originalKey:e.key}}),t}extractColumnFilters(){this.filters={},this.columns.forEach(t=>{if(t.filter){const{fieldKey:e}=this.parseColumnKey(t.key);this.filters[e]=t.filter}})}isSelectable(){return this.batchActions&&this.batchActions.length>0&&"multiple"==this.selectionMode}buildTableTemplate(){const t="top"===this.batchBarLocation?this.buildBatchActionsPanel():"",e="bottom"===this.batchBarLocation?this.buildBatchActionsPanel():"";return`\n <div class="mojo-table-wrapper">\n ${this.buildToolbarTemplate()}\n ${t}\n <div class="table-container"${(()=>{const t=this.tableOptions&&null!=this.tableOptions.fontSize?this.tableOptions.fontSize:this.options&&this.options.fontSize,e="sm"===t?"0.9rem":"xs"===t?"0.8rem":t?String(t):null;return e?` style="font-size: ${e};"`:""})()}>\n {{#loading}}\n <div class="mojo-table-loading d-flex justify-content-center align-items-center py-5">\n <div class="spinner-border" role="status">\n <span class="visually-hidden">Loading...</span>\n </div>\n </div>\n {{/loading}}\n {{^loading}}\n {{#isEmpty}}\n <div class="table-empty text-center py-5">\n <i class="bi bi-inbox fa-2x mb-2 text-muted"></i>\n <p class="text-muted">{{emptyMessage}}</p>\n </div>\n {{/isEmpty}}\n {{^isEmpty}}\n <table class="${this.buildTableClasses()}">\n ${this.buildTableHeaderTemplate()}\n <tbody data-container="items"></tbody>\n ${this.hasFooterTotals?this.buildTableFooterTemplate():""}\n </table>\n {{/isEmpty}}\n {{/loading}}\n </div>\n ${e}\n ${this.buildPaginationTemplate()}\n </div>\n `}buildTableClasses(){let t=["table"];return this.tableOptions.striped&&t.push("table-striped"),this.tableOptions.bordered&&t.push("table-bordered"),this.tableOptions.hover&&t.push("table-hover"),this.tableOptions.responsive&&t.push("table-responsive"),this.tableOptions.background&&t.push(`table-${this.tableOptions.background}`),"sm"===this.tableOptions.size&&t.push("table-sm"),"lg"===this.tableOptions.size&&t.push("table-lg"),t.join(" ")}buildToolbarTemplate(){return this.searchable||this.filterable?`\n <div class="table-action-buttons mb-3">\n <div class="d-flex align-items-center gap-2">\n ${this.buildActionButtonsTemplate()}\n ${this.filterable?this.buildFilterDropdownTemplate():""}\n ${this.searchable&&"toolbar"===this.searchPlacement?this.buildSearchTemplate():""}\n\n </div>\n <div data-container="filter-pills"></div>\n </div>\n `:""}buildActionButtonsTemplate(){let t=[];if(t.push('\n <button class="btn btn-sm btn-outline-secondary btn-refresh"\n data-action="refresh"\n title="Refresh">\n <i class="bi bi-arrow-clockwise"></i>\n </button>\n '),this.isFullscreenSupported()&&t.push('\n <button class="btn btn-sm btn-outline-secondary btn-fullscreen"\n data-action="toggle-fullscreen"\n title="Toggle Fullscreen">\n <i class="bi bi-fullscreen"></i>\n </button>\n '),this.options.showAdd&&t.push(`\n <button class="btn btn-sm btn-success btn-add"\n data-action="add"\n title="${this.options.addButtonLabel}">\n <i class="${this.options.addButtonIcon} me-1"></i>\n <span class="d-none d-lg-inline">${this.options.addButtonLabel}</span>\n </button>\n `),this.options.showExport)if(this.exportOptions&&this.exportOptions.length>1){const e=this.exportOptions.map(t=>`\n <li>\n <a class="dropdown-item" href="#" data-action="export" data-format="${t.format}">\n <i class="${t.icon||"bi bi-file-earmark-arrow-down"} me-2"></i>${t.label}\n </a>\n </li>\n `).join("");t.push(`\n <div class="dropdown">\n <button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button"\n data-bs-toggle="dropdown" aria-expanded="false" title="Export">\n <i class="bi bi-download me-1"></i>\n <span class="d-none d-lg-inline">Export</span>\n </button>\n <ul class="dropdown-menu">\n ${e}\n </ul>\n </div>\n `)}else{const e=this.exportOptions&&1===this.exportOptions.length?this.exportOptions[0].format:"json";t.push(`\n <button class="btn btn-sm btn-outline-secondary btn-export"\n data-action="export"\n data-format="${e}"\n title="Export">\n <i class="bi bi-download me-1"></i>\n <span class="d-none d-lg-inline">Export</span>\n </button>\n `)}return this.toolbarButtons&&this.toolbarButtons.length>0&&this.toolbarButtons.forEach((e,i)=>{const{label:n="Button",icon:s="",action:l="",handler:a=null,variant:o="outline-secondary",title:r=n,className:c="",permissions:d=null}=e;if(d&&!this.checkPermissions(d))return;const h=s?`<i class="${s} me-1"></i>`:"",u=`<span class="d-none d-lg-inline">${n}</span>`;let m="";a?m=`data-action="custom-toolbar-button" data-button-index="${i}"`:l&&(m=`data-action="${l}"`);const p=`btn btn-sm btn-${o} ${c}`.trim();t.push(`\n <button class="${p}"\n ${m}\n title="${r}">\n ${h}${u}\n </button>\n `)}),t.join("")}buildSearchTemplate(){return'\n <div class="flex-grow-1" style="max-width: 400px;">\n <div class="input-group input-group-sm">\n <span class="input-group-text">\n <i class="bi bi-search"></i>\n </span>\n <input type="search"\n class="form-control"\n placeholder="{{searchPlaceholder}}"\n data-filter="search"\n data-change-action="apply-search"\n value="{{collection.params.search}}"\n aria-label="Search">\n {{#searchValue}}\n <button class="btn btn-outline-secondary" type="button"\n data-action="clear-search"\n title="Clear search">\n <i class="bi bi-x"></i>\n </button>\n {{/searchValue}}\n </div>\n </div>\n '}buildFilterDropdownTemplate(){return this.filters&&Object.keys(this.filters).length>0||this.additionalFilters&&this.additionalFilters.length>0?`\n <div class="dropdown">\n <button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button"\n data-bs-toggle="dropdown" aria-expanded="false">\n <i class="bi bi-filter me-1"></i>\n <span class="d-none d-lg-inline">Add Filter</span>\n </button>\n <div class="dropdown-menu" style="min-width: 250px;">\n ${this.buildFilterList()}\n </div>\n </div>\n `:""}buildFilterList(){const t=this.getAllAvailableFilters(),e=this.getActiveFilters();return 0===t.length?'<div class="dropdown-item-text text-muted">No filters available</div>':`\n ${t.map(t=>{const i=e.hasOwnProperty(t.key),n=i?"active":"",s=this.getFilterIcon(t.type||t.config?.type);return`\n <button class="dropdown-item ${n}"\n data-action="add-filter"\n data-filter-key="${t.key}">\n <i class="bi bi-${s} me-2"></i>\n ${t.label}\n ${i?'<i class="bi bi-check-circle ms-auto"></i>':""}\n </button>\n `}).join("")}\n ${Object.keys(e).length>0?'\n <div class="dropdown-divider"></div>\n <button class="dropdown-item text-danger" data-action="clear-all-filters">\n <i class="bi bi-x-circle me-2"></i>Clear All Filters\n </button>\n ':""}\n `}updateFilterPills(){const t=this.element?.querySelector('[data-container="filter-pills"]');if(!t)return;this.getActiveFilters();const e=this.buildActivePills();t.innerHTML=e}updateSearchInputs(t){const e=this.element?.querySelectorAll('[data-filter="search"]');e&&e.forEach(e=>{e.value=t||""})}buildActivePills(){if(this.hideActivePills)return"";const t=this.getActiveFilters(),e=t.search&&""!==t.search.toString().trim();let i=Object.entries(t).filter(([t,e])=>e&&""!==e.toString().trim()&&"search"!==t);return this.hideActivePillNames&&this.hideActivePillNames.length>0&&(i=i.filter(([t])=>!this.hideActivePillNames.includes(t))),0!==i.length||e?`\n <div class="row mt-2">\n <div class="col-12">\n <div class="d-flex flex-wrap align-items-center">\n ${i.map(([t,e])=>{const{field:i}=d(t);return`\n <span class="badge bg-primary me-1 mb-1 py-1 px-2 position-relative" style="font-size: 0.75rem;">\n <i class="bi bi-filter me-1" style="font-size: 0.65rem;"></i>\n\n <button type="button" class="btn btn-link text-white p-0 ms-1"\n style="font-size: 0.65rem; line-height: 1;"\n data-action="edit-filter"\n data-filter="${t}"\n title="Edit filter">\n ${h(t,e,this.getFilterLabel(i))}\n </button>\n\n <button type="button" class="btn-close btn-close-white ms-1"\n style="font-size: 0.6rem; width: 0.5rem; height: 0.5rem;"\n data-action="remove-filter"\n data-filter="${t}"\n title="Remove filter">\n </button>\n </span>\n `}).join("")}\n ${i.length>1||i.length>0&&e||0===i.length&&e?'\n <button class="btn btn-sm btn-outline-secondary mb-1 py-0 px-2" style="font-size: 0.75rem;" data-action="clear-all-filters">\n <i class="bi bi-x-circle me-1" style="font-size: 0.7rem;"></i>\n <small>Clear All</small>\n </button>\n ':""}\n </div>\n </div>\n </div>\n `:""}buildTableHeaderTemplate(){let t="";return this.isSelectable()&&(t+='\n <th style="width: 40px; padding: 0;">\n <div class="mojo-select-all-cell" data-action="select-all">\n <div class="mojo-checkbox">\n <i class="bi bi-check"></i>\n </div>\n </div>\n </th>\n '),this.columns.forEach(e=>{const{fieldKey:i}=this.parseColumnKey(e.key),n=this.sortable&&!1!==e.sortable,s=this.getSortBy()===i?this.getSortDirection():null,l=this.getSortIcon(s),a=e.label||e.title||i,o=this.getResponsiveClasses(e.visibility);t+=`\n <th class="${n?"sortable":""} ${o}">\n <div class="d-flex align-items-center">\n <span>${a}</span>\n ${n?`\n <div class="dropdown d-inline-block ms-2">\n <button class="btn btn-sm btn-link p-0 text-decoration-none" type="button"\n data-bs-toggle="dropdown" aria-expanded="false"\n data-column="${i}">\n ${l}\n </button>\n <ul class="dropdown-menu dropdown-menu-end">\n <li><a class="dropdown-item ${"asc"===s?"active":""}"\n data-action="sort" data-field="${i}" data-direction="asc">\n <i class="bi bi-sort-alpha-down me-2"></i>Sort A-Z\n </a></li>\n <li><a class="dropdown-item ${"desc"===s?"active":""}"\n data-action="sort" data-field="${i}" data-direction="desc">\n <i class="bi bi-sort-alpha-down-alt me-2"></i>Sort Z-A\n </a></li>\n <li><a class="dropdown-item ${null===s?"active":""}"\n data-action="sort" data-field="${i}" data-direction="none">\n <i class="bi bi-x-circle me-2"></i>No Sort\n </a></li>\n </ul>\n </div>\n `:""}\n </div>\n </th>\n `}),this.actions?t+="<th>Actions</th>":this.contextMenu&&(t+='<th style="width: 1px;"></th>'),`\n <thead>\n <tr>\n ${t}\n </tr>\n </thead>\n `}buildTableFooterTemplate(){let t="";this.isSelectable()&&(t+="<td></td>");let e=0;return this.columns.forEach((i,n)=>{const s=this.getResponsiveClasses(i.visibility);if(i.footer_total){const n=`col_${e}`,l=this.parseColumnKey(i.key).formatter||i.formatter;let a;a=l&&"string"==typeof l?`{{{footerTotals.${n}.value|${l}}}}`:`{{footerTotals.${n}.value}}`,t+=`<td class="table-footer-total ${s}" data-total-column="${n}">${a}</td>`,e++}else t+=0===n?`<td class="table-footer-label ${s}"><strong>Totals</strong></td>`:`<td class="${s}"></td>`}),(this.actions||this.contextMenu)&&(t+="<td></td>"),`\n <tfoot>\n <tr class="table-totals-row">\n ${t}\n </tr>\n </tfoot>\n `}buildBatchActionsPanel(){if(!this.batchActions||0===this.batchActions.length)return"";if("top"===this.batchBarLocation){let t="";return this.batchActions.forEach(e=>{t+=`\n <button class="btn btn-sm btn-outline-secondary" data-action="batch-${e.action}" title="${e.label}">\n <i class="${e.icon} me-1"></i>\n <span class="d-none d-lg-inline">${e.label}</span>\n </button>\n `}),`\n <div class="batch-actions-panel-top alert alert-info d-none mb-3" role="alert">\n <div class="d-flex justify-content-between align-items-center">\n <div class="d-flex align-items-center">\n <strong class="me-2">\n <span class="batch-select-count">0</span> ${this.options.batchPanelTitle||"items"} selected\n </strong>\n </div>\n <div class="d-flex gap-2 align-items-center">\n ${t}\n <button class="btn btn-sm btn-outline-secondary" data-action="clear-selection" title="Clear Selection">\n <i class="bi bi-x-circle me-1"></i>\n <span class="d-none d-lg-inline">Clear</span>\n </button>\n </div>\n </div>\n </div>\n `}{let t="";return this.batchActions.forEach(e=>{t+=`\n <div class="batch-select-action text-center px-2" data-action="batch-${e.action}">\n <div class="batch-action-icon fs-3">\n <i class="${e.icon}"></i>\n </div>\n <div class="batch-action-title small">${e.label}</div>\n </div>\n `}),`\n <div class="batch-actions-panel rounded-start rounded-end" style="display: none;">\n <div class="batch-select-panel rounded-start rounded-end">\n <div class="row g-0">\n <div class="col-auto">\n <div class="batch-select-count rounded-start">0</div>\n </div>\n <div class="col">\n <div class="ps-2 batch-select-title">${this.options.batchPanelTitle||"Rows"}</div>\n </div>\n <div class="col">\n <div class="batch-select-actions d-flex justify-content-end">\n ${t}\n </div>\n </div>\n <div class="col-auto">\n <div class="batch-select-end rounded-end"></div>\n </div>\n </div>\n </div>\n </div>\n `}}buildPaginationTemplate(){return this.paginated?'\n <div class="table-status-bar mt-3">\n <div class="d-flex flex-column flex-lg-row justify-content-center justify-content-lg-between align-items-center gap-3">\n <div class="d-flex flex-column flex-sm-row align-items-center gap-2 gap-sm-3 text-center text-lg-start">\n <span class="text-muted">\n Showing <span data-value="start">0</span> to <span data-value="end">0</span>\n of <span data-value="total">0</span> entries\n </span>\n <div class="d-flex align-items-center">\n <label class="form-label me-2 mb-0">Show:</label>\n <select class="form-select form-select-sm" style="width: auto;" data-change-action="page-size">\n <option value="5">5</option>\n <option value="10">10</option>\n <option value="25">25</option>\n <option value="50">50</option>\n <option value="100">100</option>\n </select>\n </div>\n </div>\n <nav aria-label="Table pagination">\n <ul class="pagination pagination-sm mb-0 justify-content-center" data-container="pagination">\n \x3c!-- Pagination will be rendered here --\x3e\n </ul>\n </nav>\n </div>\n </div>\n ':""}_createItemView(t,e){const i=new this.itemClass({model:t,index:e,listView:this,tableView:this,template:this.itemTemplate,columns:this.columns,actions:this.actions,contextMenu:this.contextMenu,batchActions:this.batchActions,containerId:"items"});return this.itemViews.set(t.id,i),i.on("item:select",t=>{this._onItemSelect(t),this.updateBatchActionsPanel()}),i.on("item:deselect",t=>{this._onItemDeselect(t),this.updateBatchActionsPanel()}),i.on("row:click",this._onRowClick.bind(this)),i.on("row:view",this._onRowView.bind(this)),i.on("row:edit",this._onRowEdit.bind(this)),i.on("row:delete",this._onRowDelete.bind(this)),i.on("cell:edit",this._onCellEdit.bind(this)),i.on("cell:save",this._onCellSave.bind(this)),i.on("cell:cancel",this._onCellCancel.bind(this)),i}async onMounted(){await super.onMounted();const t=this.getActiveFilters();this.collection&&Object.keys(t).length>0&&this.updateFilterPills(),this.setupSearchClearListener()}setupSearchClearListener(){this.element&&this.element.querySelectorAll('input[type="search"][data-filter="search"]').forEach(t=>{t.addEventListener("input",t=>{""===t.target.value&&this.getActiveFilters().search&&this.onActionClearSearch(t,t.target)})})}_onRowClick(t){if(this.emit("row:click",t),this.options.onRowClick)return this.options.onRowClick(t.model,t.event);"view"===this.clickAction?this._onRowView(t):"edit"===this.clickAction&&this._onRowEdit(t)}getModelClass(t){return this.collection?.ModelClass?this.collection.ModelClass:this.collection?.model?this.collection.model:t?.constructor?t.constructor:null}getModelName(t){const e=this.getModelClass(t);return e&&(e.MODEL_NAME||e.name.replace(/Model$/,""))||"Item"}getItemViewClass(t){if(this.itemView)return this.itemView;const e=this.getModelClass(t);return e?.VIEW_CLASS?e.VIEW_CLASS:null}getAddFormConfig(t){return this.addForm||t?.ADD_FORM||this.editForm||t?.EDIT_FORM}getEditFormConfig(t){return this.editForm||t?.EDIT_FORM||this.addForm||t?.ADD_FORM}getFormDialogConfig(t){return{...t?.FORM_DIALOG_CONFIG,...this.formDialogConfig}}renderTemplateString(t,e){return t?l.render(t,e):""}async _onRowView(t){if(this.emit("row:view",t),this.options.onItemView)return void(await this.options.onItemView(t.model,t.event));const e=this.getItemViewClass(t.model);if(e){const i=new e({model:t.model,collection:this.collection});await a.showDialog({header:!1,body:i,size:"lg",centered:!1,...this.getFormDialogConfig(this.getModelClass(t.model)),...this.viewDialogOptions})}else await a.showData({title:`View ${this.getModelName(t.model)} #${t.model.id}`,model:t.model})}async _onRowEdit(t){if(this.emit("row:edit",t),this.options.onItemEdit)return void(await this.options.onItemEdit(t.model,t.event));const e=this.getModelClass(t.model);let i=this.getEditFormConfig(e);if(i){i.fields||(i={title:`Edit ${this.getModelName(t.model)}`,fields:i});const n=await a.showModelForm({model:t.model,...i,...this.getFormDialogConfig(e)});if(!n)return;if(!n.success||!n?.result?.data.status)return void a.showError(n?.result?.data?.error||n?.result?.message||"An error occurred")}else{const e=await a.showDialog({title:`Edit ${this.getModelName(t.model)} #${t.model.id}`,body:new o({model:t.model,fields:this.options.formFields||[]})});if(e){const i=await t.model.save(e);if(!i.data?.status)return void a.showError(i.data.error||"An error occurred");await this.refresh()}}}async _onRowDelete(t){if(this.emit("row:delete",t),this.options.onItemDelete)return void(await this.options.onItemDelete(t.model,t.event));const e=this.getModelClass(t.model),i=this.deleteTemplate||e?.DELETE_TEMPLATE||'Are you sure you want to delete this {{name||"item"}}?',n=this.renderTemplateString(i,t.model);await a.confirm({message:n||"Are you sure you want to delete this item?",title:"Confirm Delete",confirmText:"Delete",confirmClass:"btn-danger"})&&(await t.model.destroy(),this.collection.fetch())}_onCellEdit(t){this.emit("cell:edit",t)}async _onCellSave(t){this.emit("cell:save",t)}_onCellCancel(t){this.emit("cell:cancel",t)}isFullscreenSupported(){return!!(document.fullscreenEnabled||document.mozFullScreenEnabled||document.webkitFullscreenEnabled||document.msFullscreenEnabled)}async onActionToggleFullscreen(t,e){this.isFullscreen?await this.exitFullscreen():await this.enterFullscreen()}async enterFullscreen(){try{this.element.requestFullscreen?await this.element.requestFullscreen():this.element.mozRequestFullScreen?await this.element.mozRequestFullScreen():this.element.webkitRequestFullscreen?await this.element.webkitRequestFullscreen():this.element.msRequestFullscreen&&await this.element.msRequestFullscreen(),this.isFullscreen=!0,this.element.classList.add("table-fullscreen"),this.updateFullscreenButton(),this.setupFullscreenListeners(),this.emit("table:fullscreen:enter")}catch(t){console.warn("Could not enter fullscreen:",t)}}async exitFullscreen(){try{document.exitFullscreen?await document.exitFullscreen():document.mozCancelFullScreen?await document.mozCancelFullScreen():document.webkitExitFullscreen?await document.webkitExitFullscreen():document.msExitFullscreen&&await document.msExitFullscreen(),this.isFullscreen=!1,this.element.classList.remove("table-fullscreen"),this.updateFullscreenButton(),this.emit("table:fullscreen:exit")}catch(t){console.warn("Could not exit fullscreen:",t)}}updateFullscreenButton(){const t=this.element?.querySelector(".btn-fullscreen"),e=t?.querySelector("i");t&&e&&(this.isFullscreen?(e.className="bi bi-fullscreen-exit",t.title="Exit Fullscreen"):(e.className="bi bi-fullscreen",t.title="Enter Fullscreen"))}setupFullscreenListeners(){if(this._fullscreenHandler)return;const t=()=>{!(document.fullscreenElement||document.mozFullScreenElement||document.webkitFullscreenElement||document.msFullscreenElement)&&this.isFullscreen&&(this.isFullscreen=!1,this.element.classList.remove("table-fullscreen"),this.updateFullscreenButton(),this.emit("table:fullscreen:exit"))};document.addEventListener("fullscreenchange",t),document.addEventListener("mozfullscreenchange",t),document.addEventListener("webkitfullscreenchange",t),document.addEventListener("msfullscreenchange",t),this._fullscreenHandler=t}cleanupFullscreenListeners(){this._fullscreenHandler&&(document.removeEventListener("fullscreenchange",this._fullscreenHandler),document.removeEventListener("mozfullscreenchange",this._fullscreenHandler),document.removeEventListener("webkitfullscreenchange",this._fullscreenHandler),document.removeEventListener("msfullscreenchange",this._fullscreenHandler),this._fullscreenHandler=null)}destroy(){this.cleanupFullscreenListeners(),super.destroy()}async onActionRefresh(t,e){await this.refresh()}async onActionAdd(t,e){if(this.options.onAdd)return this.emit("table:add",{event:t}),void(await this.options.onAdd(t));this.emit("table:add",{event:t});const i=this.getModelClass();if(!i)return void console.warn("Cannot determine Model class for add operation");let n=this.getAddFormConfig(i);if(n){const t=new i;n.fields||(n={title:`Add ${this.getModelName()}`,fields:n});const e=await a.showForm({model:t,...n,...this.getFormDialogConfig(i)});if(e){this.options.addRequiresActiveGroup&&(e.group=this.getApp().activeGroup.id),this.options.addRequiresActiveUser&&(e.user=this.getApp().activeUser.id),this.options.addFormDefaults&&Object.assign(e,this.options.addFormDefaults);const i=await t.save(e);if(!i?.data.status)return void a.showError(i?.data.error||"An error occurred");this.collection&&this.collection.add(t),await this.refresh()}}else{const t=new i,e=await a.showDialog({title:`Add ${this.getModelName()}`,body:new o({model:t,fields:this.options.formFields||[]})});if(e){const i=await t.save(e);if(!i?.data.status)return void a.showError(i.data.error||"An error occurred");this.collection&&this.collection.add(t),await this.refresh()}}}async onActionExport(t,e){const i=e.getAttribute("data-format")||"json";this.emit("table:export",{format:i,source:this.exportSource,event:t}),"remote"===this.exportSource?this.collection?await this.collection.download(i):console.warn("TableView: Cannot export from remote without a collection."):this.options.onExport?await this.options.onExport(this.collection?.toJSON()||[],i):console.warn("TableView: onExport handler not implemented for local export.")}async onActionApplySearch(t,e){const i=e.value.trim();this.collection&&(this.setFilter("search",i),this.collection.params.start=0,this.collection.restEnabled?await this.collection.fetch():this.render()),this.updateFilterPills(),this.emit("table:search",{searchTerm:i,event:t}),this.emit("params-changed")}async onActionClearSearch(t,e){this.setFilter("search",null),this.collection&&(this.collection.params.start=0,this.collection.restEnabled&&await this.collection.fetch()),await this.render(),this.updateFilterPills(),this.emit("table:search",{searchTerm:"",event:t}),this.emit("params-changed")}getSortBy(){const t=this.collection?.params?.sort;return t?t.startsWith("-")?t.slice(1):t:null}getSortDirection(){const t=this.collection?.params?.sort;return t&&t.startsWith("-")?"desc":"asc"}getSortIcon(t){return"asc"===t?'<i class="bi bi-sort-alpha-down text-primary"></i>':"desc"===t?'<i class="bi bi-sort-alpha-down-alt text-primary"></i>':'<i class="bi bi-three-dots-vertical text-muted"></i>'}async onActionSort(t,e){t.preventDefault();const i=e.getAttribute("data-field"),n=e.getAttribute("data-direction");if(this.collection){let t;if(t="none"===n?void 0:"desc"===n?`-${i}`:i,this.collection.setParams({...this.collection.params,sort:t,start:0}),this.collection.restEnabled)await this.collection.fetch();else{if(t){const e=t.startsWith("-"),i=e?t.slice(1):t;this.collection.sort((t,n)=>{const s=t.get(i),l=n.get(i);return s<l?e?1:-1:s>l?e?-1:1:0})}this.render()}}this.updateSortIcons(),this.emit("table:sort",{field:i,event:t}),this.emit("params-changed")}updateSortIcons(){if(!this.element)return;const t=this.getSortBy(),e=this.getSortDirection();this.columns.forEach(i=>{if(this.sortable&&!1!==i.sortable){const{fieldKey:n}=this.parseColumnKey(i.key),s=this.element.querySelector(`[data-bs-toggle="dropdown"][data-column="${n}"]`);if(s){const i=t===n,l=this.getSortIcon(i?e:null);s.innerHTML=l;const a=s.nextElementSibling;if(a){const s=a.querySelector(`[data-field="${n}"][data-direction="asc"]`),l=a.querySelector(`[data-field="${n}"][data-direction="desc"]`),o=a.querySelector(`[data-field="${n}"][data-direction="none"]`);s&&s.classList.toggle("active",i&&"asc"===e),l&&l.classList.toggle("active",i&&"desc"===e),o&&o.classList.toggle("active",!i||t!==n)}}}})}async onActionSelectAll(t,e){t.stopPropagation();const i=this.itemViews.size>0&&Array.from(this.itemViews.values()).every(t=>t.selected);i?this.clearSelection():this.forEachItem(t=>{t.selected||t.select()});const n=this.element?.querySelector(".mojo-select-all-cell");n&&n.classList.toggle("selected",!i),this.updateBatchActionsPanel()}async onBeforeRender(){this.searchValue=this.getActiveFilters().search||"",this.footerTotals=this.calculateFooterTotals()}async onAfterRender(){if(await super.onAfterRender(),this.hasFooterTotals&&this.updateFooterTotals(),this.paginated&&this.collection){const t=this.collection.meta?.count||this.collection.length(),e=this.collection.params?.start||0,i=this.collection.params?.size||10,n=Math.min(e+i,t),s=this.element.querySelector('[data-value="start"]'),l=this.element.querySelector('[data-value="end"]'),a=this.element.querySelector('[data-value="total"]');s&&(s.textContent=e+1),l&&(l.textContent=n),a&&(a.textContent=t);const o=this.element.querySelector('[data-change-action="page-size"]');o&&(o.value=i),this.renderPagination()}this.updateSortIcons(),this.updateFilterPills(),this.setupSearchClearListener()}renderPagination(){const t=this.element.querySelector('[data-container="pagination"]');if(!t||!this.collection)return;const e=this.collection.meta?.count||this.collection.length(),i=this.collection.params?.size||10,n=this.collection.params?.start||0,s=Math.floor(n/i)+1,l=Math.ceil(e/i);if(l<=1)return void(t.innerHTML="");const a=s>1?s-1:l,o=s<l?s+1:1,r=[];r.push(`\n <li class="page-item">\n <a class="page-link" href="#" data-action="page" data-page="${a}">\n <i class="bi bi-chevron-left"></i>\n </a>\n </li>\n `);const c=/* @__PURE__ */new Set([1,l]);for(let u=s-1;u<=s+1;u++)u>=1&&u<=l&&c.add(u);const d=Array.from(c).sort((t,e)=>t-e);let h=0;for(const u of d)h&&u-h>1&&r.push('\n <li class="page-item disabled"><span class="page-link">…</span></li>\n '),r.push(`\n <li class="page-item ${u===s?"active":""}">\n <a class="page-link" href="#" data-action="page" data-page="${u}">${u}</a>\n </li>\n `),h=u;r.push(`\n <li class="page-item">\n <a class="page-link" href="#" data-action="page" data-page="${o}">\n <i class="bi bi-chevron-right"></i>\n </a>\n </li>\n `),t.innerHTML=r.join("")}async onActionPage(t,e){t.preventDefault();const i=parseInt(e.getAttribute("data-page"),10),n=this.collection.params?.size||10,s=this.collection.meta?.count||this.collection.length(),l=Math.max(1,Math.ceil(s/n));let a=isNaN(i)?1:i;a<1&&(a=l),a>l&&(a=1),this.collection.setParams({...this.collection.params,start:(a-1)*n}),this.collection.restEnabled?await this.collection.fetch():this.render(),this.emit("table:page",{page:a,event:t}),this.emit("params-changed")}async onChangePageSize(t,e){const i=parseInt(e.value);this.collection&&(this.collection.setParams({...this.collection.params,start:0,size:i}),this.collection.restEnabled&&await this.collection.fetch(),this.render()),this.emit("table:pagesize",{size:i,event:t}),this.emit("params-changed")}getActiveFilters(){if(!this.collection?.params)return{};const{start:t,size:e,sort:i,...n}=this.collection.params,s={},l=/* @__PURE__ */new Set;return this.getAllAvailableFilters().forEach(t=>{if("daterange"===t.config.type){const e=t.key,i=t.config.startName||"dr_start",a=t.config.endName||"dr_end",o=t.config.fieldName||"dr_field";n[o]===e&&(n[i]||n[a])&&(s[e]={start:n[i]||"",end:n[a]||""},l.add(i),l.add(a),l.add(o))}}),Object.keys(n).forEach(t=>{l.has(t)||(s[t]=n[t])}),Object.keys(s).forEach(t=>{if(s.hasOwnProperty(t)){const e=`${t}__in`;s.hasOwnProperty(e)&&(delete s[t],s[e]=s[e])}}),s}setFilter(t,e){if(!this.collection)return;const i=this.getFilterConfig(t);if(i&&"daterange"===i.type){const n=i.startName||"dr_start",s=i.endName||"dr_end",l=i.fieldName||"dr_field";delete this.collection.params[n],delete this.collection.params[s],delete this.collection.params[l],e&&"object"==typeof e&&(e.start||e.end)&&(e.start&&(this.collection.params[n]=e.start),e.end&&(this.collection.params[s]=e.end),this.collection.params[l]=t)}else{const{field:i,lookup:n}=d(t);if(delete this.collection.params[t],delete this.collection.params[i],delete this.collection.params[`${i}__in`],!e||Array.isArray(e)&&0===e.length)return;Array.isArray(e)?1===e.length?this.collection.params[i]=e[0]:this.collection.params[`${i}__in`]=e.join(","):this.collection.params[t]=e}}getAllAvailableFilters(){const t=[];return this.columns.forEach(e=>{if(e.filter){const{fieldKey:i}=this.parseColumnKey(e.key);t.push({key:i,label:e.filter.label||e.label||i,type:e.filter.type,config:e.filter})}}),this.additionalFilters&&Array.isArray(this.additionalFilters)&&this.additionalFilters.forEach(e=>{t.push({key:e.name||e.key,label:e.label,type:e.type,config:e})}),t}getFilterConfig(t){const e=this.columns.find(e=>{const{fieldKey:i}=this.parseColumnKey(e.key);return i===t});if(e&&e.filter)return e.filter;if(this.additionalFilters&&Array.isArray(this.additionalFilters)){const e=this.additionalFilters.find(e=>(e.name||e.key)===t);if(e)return e}return null}getFilterLabel(t){if("search"===t)return"Search";const e=this.filters[t];if(e&&e.label)return e.label;const i=this.additionalFilters.find(e=>(e.name||e.key)===t);return i&&i.label?i.label:t.charAt(0).toUpperCase()+t.slice(1)}getFilterDisplayValue(t,e){if("search"===t)return`"${e}"`;const i=this.filters[t]||this.additionalFilters.find(e=>(e.name||e.key)===t);if(i&&"daterange"===i.type&&"object"==typeof e)return`${e.start||""} to ${e.end||""}`;if(i&&"select"===i.type&&i.options){if("object"==typeof i.options[0]){const t=i.options.find(t=>t.value===e);return t?t.label:e}return e}return e}getFilterIcon(t){return{text:"search",select:"funnel",date:"calendar",daterange:"calendar-range",number:"123",boolean:"toggle-on"}[t]||"filter"}async onActionAddFilter(t,e){const i=e.getAttribute("data-filter-key"),n=this.getFilterConfig(i),s=this.getActiveFilters()[i];if(!n)return void console.warn("No filter config found for key:",i);const l=await a.showForm({title:`${void 0!==s&&""!==s?"Edit":"Add"} ${this.getFilterLabel(i)} Filter`,size:"md",fields:[this.buildFilterDialogField(n,s,i)]});if(l){const t=this.extractFilterValue(n,l);this.setFilter(i,t),await this.applyFilters()}}buildFilterDialogField(t,e,i){const n={name:"filter_value",label:t.label,value:e,...t,placeholder:t.placeholder||t.placeHolder};if("daterange"===t.type){if(n.startName=n.startName||"dr_start",n.endName=n.endName||"dr_end",n.fieldName=n.fieldName||"dr_field",n.format=n.format||"YYYY-MM-DD",n.displayFormat=n.displayFormat||"MMM DD, YYYY",n.separator=n.separator||" to ",n.label=n.label||"Date Range",e&&"object"==typeof e){const t=t=>{if(!t&&0!==t)return"";if(t instanceof Date&&!isNaN(t))return t.toISOString().slice(0,10);const e=String(t).trim();if(!e)return"";if(/^-?\d+$/.test(e)){const t=Number(e),i=e.length<=10?1e3*t:t,n=new Date(i);if(!isNaN(n))return n.toISOString().slice(0,10)}const i=new Date(e);return isNaN(i)?e:i.toISOString().slice(0,10)};n.startDate=t(e.start||e.from||e.begin||""),n.endDate=t(e.end||e.to||e.finish||"")}}else if("multiselect"===t.type){let i=[];e&&(Array.isArray(e)?i=e:"string"==typeof e&&(i=e.split(",").map(t=>t.trim()).filter(t=>t))),n.value=i,n.placeholder||n.placeHolder||(t.placeholder||t.placeHolder?n.placeholder=t.placeholder||t.placeHolder:t.label&&(n.placeholder=`Select ${t.label}...`))}return n}extractFilterValue(t,e){if("daterange"===t.type){const i=t.startName||"dr_start",n=t.endName||"dr_end";return{start:e[i],end:e[n]}}return t.type,e.filter_value}async applyFilters(){if(this.collection&&(this.collection.params.start=0),this.collection?.restEnabled)try{await this.collection.fetch(),this.render()}catch(t){console.error("Failed to fetch filtered data:",t),this.render()}else this.render();this.updateFilterPills(),this.emit("params-changed")}async onActionEditFilter(t,e){const i=e.getAttribute("data-filter"),{field:n}=d(i);let s=this.getFilterConfig(n)||this.getFilterConfig(i);const l=this.getActiveFilters(),o=l[i]||l[n];if(!s)return void console.warn("No filter config found for key:",i,"or field:",n);const r={filter_value:o};if("daterange"===s.type&&o&&"object"==typeof o){const t=s.startName||"dr_start",e=s.endName||"dr_end";r[t]=o.start||"",r[e]=o.end||""}const c=await a.showForm({title:`Edit ${this.getFilterLabel(n)} Filter`,size:"md",data:r,fields:[this.buildFilterDialogField(s,o,n)]});if(c){const t=this.extractFilterValue(s,c);this.setFilter(i,t),await this.applyFilters()}}async onActionRemoveFilter(t,e){const i=e.getAttribute("data-filter"),{field:n}=d(i);this.setFilter(i,null),"search"===i&&this.updateSearchInputs(""),this.collection.restEnabled&&await this.collection.fetch(),this.render(),this.updateFilterPills(),this.emit("filter:remove",{key:i,field:n}),this.emit("params-changed")}async onActionClearAllFilters(t,e){if(!this.collection)return;const{start:i,size:n,sort:s}=this.collection.params;this.collection.params={start:i,size:n},s&&(this.collection.params.sort=s),this.updateSearchInputs(""),this.collection.restEnabled&&await this.collection.fetch(),this.render(),this.updateFilterPills(),this.emit("filters:clear"),this.emit("params-changed")}updateBatchActionsPanel(){if(!this.batchActions||0===this.batchActions.length)return;const t=this.getSelectedItems().length;if("top"===this.batchBarLocation){const e=this.element?.querySelector(".batch-actions-panel-top"),i=this.element?.querySelector(".batch-select-count");e&&i&&(i.textContent=t,t>0?e.classList.remove("d-none"):e.classList.add("d-none"))}else{const e=this.element?.querySelector(".batch-actions-panel"),i=this.element?.querySelector(".batch-select-count");e&&i&&(i.textContent=t,e.style.display=t>0?"block":"none")}const e=this.element?.querySelector(".mojo-select-all-cell");if(e){const t=this.itemViews.size>0&&Array.from(this.itemViews.values()).every(t=>t.selected),i=Array.from(this.itemViews.values()).some(t=>t.selected);e.classList.toggle("selected",t),e.classList.toggle("indeterminate",!t&&i);const n=e.querySelector("i");n&&(n.className=!t&&i?"bi bi-dash":"bi bi-check")}}async onActionBatch(t,e){const i=e.getAttribute("data-action").replace("batch-",""),n=this.getSelectedItems();this.emit("batch:action",{action:i,items:n,event:t})}async onActionClearSelection(t,e){this.clearSelection(),this.updateBatchActionsPanel()}async onActionCustomToolbarButton(t,e){const i=parseInt(e.getAttribute("data-button-index"),10),n=this.toolbarButtons[i];n&&"function"==typeof n.handler&&await n.handler.call(this,t,e)}}export{u as D,c as L,MemberList as M,TableView as T,TableRow as a,Member as b,Log as c,LogList as d,r as e,h as f,d as p};
2
- //# sourceMappingURL=TableView-CWk5k4LQ.js.map
@@ -1,3 +0,0 @@
1
- "use strict";exports.ToastService=class{constructor(t={}){this.options={containerId:"toast-container",position:"top-end",autohide:!0,defaultDelay:5e3,maxToasts:5,...t},this.toasts=/* @__PURE__ */new Map,this.toastCounter=0,this.init()}init(){this.createContainer()}createContainer(){let t=document.getElementById(this.options.containerId);t||(t=document.createElement("div"),t.id=this.options.containerId,t.className=`toast-container position-fixed ${this.getPositionClasses()}`,t.style.zIndex="1070",t.setAttribute("aria-live","polite"),t.setAttribute("aria-atomic","true"),document.body.appendChild(t)),this.container=t}getPositionClasses(){const t={"top-start":"top-0 start-0 p-3","top-center":"top-0 start-50 translate-middle-x p-3","top-end":"top-0 end-0 p-3","middle-start":"top-50 start-0 translate-middle-y p-3","middle-center":"top-50 start-50 translate-middle p-3","middle-end":"top-50 end-0 translate-middle-y p-3","bottom-start":"bottom-0 start-0 p-3","bottom-center":"bottom-0 start-50 translate-middle-x p-3","bottom-end":"bottom-0 end-0 p-3"};return t[this.options.position]||t["top-end"]}success(t,e={}){return this.show(t,"success",{icon:"bi-check-circle-fill",...e})}error(t,e={}){return this.show(t,"error",{icon:"bi-exclamation-triangle-fill",autohide:!0,...e})}info(t,e={}){return this.show(t,"info",{icon:"bi-info-circle-fill",...e})}warning(t,e={}){return this.show(t,"warning",{icon:"bi-exclamation-triangle-fill",...e})}plain(t,e={}){return this.show(t,"plain",{...e})}show(t,e="info",s={}){this.enforceMaxToasts();const i="toast-"+ ++this.toastCounter,o={title:this.getDefaultTitle(e),icon:this.getDefaultIcon(e),autohide:this.options.autohide,delay:this.options.defaultDelay,dismissible:!0,...s},a=this.createToastElement(i,t,e,o);if(this.container.appendChild(a),"undefined"==typeof bootstrap)throw new Error("Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.");const n=new bootstrap.Toast(a,{autohide:o.autohide,delay:o.delay});return this.toasts.set(i,{element:a,bootstrap:n,type:e,message:t}),a.addEventListener("hidden.bs.toast",()=>{this.cleanup(i)}),n.show(),{id:i,hide:()=>{try{n.hide()}catch(t){console.warn("Error hiding toast:",t)}},dispose:()=>this.cleanup(i),updateProgress:s.updateProgress||null}}showView(t,e="info",s={}){this.enforceMaxToasts();const i="toast-"+ ++this.toastCounter,o={title:s.title||this.getDefaultTitle(e),icon:s.icon||this.getDefaultIcon(e),autohide:this.options.autohide,delay:this.options.defaultDelay,dismissible:!0,...s},a=this.createViewToastElement(i,t,e,o);if(this.container.appendChild(a),"undefined"==typeof bootstrap)throw new Error("Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.");const n=new bootstrap.Toast(a,{autohide:o.autohide,delay:o.delay});this.toasts.set(i,{element:a,bootstrap:n,type:e,view:t,message:"View toast"}),a.addEventListener("hidden.bs.toast",()=>{this.cleanupView(i)});const r=a.querySelector(".toast-view-body");return r&&t&&t.render(!0,r),n.show(),{id:i,view:t,hide:()=>{try{n.hide()}catch(t){console.warn("Error hiding view toast:",t)}},dispose:()=>this.cleanupView(i),updateProgress:e=>{t&&"function"==typeof t.updateProgress&&t.updateProgress(e)}}}createToastElement(t,e,s,i){const o=document.createElement("div");o.id=t,o.className=`toast toast-service-${s}`,o.setAttribute("role","alert"),o.setAttribute("aria-live","assertive"),o.setAttribute("aria-atomic","true");const a=i.title||i.icon?this.createToastHeader(i,s):"",n=this.createToastBody(e,i.icon&&!i.title);return o.innerHTML=`\n ${a}\n ${n}\n `,o}createViewToastElement(t,e,s,i){const o=document.createElement("div");o.id=t,o.className=`toast toast-service-${s}`,o.setAttribute("role","alert"),o.setAttribute("aria-live","assertive"),o.setAttribute("aria-atomic","true");const a=i.title||i.icon?this.createToastHeader(i,s):"",n=this.createViewToastBody();return o.innerHTML=`\n ${a}\n ${n}\n `,o}createViewToastBody(){return'\n <div class="toast-body p-0">\n <div class="toast-view-body p-3"></div>\n </div>\n '}createToastHeader(t,e){const s=t.icon?`<i class="${t.icon} toast-service-icon me-2"></i>`:"",i=t.title?`<strong class="me-auto">${s}${this.escapeHtml(t.title)}</strong>`:"",o=t.showTime?`<small class="text-muted">${this.getTimeString()}</small>`:"",a=t.dismissible?'<button type="button" class="btn-close toast-service-close" data-bs-dismiss="toast" aria-label="Close"></button>':"";return i||o||a?`\n <div class="toast-header">\n ${i}\n ${o}\n ${a}\n </div>\n `:""}createToastBody(t,e=!1){return`\n <div class="toast-body d-flex align-items-center">\n ${e?`<i class="${this.getDefaultIcon("info")} toast-service-icon me-2"></i>`:""}\n <span>${this.escapeHtml(t)}</span>\n </div>\n `}getDefaultTitle(t){return{success:"Success",error:"Error",warning:"Warning",info:"Information",plain:""}[t]||"Notification"}getDefaultIcon(t){return{success:"bi-check-circle-fill",error:"bi-exclamation-triangle-fill",warning:"bi-exclamation-triangle-fill",info:"bi-info-circle-fill",plain:""}[t]||"bi-info-circle-fill"}enforceMaxToasts(){if(Array.from(this.toasts.values()).length>=this.options.maxToasts){const t=this.toasts.keys().next().value,e=this.toasts.get(t);e&&e.bootstrap.hide()}}cleanup(t){const e=this.toasts.get(t);if(e){try{e.bootstrap.dispose()}catch(s){console.warn("Error disposing toast:",s)}e.element&&e.element.parentNode&&e.element.parentNode.removeChild(e.element),this.toasts.delete(t)}}cleanupView(t){const e=this.toasts.get(t);if(e){if(e.view&&"function"==typeof e.view.dispose)try{e.view.dispose()}catch(s){console.warn("Error disposing view in toast:",s)}try{e.bootstrap.dispose()}catch(s){console.warn("Error disposing toast:",s)}e.element&&e.element.parentNode&&e.element.parentNode.removeChild(e.element),this.toasts.delete(t)}}hideAll(){this.toasts.forEach((t,e)=>{t.bootstrap.hide()})}clearAll(){this.toasts.forEach((t,e)=>{this.cleanup(e)})}getTimeString(){/* @__PURE__ */
2
- return(new Date).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}escapeHtml(t){const e=document.createElement("div");return e.textContent=t,e.innerHTML}dispose(){this.clearAll(),this.container&&this.container.parentNode&&this.container.parentNode.removeChild(this.container)}getStats(){const t={total:this.toasts.size,byType:{}};return this.toasts.forEach(e=>{t.byType[e.type]=(t.byType[e.type]||0)+1}),t}setOptions(t){this.options={...this.options,...t},t.position&&this.container&&(this.container.className=`toast-container position-fixed ${this.getPositionClasses()}`)}};
3
- //# sourceMappingURL=ToastService-C2tTooFn.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ToastService-C2tTooFn.js","sources":["../../src/core/services/ToastService.js"],"sourcesContent":["/**\n * ToastService - Bootstrap 5 Toast Notification Service\n * Provides methods to display toast notifications with different types and options\n *\n * Features:\n * - Bootstrap 5 toast integration\n * - Multiple toast types (success, error, info, warning)\n * - Auto-dismiss with customizable delays\n * - Toast container management\n * - Event integration\n * - Proper cleanup and memory management\n *\n * @example\n * const toastService = new ToastService();\n * toastService.success('Operation completed successfully!');\n * toastService.error('Something went wrong');\n * toastService.info('FYI: This is informational');\n * toastService.warning('Please be careful');\n */\n\nclass ToastService {\n constructor(options = {}) {\n this.options = {\n containerId: 'toast-container',\n position: 'top-end', // top-start, top-center, top-end, middle-start, etc.\n autohide: true,\n defaultDelay: 5000, // 5 seconds\n maxToasts: 5, // Maximum number of toasts to show at once\n ...options\n };\n\n this.toasts = new Map(); // Track active toasts\n this.toastCounter = 0; // For unique IDs\n\n this.init();\n }\n\n /**\n * Initialize the toast service\n */\n init() {\n this.createContainer();\n }\n\n /**\n * Create the toast container if it doesn't exist\n */\n createContainer() {\n let container = document.getElementById(this.options.containerId);\n\n if (!container) {\n container = document.createElement('div');\n container.id = this.options.containerId;\n container.className = `toast-container position-fixed ${this.getPositionClasses()}`;\n container.style.zIndex = '1070'; // Bootstrap toast z-index\n container.setAttribute('aria-live', 'polite');\n container.setAttribute('aria-atomic', 'true');\n\n document.body.appendChild(container);\n }\n\n this.container = container;\n }\n\n /**\n * Get CSS classes for toast positioning\n */\n getPositionClasses() {\n const positionMap = {\n 'top-start': 'top-0 start-0 p-3',\n 'top-center': 'top-0 start-50 translate-middle-x p-3',\n 'top-end': 'top-0 end-0 p-3',\n 'middle-start': 'top-50 start-0 translate-middle-y p-3',\n 'middle-center': 'top-50 start-50 translate-middle p-3',\n 'middle-end': 'top-50 end-0 translate-middle-y p-3',\n 'bottom-start': 'bottom-0 start-0 p-3',\n 'bottom-center': 'bottom-0 start-50 translate-middle-x p-3',\n 'bottom-end': 'bottom-0 end-0 p-3'\n };\n\n return positionMap[this.options.position] || positionMap['top-end'];\n }\n\n\n\n /**\n * Show a success toast\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n success(message, options = {}) {\n return this.show(message, 'success', {\n icon: 'bi-check-circle-fill',\n ...options\n });\n }\n\n /**\n * Show an error toast\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n error(message, options = {}) {\n return this.show(message, 'error', {\n icon: 'bi-exclamation-triangle-fill',\n autohide: true, // Keep error toasts visible until manually dismissed\n ...options\n });\n }\n\n /**\n * Show an info toast\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n info(message, options = {}) {\n return this.show(message, 'info', {\n icon: 'bi-info-circle-fill',\n ...options\n });\n }\n\n /**\n * Show a warning toast\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n warning(message, options = {}) {\n return this.show(message, 'warning', {\n icon: 'bi-exclamation-triangle-fill',\n ...options\n });\n }\n\n /**\n * Show a plain toast without specific styling\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n plain(message, options = {}) {\n return this.show(message, 'plain', {\n ...options\n });\n }\n\n /**\n * Show a toast with specified type and options\n * @param {string} message - The message to display\n * @param {string} type - Toast type (success, error, info, warning)\n * @param {object} options - Additional options\n */\n show(message, type = 'info', options = {}) {\n // Enforce max toasts limit\n this.enforceMaxToasts();\n\n const toastId = `toast-${++this.toastCounter}`;\n const config = {\n title: this.getDefaultTitle(type),\n icon: this.getDefaultIcon(type),\n autohide: this.options.autohide,\n delay: this.options.defaultDelay,\n dismissible: true,\n ...options\n };\n\n const toastElement = this.createToastElement(toastId, message, type, config);\n this.container.appendChild(toastElement);\n\n // Initialize Bootstrap toast\n if (typeof bootstrap === 'undefined') {\n throw new Error('Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.');\n }\n const bsToast = new bootstrap.Toast(toastElement, {\n autohide: config.autohide,\n delay: config.delay\n });\n\n // Store toast reference\n this.toasts.set(toastId, {\n element: toastElement,\n bootstrap: bsToast,\n type: type,\n message: message\n });\n\n // Setup cleanup on hide\n toastElement.addEventListener('hidden.bs.toast', () => {\n this.cleanup(toastId);\n });\n\n // Show the toast\n bsToast.show();\n\n return {\n id: toastId,\n hide: () => {\n try {\n bsToast.hide();\n } catch (error) {\n console.warn('Error hiding toast:', error);\n }\n },\n dispose: () => this.cleanup(toastId),\n updateProgress: options.updateProgress || null\n };\n }\n\n /**\n * Show a toast with a View component in the body\n * @param {View} view - The View component to display\n * @param {string} type - Toast type (success, error, info, warning, plain)\n * @param {object} options - Additional options\n */\n showView(view, type = 'info', options = {}) {\n // Enforce max toasts limit\n this.enforceMaxToasts();\n\n const toastId = `toast-${++this.toastCounter}`;\n const config = {\n title: options.title || this.getDefaultTitle(type),\n icon: options.icon || this.getDefaultIcon(type),\n autohide: this.options.autohide,\n delay: this.options.defaultDelay,\n dismissible: true,\n ...options\n };\n\n const toastElement = this.createViewToastElement(toastId, view, type, config);\n this.container.appendChild(toastElement);\n\n // Initialize Bootstrap toast\n if (typeof bootstrap === 'undefined') {\n throw new Error('Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.');\n }\n const bsToast = new bootstrap.Toast(toastElement, {\n autohide: config.autohide,\n delay: config.delay\n });\n\n // Store toast reference with view\n this.toasts.set(toastId, {\n element: toastElement,\n bootstrap: bsToast,\n type: type,\n view: view,\n message: 'View toast'\n });\n\n // Setup cleanup on hide - dispose view properly\n toastElement.addEventListener('hidden.bs.toast', () => {\n this.cleanupView(toastId);\n });\n\n // Mount and render the view\n const bodyContainer = toastElement.querySelector('.toast-view-body');\n if (bodyContainer && view) {\n view.render(true, bodyContainer);\n }\n\n // Show the toast\n bsToast.show();\n\n return {\n id: toastId,\n view: view,\n hide: () => {\n try {\n bsToast.hide();\n } catch (error) {\n console.warn('Error hiding view toast:', error);\n }\n },\n dispose: () => this.cleanupView(toastId),\n updateProgress: (progressInfo) => {\n if (view && typeof view.updateProgress === 'function') {\n view.updateProgress(progressInfo);\n }\n }\n };\n }\n\n /**\n * Create toast DOM element\n */\n createToastElement(id, message, type, config) {\n const toast = document.createElement('div');\n toast.id = id;\n toast.className = `toast toast-service-${type}`;\n toast.setAttribute('role', 'alert');\n toast.setAttribute('aria-live', 'assertive');\n toast.setAttribute('aria-atomic', 'true');\n\n const header = config.title || config.icon ? this.createToastHeader(config, type) : '';\n const body = this.createToastBody(message, config.icon && !config.title);\n\n toast.innerHTML = `\n ${header}\n ${body}\n `;\n\n return toast;\n }\n\n /**\n * Create toast DOM element for View component\n */\n createViewToastElement(id, view, type, config) {\n const toast = document.createElement('div');\n toast.id = id;\n toast.className = `toast toast-service-${type}`;\n toast.setAttribute('role', 'alert');\n toast.setAttribute('aria-live', 'assertive');\n toast.setAttribute('aria-atomic', 'true');\n\n const header = config.title || config.icon ? this.createToastHeader(config, type) : '';\n const body = this.createViewToastBody();\n\n toast.innerHTML = `\n ${header}\n ${body}\n `;\n\n return toast;\n }\n\n /**\n * Create toast body for View component\n */\n createViewToastBody() {\n return `\n <div class=\"toast-body p-0\">\n <div class=\"toast-view-body p-3\"></div>\n </div>\n `;\n }\n\n /**\n * Create toast header with title and icon\n */\n createToastHeader(config, _type) {\n const iconHtml = config.icon ?\n `<i class=\"${config.icon} toast-service-icon me-2\"></i>` : '';\n\n const titleHtml = config.title ?\n `<strong class=\"me-auto\">${iconHtml}${this.escapeHtml(config.title)}</strong>` : '';\n\n const timeHtml = config.showTime ?\n `<small class=\"text-muted\">${this.getTimeString()}</small>` : '';\n\n const closeButton = config.dismissible ?\n `<button type=\"button\" class=\"btn-close toast-service-close\" data-bs-dismiss=\"toast\" aria-label=\"Close\"></button>` : '';\n\n if (!titleHtml && !timeHtml && !closeButton) {\n return '';\n }\n\n return `\n <div class=\"toast-header\">\n ${titleHtml}\n ${timeHtml}\n ${closeButton}\n </div>\n `;\n }\n\n /**\n * Create toast body with message\n */\n createToastBody(message, showIcon = false) {\n const iconHtml = showIcon ?\n `<i class=\"${this.getDefaultIcon('info')} toast-service-icon me-2\"></i>` : '';\n\n return `\n <div class=\"toast-body d-flex align-items-center\">\n ${iconHtml}\n <span>${this.escapeHtml(message)}</span>\n </div>\n `;\n }\n\n /**\n * Get default title for toast type\n */\n getDefaultTitle(type) {\n const titles = {\n success: 'Success',\n error: 'Error',\n warning: 'Warning',\n info: 'Information',\n plain: ''\n };\n return titles[type] || 'Notification';\n }\n\n /**\n * Get default icon for toast type\n */\n getDefaultIcon(type) {\n const icons = {\n success: 'bi-check-circle-fill',\n error: 'bi-exclamation-triangle-fill',\n warning: 'bi-exclamation-triangle-fill',\n info: 'bi-info-circle-fill',\n plain: ''\n };\n return icons[type] || 'bi-info-circle-fill';\n }\n\n /**\n * Enforce maximum number of toasts\n */\n enforceMaxToasts() {\n const activeToasts = Array.from(this.toasts.values());\n\n if (activeToasts.length >= this.options.maxToasts) {\n // Remove oldest toast\n const oldestId = this.toasts.keys().next().value;\n const oldest = this.toasts.get(oldestId);\n\n if (oldest) {\n oldest.bootstrap.hide();\n }\n }\n }\n\n /**\n * Clean up toast resources\n */\n cleanup(toastId) {\n const toast = this.toasts.get(toastId);\n\n if (toast) {\n // Dispose Bootstrap toast\n try {\n toast.bootstrap.dispose();\n } catch (e) {\n console.warn('Error disposing toast:', e);\n }\n\n // Remove from DOM\n if (toast.element && toast.element.parentNode) {\n toast.element.parentNode.removeChild(toast.element);\n }\n\n // Remove from tracking\n this.toasts.delete(toastId);\n }\n }\n\n /**\n * Clean up view toast resources with proper view disposal\n */\n cleanupView(toastId) {\n const toast = this.toasts.get(toastId);\n\n if (toast) {\n // Dispose view first if it exists\n if (toast.view && typeof toast.view.dispose === 'function') {\n try {\n toast.view.dispose();\n } catch (e) {\n console.warn('Error disposing view in toast:', e);\n }\n }\n\n // Dispose Bootstrap toast\n try {\n toast.bootstrap.dispose();\n } catch (e) {\n console.warn('Error disposing toast:', e);\n }\n\n // Remove from DOM\n if (toast.element && toast.element.parentNode) {\n toast.element.parentNode.removeChild(toast.element);\n }\n\n // Remove from tracking\n this.toasts.delete(toastId);\n }\n }\n\n /**\n * Hide all active toasts\n */\n hideAll() {\n this.toasts.forEach((toast, _id) => {\n toast.bootstrap.hide();\n });\n }\n\n /**\n * Clear all toasts immediately\n */\n clearAll() {\n this.toasts.forEach((toast, id) => {\n this.cleanup(id);\n });\n }\n\n /**\n * Get current time string\n */\n getTimeString() {\n return new Date().toLocaleTimeString([], {\n hour: '2-digit',\n minute: '2-digit'\n });\n }\n\n /**\n * Escape HTML to prevent XSS\n */\n escapeHtml(str) {\n const div = document.createElement('div');\n div.textContent = str;\n return div.innerHTML;\n }\n\n /**\n * Dispose of the entire toast service\n */\n dispose() {\n this.clearAll();\n\n if (this.container && this.container.parentNode) {\n this.container.parentNode.removeChild(this.container);\n }\n }\n\n /**\n * Get statistics about active toasts\n */\n getStats() {\n const stats = {\n total: this.toasts.size,\n byType: {}\n };\n\n this.toasts.forEach(toast => {\n stats.byType[toast.type] = (stats.byType[toast.type] || 0) + 1;\n });\n\n return stats;\n }\n\n /**\n * Set global options\n */\n setOptions(newOptions) {\n this.options = { ...this.options, ...newOptions };\n\n // Recreate container if position changed\n if (newOptions.position) {\n if (this.container) {\n this.container.className = `toast-container position-fixed ${this.getPositionClasses()}`;\n }\n }\n }\n}\n\nexport default ToastService;\n"],"names":["constructor","options","this","containerId","position","autohide","defaultDelay","maxToasts","toasts","Map","toastCounter","init","createContainer","container","document","getElementById","createElement","id","className","getPositionClasses","style","zIndex","setAttribute","body","appendChild","positionMap","success","message","show","icon","error","info","warning","plain","type","enforceMaxToasts","toastId","config","title","getDefaultTitle","getDefaultIcon","delay","dismissible","toastElement","createToastElement","bootstrap","Error","bsToast","Toast","set","element","addEventListener","cleanup","hide","console","warn","dispose","updateProgress","showView","view","createViewToastElement","cleanupView","bodyContainer","querySelector","render","progressInfo","toast","header","createToastHeader","createToastBody","innerHTML","createViewToastBody","_type","iconHtml","titleHtml","escapeHtml","timeHtml","showTime","getTimeString","closeButton","showIcon","Array","from","values","length","oldestId","keys","next","value","oldest","get","e","parentNode","removeChild","delete","hideAll","forEach","_id","clearAll","Date","toLocaleTimeString","hour","minute","str","div","textContent","getStats","stats","total","size","byType","setOptions","newOptions"],"mappings":"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"}
@@ -1,3 +0,0 @@
1
- class ToastService{constructor(t={}){this.options={containerId:"toast-container",position:"top-end",autohide:!0,defaultDelay:5e3,maxToasts:5,...t},this.toasts=/* @__PURE__ */new Map,this.toastCounter=0,this.init()}init(){this.createContainer()}createContainer(){let t=document.getElementById(this.options.containerId);t||(t=document.createElement("div"),t.id=this.options.containerId,t.className=`toast-container position-fixed ${this.getPositionClasses()}`,t.style.zIndex="1070",t.setAttribute("aria-live","polite"),t.setAttribute("aria-atomic","true"),document.body.appendChild(t)),this.container=t}getPositionClasses(){const t={"top-start":"top-0 start-0 p-3","top-center":"top-0 start-50 translate-middle-x p-3","top-end":"top-0 end-0 p-3","middle-start":"top-50 start-0 translate-middle-y p-3","middle-center":"top-50 start-50 translate-middle p-3","middle-end":"top-50 end-0 translate-middle-y p-3","bottom-start":"bottom-0 start-0 p-3","bottom-center":"bottom-0 start-50 translate-middle-x p-3","bottom-end":"bottom-0 end-0 p-3"};return t[this.options.position]||t["top-end"]}success(t,e={}){return this.show(t,"success",{icon:"bi-check-circle-fill",...e})}error(t,e={}){return this.show(t,"error",{icon:"bi-exclamation-triangle-fill",autohide:!0,...e})}info(t,e={}){return this.show(t,"info",{icon:"bi-info-circle-fill",...e})}warning(t,e={}){return this.show(t,"warning",{icon:"bi-exclamation-triangle-fill",...e})}plain(t,e={}){return this.show(t,"plain",{...e})}show(t,e="info",s={}){this.enforceMaxToasts();const i="toast-"+ ++this.toastCounter,o={title:this.getDefaultTitle(e),icon:this.getDefaultIcon(e),autohide:this.options.autohide,delay:this.options.defaultDelay,dismissible:!0,...s},a=this.createToastElement(i,t,e,o);if(this.container.appendChild(a),"undefined"==typeof bootstrap)throw new Error("Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.");const n=new bootstrap.Toast(a,{autohide:o.autohide,delay:o.delay});return this.toasts.set(i,{element:a,bootstrap:n,type:e,message:t}),a.addEventListener("hidden.bs.toast",()=>{this.cleanup(i)}),n.show(),{id:i,hide:()=>{try{n.hide()}catch(t){console.warn("Error hiding toast:",t)}},dispose:()=>this.cleanup(i),updateProgress:s.updateProgress||null}}showView(t,e="info",s={}){this.enforceMaxToasts();const i="toast-"+ ++this.toastCounter,o={title:s.title||this.getDefaultTitle(e),icon:s.icon||this.getDefaultIcon(e),autohide:this.options.autohide,delay:this.options.defaultDelay,dismissible:!0,...s},a=this.createViewToastElement(i,t,e,o);if(this.container.appendChild(a),"undefined"==typeof bootstrap)throw new Error("Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.");const n=new bootstrap.Toast(a,{autohide:o.autohide,delay:o.delay});this.toasts.set(i,{element:a,bootstrap:n,type:e,view:t,message:"View toast"}),a.addEventListener("hidden.bs.toast",()=>{this.cleanupView(i)});const r=a.querySelector(".toast-view-body");return r&&t&&t.render(!0,r),n.show(),{id:i,view:t,hide:()=>{try{n.hide()}catch(t){console.warn("Error hiding view toast:",t)}},dispose:()=>this.cleanupView(i),updateProgress:e=>{t&&"function"==typeof t.updateProgress&&t.updateProgress(e)}}}createToastElement(t,e,s,i){const o=document.createElement("div");o.id=t,o.className=`toast toast-service-${s}`,o.setAttribute("role","alert"),o.setAttribute("aria-live","assertive"),o.setAttribute("aria-atomic","true");const a=i.title||i.icon?this.createToastHeader(i,s):"",n=this.createToastBody(e,i.icon&&!i.title);return o.innerHTML=`\n ${a}\n ${n}\n `,o}createViewToastElement(t,e,s,i){const o=document.createElement("div");o.id=t,o.className=`toast toast-service-${s}`,o.setAttribute("role","alert"),o.setAttribute("aria-live","assertive"),o.setAttribute("aria-atomic","true");const a=i.title||i.icon?this.createToastHeader(i,s):"",n=this.createViewToastBody();return o.innerHTML=`\n ${a}\n ${n}\n `,o}createViewToastBody(){return'\n <div class="toast-body p-0">\n <div class="toast-view-body p-3"></div>\n </div>\n '}createToastHeader(t,e){const s=t.icon?`<i class="${t.icon} toast-service-icon me-2"></i>`:"",i=t.title?`<strong class="me-auto">${s}${this.escapeHtml(t.title)}</strong>`:"",o=t.showTime?`<small class="text-muted">${this.getTimeString()}</small>`:"",a=t.dismissible?'<button type="button" class="btn-close toast-service-close" data-bs-dismiss="toast" aria-label="Close"></button>':"";return i||o||a?`\n <div class="toast-header">\n ${i}\n ${o}\n ${a}\n </div>\n `:""}createToastBody(t,e=!1){return`\n <div class="toast-body d-flex align-items-center">\n ${e?`<i class="${this.getDefaultIcon("info")} toast-service-icon me-2"></i>`:""}\n <span>${this.escapeHtml(t)}</span>\n </div>\n `}getDefaultTitle(t){return{success:"Success",error:"Error",warning:"Warning",info:"Information",plain:""}[t]||"Notification"}getDefaultIcon(t){return{success:"bi-check-circle-fill",error:"bi-exclamation-triangle-fill",warning:"bi-exclamation-triangle-fill",info:"bi-info-circle-fill",plain:""}[t]||"bi-info-circle-fill"}enforceMaxToasts(){if(Array.from(this.toasts.values()).length>=this.options.maxToasts){const t=this.toasts.keys().next().value,e=this.toasts.get(t);e&&e.bootstrap.hide()}}cleanup(t){const e=this.toasts.get(t);if(e){try{e.bootstrap.dispose()}catch(s){console.warn("Error disposing toast:",s)}e.element&&e.element.parentNode&&e.element.parentNode.removeChild(e.element),this.toasts.delete(t)}}cleanupView(t){const e=this.toasts.get(t);if(e){if(e.view&&"function"==typeof e.view.dispose)try{e.view.dispose()}catch(s){console.warn("Error disposing view in toast:",s)}try{e.bootstrap.dispose()}catch(s){console.warn("Error disposing toast:",s)}e.element&&e.element.parentNode&&e.element.parentNode.removeChild(e.element),this.toasts.delete(t)}}hideAll(){this.toasts.forEach((t,e)=>{t.bootstrap.hide()})}clearAll(){this.toasts.forEach((t,e)=>{this.cleanup(e)})}getTimeString(){/* @__PURE__ */
2
- return(new Date).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}escapeHtml(t){const e=document.createElement("div");return e.textContent=t,e.innerHTML}dispose(){this.clearAll(),this.container&&this.container.parentNode&&this.container.parentNode.removeChild(this.container)}getStats(){const t={total:this.toasts.size,byType:{}};return this.toasts.forEach(e=>{t.byType[e.type]=(t.byType[e.type]||0)+1}),t}setOptions(t){this.options={...this.options,...t},t.position&&this.container&&(this.container.className=`toast-container position-fixed ${this.getPositionClasses()}`)}}export{ToastService as T};
3
- //# sourceMappingURL=ToastService-nUaGVpSl.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ToastService-nUaGVpSl.js","sources":["../../src/core/services/ToastService.js"],"sourcesContent":["/**\n * ToastService - Bootstrap 5 Toast Notification Service\n * Provides methods to display toast notifications with different types and options\n *\n * Features:\n * - Bootstrap 5 toast integration\n * - Multiple toast types (success, error, info, warning)\n * - Auto-dismiss with customizable delays\n * - Toast container management\n * - Event integration\n * - Proper cleanup and memory management\n *\n * @example\n * const toastService = new ToastService();\n * toastService.success('Operation completed successfully!');\n * toastService.error('Something went wrong');\n * toastService.info('FYI: This is informational');\n * toastService.warning('Please be careful');\n */\n\nclass ToastService {\n constructor(options = {}) {\n this.options = {\n containerId: 'toast-container',\n position: 'top-end', // top-start, top-center, top-end, middle-start, etc.\n autohide: true,\n defaultDelay: 5000, // 5 seconds\n maxToasts: 5, // Maximum number of toasts to show at once\n ...options\n };\n\n this.toasts = new Map(); // Track active toasts\n this.toastCounter = 0; // For unique IDs\n\n this.init();\n }\n\n /**\n * Initialize the toast service\n */\n init() {\n this.createContainer();\n }\n\n /**\n * Create the toast container if it doesn't exist\n */\n createContainer() {\n let container = document.getElementById(this.options.containerId);\n\n if (!container) {\n container = document.createElement('div');\n container.id = this.options.containerId;\n container.className = `toast-container position-fixed ${this.getPositionClasses()}`;\n container.style.zIndex = '1070'; // Bootstrap toast z-index\n container.setAttribute('aria-live', 'polite');\n container.setAttribute('aria-atomic', 'true');\n\n document.body.appendChild(container);\n }\n\n this.container = container;\n }\n\n /**\n * Get CSS classes for toast positioning\n */\n getPositionClasses() {\n const positionMap = {\n 'top-start': 'top-0 start-0 p-3',\n 'top-center': 'top-0 start-50 translate-middle-x p-3',\n 'top-end': 'top-0 end-0 p-3',\n 'middle-start': 'top-50 start-0 translate-middle-y p-3',\n 'middle-center': 'top-50 start-50 translate-middle p-3',\n 'middle-end': 'top-50 end-0 translate-middle-y p-3',\n 'bottom-start': 'bottom-0 start-0 p-3',\n 'bottom-center': 'bottom-0 start-50 translate-middle-x p-3',\n 'bottom-end': 'bottom-0 end-0 p-3'\n };\n\n return positionMap[this.options.position] || positionMap['top-end'];\n }\n\n\n\n /**\n * Show a success toast\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n success(message, options = {}) {\n return this.show(message, 'success', {\n icon: 'bi-check-circle-fill',\n ...options\n });\n }\n\n /**\n * Show an error toast\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n error(message, options = {}) {\n return this.show(message, 'error', {\n icon: 'bi-exclamation-triangle-fill',\n autohide: true, // Keep error toasts visible until manually dismissed\n ...options\n });\n }\n\n /**\n * Show an info toast\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n info(message, options = {}) {\n return this.show(message, 'info', {\n icon: 'bi-info-circle-fill',\n ...options\n });\n }\n\n /**\n * Show a warning toast\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n warning(message, options = {}) {\n return this.show(message, 'warning', {\n icon: 'bi-exclamation-triangle-fill',\n ...options\n });\n }\n\n /**\n * Show a plain toast without specific styling\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n plain(message, options = {}) {\n return this.show(message, 'plain', {\n ...options\n });\n }\n\n /**\n * Show a toast with specified type and options\n * @param {string} message - The message to display\n * @param {string} type - Toast type (success, error, info, warning)\n * @param {object} options - Additional options\n */\n show(message, type = 'info', options = {}) {\n // Enforce max toasts limit\n this.enforceMaxToasts();\n\n const toastId = `toast-${++this.toastCounter}`;\n const config = {\n title: this.getDefaultTitle(type),\n icon: this.getDefaultIcon(type),\n autohide: this.options.autohide,\n delay: this.options.defaultDelay,\n dismissible: true,\n ...options\n };\n\n const toastElement = this.createToastElement(toastId, message, type, config);\n this.container.appendChild(toastElement);\n\n // Initialize Bootstrap toast\n if (typeof bootstrap === 'undefined') {\n throw new Error('Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.');\n }\n const bsToast = new bootstrap.Toast(toastElement, {\n autohide: config.autohide,\n delay: config.delay\n });\n\n // Store toast reference\n this.toasts.set(toastId, {\n element: toastElement,\n bootstrap: bsToast,\n type: type,\n message: message\n });\n\n // Setup cleanup on hide\n toastElement.addEventListener('hidden.bs.toast', () => {\n this.cleanup(toastId);\n });\n\n // Show the toast\n bsToast.show();\n\n return {\n id: toastId,\n hide: () => {\n try {\n bsToast.hide();\n } catch (error) {\n console.warn('Error hiding toast:', error);\n }\n },\n dispose: () => this.cleanup(toastId),\n updateProgress: options.updateProgress || null\n };\n }\n\n /**\n * Show a toast with a View component in the body\n * @param {View} view - The View component to display\n * @param {string} type - Toast type (success, error, info, warning, plain)\n * @param {object} options - Additional options\n */\n showView(view, type = 'info', options = {}) {\n // Enforce max toasts limit\n this.enforceMaxToasts();\n\n const toastId = `toast-${++this.toastCounter}`;\n const config = {\n title: options.title || this.getDefaultTitle(type),\n icon: options.icon || this.getDefaultIcon(type),\n autohide: this.options.autohide,\n delay: this.options.defaultDelay,\n dismissible: true,\n ...options\n };\n\n const toastElement = this.createViewToastElement(toastId, view, type, config);\n this.container.appendChild(toastElement);\n\n // Initialize Bootstrap toast\n if (typeof bootstrap === 'undefined') {\n throw new Error('Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.');\n }\n const bsToast = new bootstrap.Toast(toastElement, {\n autohide: config.autohide,\n delay: config.delay\n });\n\n // Store toast reference with view\n this.toasts.set(toastId, {\n element: toastElement,\n bootstrap: bsToast,\n type: type,\n view: view,\n message: 'View toast'\n });\n\n // Setup cleanup on hide - dispose view properly\n toastElement.addEventListener('hidden.bs.toast', () => {\n this.cleanupView(toastId);\n });\n\n // Mount and render the view\n const bodyContainer = toastElement.querySelector('.toast-view-body');\n if (bodyContainer && view) {\n view.render(true, bodyContainer);\n }\n\n // Show the toast\n bsToast.show();\n\n return {\n id: toastId,\n view: view,\n hide: () => {\n try {\n bsToast.hide();\n } catch (error) {\n console.warn('Error hiding view toast:', error);\n }\n },\n dispose: () => this.cleanupView(toastId),\n updateProgress: (progressInfo) => {\n if (view && typeof view.updateProgress === 'function') {\n view.updateProgress(progressInfo);\n }\n }\n };\n }\n\n /**\n * Create toast DOM element\n */\n createToastElement(id, message, type, config) {\n const toast = document.createElement('div');\n toast.id = id;\n toast.className = `toast toast-service-${type}`;\n toast.setAttribute('role', 'alert');\n toast.setAttribute('aria-live', 'assertive');\n toast.setAttribute('aria-atomic', 'true');\n\n const header = config.title || config.icon ? this.createToastHeader(config, type) : '';\n const body = this.createToastBody(message, config.icon && !config.title);\n\n toast.innerHTML = `\n ${header}\n ${body}\n `;\n\n return toast;\n }\n\n /**\n * Create toast DOM element for View component\n */\n createViewToastElement(id, view, type, config) {\n const toast = document.createElement('div');\n toast.id = id;\n toast.className = `toast toast-service-${type}`;\n toast.setAttribute('role', 'alert');\n toast.setAttribute('aria-live', 'assertive');\n toast.setAttribute('aria-atomic', 'true');\n\n const header = config.title || config.icon ? this.createToastHeader(config, type) : '';\n const body = this.createViewToastBody();\n\n toast.innerHTML = `\n ${header}\n ${body}\n `;\n\n return toast;\n }\n\n /**\n * Create toast body for View component\n */\n createViewToastBody() {\n return `\n <div class=\"toast-body p-0\">\n <div class=\"toast-view-body p-3\"></div>\n </div>\n `;\n }\n\n /**\n * Create toast header with title and icon\n */\n createToastHeader(config, _type) {\n const iconHtml = config.icon ?\n `<i class=\"${config.icon} toast-service-icon me-2\"></i>` : '';\n\n const titleHtml = config.title ?\n `<strong class=\"me-auto\">${iconHtml}${this.escapeHtml(config.title)}</strong>` : '';\n\n const timeHtml = config.showTime ?\n `<small class=\"text-muted\">${this.getTimeString()}</small>` : '';\n\n const closeButton = config.dismissible ?\n `<button type=\"button\" class=\"btn-close toast-service-close\" data-bs-dismiss=\"toast\" aria-label=\"Close\"></button>` : '';\n\n if (!titleHtml && !timeHtml && !closeButton) {\n return '';\n }\n\n return `\n <div class=\"toast-header\">\n ${titleHtml}\n ${timeHtml}\n ${closeButton}\n </div>\n `;\n }\n\n /**\n * Create toast body with message\n */\n createToastBody(message, showIcon = false) {\n const iconHtml = showIcon ?\n `<i class=\"${this.getDefaultIcon('info')} toast-service-icon me-2\"></i>` : '';\n\n return `\n <div class=\"toast-body d-flex align-items-center\">\n ${iconHtml}\n <span>${this.escapeHtml(message)}</span>\n </div>\n `;\n }\n\n /**\n * Get default title for toast type\n */\n getDefaultTitle(type) {\n const titles = {\n success: 'Success',\n error: 'Error',\n warning: 'Warning',\n info: 'Information',\n plain: ''\n };\n return titles[type] || 'Notification';\n }\n\n /**\n * Get default icon for toast type\n */\n getDefaultIcon(type) {\n const icons = {\n success: 'bi-check-circle-fill',\n error: 'bi-exclamation-triangle-fill',\n warning: 'bi-exclamation-triangle-fill',\n info: 'bi-info-circle-fill',\n plain: ''\n };\n return icons[type] || 'bi-info-circle-fill';\n }\n\n /**\n * Enforce maximum number of toasts\n */\n enforceMaxToasts() {\n const activeToasts = Array.from(this.toasts.values());\n\n if (activeToasts.length >= this.options.maxToasts) {\n // Remove oldest toast\n const oldestId = this.toasts.keys().next().value;\n const oldest = this.toasts.get(oldestId);\n\n if (oldest) {\n oldest.bootstrap.hide();\n }\n }\n }\n\n /**\n * Clean up toast resources\n */\n cleanup(toastId) {\n const toast = this.toasts.get(toastId);\n\n if (toast) {\n // Dispose Bootstrap toast\n try {\n toast.bootstrap.dispose();\n } catch (e) {\n console.warn('Error disposing toast:', e);\n }\n\n // Remove from DOM\n if (toast.element && toast.element.parentNode) {\n toast.element.parentNode.removeChild(toast.element);\n }\n\n // Remove from tracking\n this.toasts.delete(toastId);\n }\n }\n\n /**\n * Clean up view toast resources with proper view disposal\n */\n cleanupView(toastId) {\n const toast = this.toasts.get(toastId);\n\n if (toast) {\n // Dispose view first if it exists\n if (toast.view && typeof toast.view.dispose === 'function') {\n try {\n toast.view.dispose();\n } catch (e) {\n console.warn('Error disposing view in toast:', e);\n }\n }\n\n // Dispose Bootstrap toast\n try {\n toast.bootstrap.dispose();\n } catch (e) {\n console.warn('Error disposing toast:', e);\n }\n\n // Remove from DOM\n if (toast.element && toast.element.parentNode) {\n toast.element.parentNode.removeChild(toast.element);\n }\n\n // Remove from tracking\n this.toasts.delete(toastId);\n }\n }\n\n /**\n * Hide all active toasts\n */\n hideAll() {\n this.toasts.forEach((toast, _id) => {\n toast.bootstrap.hide();\n });\n }\n\n /**\n * Clear all toasts immediately\n */\n clearAll() {\n this.toasts.forEach((toast, id) => {\n this.cleanup(id);\n });\n }\n\n /**\n * Get current time string\n */\n getTimeString() {\n return new Date().toLocaleTimeString([], {\n hour: '2-digit',\n minute: '2-digit'\n });\n }\n\n /**\n * Escape HTML to prevent XSS\n */\n escapeHtml(str) {\n const div = document.createElement('div');\n div.textContent = str;\n return div.innerHTML;\n }\n\n /**\n * Dispose of the entire toast service\n */\n dispose() {\n this.clearAll();\n\n if (this.container && this.container.parentNode) {\n this.container.parentNode.removeChild(this.container);\n }\n }\n\n /**\n * Get statistics about active toasts\n */\n getStats() {\n const stats = {\n total: this.toasts.size,\n byType: {}\n };\n\n this.toasts.forEach(toast => {\n stats.byType[toast.type] = (stats.byType[toast.type] || 0) + 1;\n });\n\n return stats;\n }\n\n /**\n * Set global options\n */\n setOptions(newOptions) {\n this.options = { ...this.options, ...newOptions };\n\n // Recreate container if position changed\n if (newOptions.position) {\n if (this.container) {\n this.container.className = `toast-container position-fixed ${this.getPositionClasses()}`;\n }\n }\n }\n}\n\nexport default ToastService;\n"],"names":["ToastService","constructor","options","this","containerId","position","autohide","defaultDelay","maxToasts","toasts","Map","toastCounter","init","createContainer","container","document","getElementById","createElement","id","className","getPositionClasses","style","zIndex","setAttribute","body","appendChild","positionMap","success","message","show","icon","error","info","warning","plain","type","enforceMaxToasts","toastId","config","title","getDefaultTitle","getDefaultIcon","delay","dismissible","toastElement","createToastElement","bootstrap","Error","bsToast","Toast","set","element","addEventListener","cleanup","hide","console","warn","dispose","updateProgress","showView","view","createViewToastElement","cleanupView","bodyContainer","querySelector","render","progressInfo","toast","header","createToastHeader","createToastBody","innerHTML","createViewToastBody","_type","iconHtml","titleHtml","escapeHtml","timeHtml","showTime","getTimeString","closeButton","showIcon","Array","from","values","length","oldestId","keys","next","value","oldest","get","e","parentNode","removeChild","delete","hideAll","forEach","_id","clearAll","Date","toLocaleTimeString","hour","minute","str","div","textContent","getStats","stats","total","size","byType","setOptions","newOptions"],"mappings":"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"}