web-mojo 2.5.8 → 2.5.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/admin-models.es.js +1 -1
  3. package/dist/admin.cjs.js +1 -1
  4. package/dist/admin.cjs.js.map +1 -1
  5. package/dist/admin.es.js +1 -1
  6. package/dist/admin.es.js.map +1 -1
  7. package/dist/auth.cjs.js +1 -1
  8. package/dist/auth.es.js +1 -1
  9. package/dist/charts.cjs.js +1 -1
  10. package/dist/charts.es.js +1 -1
  11. package/dist/chunks/{ChatView-C27ckVwL.js → ChatView-CPaBPG5C.js} +2 -2
  12. package/dist/chunks/{ChatView-C27ckVwL.js.map → ChatView-CPaBPG5C.js.map} +1 -1
  13. package/dist/chunks/{ContextMenu-xdLpFeau.js → ContextMenu-Dch7L988.js} +2 -2
  14. package/dist/chunks/{ContextMenu-xdLpFeau.js.map → ContextMenu-Dch7L988.js.map} +1 -1
  15. package/dist/chunks/{DataView-DBQUt_vV.js → DataView-DhhTCP0d.js} +2 -2
  16. package/dist/chunks/{DataView-DBQUt_vV.js.map → DataView-DhhTCP0d.js.map} +1 -1
  17. package/dist/chunks/{FormView-DaZFbDWr.js → FormView-C6XZFZ6s.js} +2 -2
  18. package/dist/chunks/{FormView-DaZFbDWr.js.map → FormView-C6XZFZ6s.js.map} +1 -1
  19. package/dist/chunks/{ListView-C-jiqALE.js → ListView-McEedPG8.js} +2 -2
  20. package/dist/chunks/{ListView-C-jiqALE.js.map → ListView-McEedPG8.js.map} +1 -1
  21. package/dist/chunks/{MetricsCountryMapView-5r0SeCQw.js → MetricsCountryMapView-B9hNrGIG.js} +2 -2
  22. package/dist/chunks/{MetricsCountryMapView-5r0SeCQw.js.map → MetricsCountryMapView-B9hNrGIG.js.map} +1 -1
  23. package/dist/chunks/{Modal-Dgtnpj85.js → Modal-DfAzkbgB.js} +3 -3
  24. package/dist/chunks/{Modal-Dgtnpj85.js.map → Modal-DfAzkbgB.js.map} +1 -1
  25. package/dist/chunks/{Passkeys-B4bndv5b.js → Passkeys-C7YPKCKQ.js} +2 -2
  26. package/dist/chunks/{Passkeys-B4bndv5b.js.map → Passkeys-C7YPKCKQ.js.map} +1 -1
  27. package/dist/chunks/{TokenManager-DR5zQikX.js → TokenManager-CIGfr9LQ.js} +2 -2
  28. package/dist/chunks/{TokenManager-DR5zQikX.js.map → TokenManager-CIGfr9LQ.js.map} +1 -1
  29. package/dist/chunks/{User-DNifTiFu.js → User-DgB81g6W.js} +2 -2
  30. package/dist/chunks/{User-DNifTiFu.js.map → User-DgB81g6W.js.map} +1 -1
  31. package/dist/chunks/{UserProfileView-DugtA_qG.js → UserProfileView-Cn4x_As0.js} +2 -2
  32. package/dist/chunks/{UserProfileView-DugtA_qG.js.map → UserProfileView-Cn4x_As0.js.map} +1 -1
  33. package/dist/chunks/{WebApp-CNhEZYYG.js → WebApp-BLO494WW.js} +2 -2
  34. package/dist/chunks/{WebApp-CNhEZYYG.js.map → WebApp-BLO494WW.js.map} +1 -1
  35. package/dist/chunks/{admin-models-CdOCWMEj.js → admin-models-C8VPjEPG.js} +2 -2
  36. package/dist/chunks/{admin-models-CdOCWMEj.js.map → admin-models-C8VPjEPG.js.map} +1 -1
  37. package/dist/chunks/{exportChart-QDe89jLV.js → exportChart-9xYMybEK.js} +2 -2
  38. package/dist/chunks/{exportChart-QDe89jLV.js.map → exportChart-9xYMybEK.js.map} +1 -1
  39. package/dist/chunks/{index-D-gO-M9M.js → index-D09iCOVq.js} +2 -2
  40. package/dist/chunks/{index-D-gO-M9M.js.map → index-D09iCOVq.js.map} +1 -1
  41. package/dist/chunks/version-Bs_8ymHO.js +2 -0
  42. package/dist/chunks/version-Bs_8ymHO.js.map +1 -0
  43. package/dist/chunks/version-DlzdKqY9.js +2 -0
  44. package/dist/chunks/version-DlzdKqY9.js.map +1 -0
  45. package/dist/docit.cjs.js +1 -1
  46. package/dist/docit.es.js +1 -1
  47. package/dist/index.cjs.js +1 -1
  48. package/dist/index.es.js +1 -1
  49. package/dist/lightbox.cjs.js +1 -1
  50. package/dist/lightbox.es.js +1 -1
  51. package/dist/map.es.js +1 -1
  52. package/dist/timeline.es.js +1 -1
  53. package/dist/user-profile.es.js +1 -1
  54. package/package.json +1 -1
  55. package/dist/chunks/version-CBPFfIng.js +0 -2
  56. package/dist/chunks/version-CBPFfIng.js.map +0 -1
  57. package/dist/chunks/version-D8Oyq5Pb.js +0 -2
  58. package/dist/chunks/version-D8Oyq5Pb.js.map +0 -1
@@ -1,2 +1,2 @@
1
- import{C as e,M as t,d as s,r as n}from"./User-DNifTiFu.js";import{L as i,a as l}from"./ListView-C-jiqALE.js";class Log extends t{constructor(e={}){super(e,{endpoint:"/api/logs"})}}class LogList extends e{constructor(e={}){super({ModelClass:Log,endpoint:"/api/logs",size:10,...e})}}class Member extends t{constructor(e={}){super(e,{endpoint:"/api/group/member"})}hasPermission(e){if(Array.isArray(e))return e.some(e=>this.hasPermission(e));const t=this.get("permissions");return!!t&&1==t[e]}async fetchForGroup(e){return await this.fetch({url:`/api/group/${e}/member`})}}class MemberList extends e{constructor(e={}){super({ModelClass:Member,endpoint:"/api/group/member",size:10,...e})}}const a={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.BASE_PERMISSIONS=[{name:"manage_group",label:"Group Admin"},{name:"view_metrics",label:"View Metrics"},{name:"view_logs",label:"View Logs"},{name:"view_tickets",label:"View Tickets"},{name:"view_members",label:"View Members"},{name:"manage_members",label:"Manage Members"},{name:"view_billing",label:"View Billing"}],Member.APP_PERMISSIONS=[],Member.PERMISSIONS=[],Member.PERMISSION_FIELDS=[];const o=e=>({name:`permissions.${e.name}`,type:"switch",label:e.label,columns:6,...e.tooltip?{tooltip:e.tooltip}:{}});Member.rebuildPermissions=function(){Member.PERMISSIONS.length=0,Member.PERMISSIONS.push(...Member.BASE_PERMISSIONS,...Member.APP_PERMISSIONS),Member.PERMISSION_FIELDS.length=0,Member.PERMISSION_FIELDS.push(...Member.PERMISSIONS.map(o))},Member.rebuildPermissions(),Member.EDIT_FORM=a.edit,Member.ADD_FORM=a.create;class TableRow extends i{constructor(e={}){super({tagName:"tr",className:"table-row",enableTooltips:!0,...e}),this.columns=e.columns||[],this.actions=e.actions||null,this.contextMenu=e.contextMenu||null,this.batchActions=e.batchActions||null,this.tableView=e.tableView||e.listView||null,this.editingCells=/* @__PURE__ */new Set,this.template=this.buildRowTemplate()}getResponsiveClasses(e){if(!e)return"";const t=["sm","md","lg","xl","xxl"];if("string"==typeof e)return t.includes(e)?`d-none d-${e}-table-cell`:(console.warn(`Invalid visibility breakpoint: ${e}. Valid options are: ${t.join(", ")}`),"");if("object"==typeof e){const s=[];if(e.hide){if(!t.includes(e.hide))return console.warn(`Invalid hide breakpoint: ${e.hide}. Valid options are: ${t.join(", ")}`),"";s.push(`d-table-cell d-${e.hide}-none`)}if(e.show){if(!t.includes(e.show))return console.warn(`Invalid show breakpoint: ${e.show}. Valid options are: ${t.join(", ")}`),"";e.hide?s.push(`d-${e.show}-table-cell`):s.push(`d-none d-${e.show}-table-cell`)}return s.join(" ")}return""}buildRowTemplate(){let e="";return this.tableView&&this.tableView.isSelectable()&&(e+='\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((t,s)=>{const n=[t.class||t.className||"",this.getResponsiveClasses(t.visibility),t.editable?"editable-cell":"",this.tableView&&this.tableView.getAlignClass?this.tableView.getAlignClass(t.align):""].filter(e=>e).join(" "),i=this.buildCellTemplate(t,s);let l=t.action;!l&&t.editable?l="edit-cell":!l&&this.tableView.rowAction&&(l=this.tableView.rowAction),e+=l?`<td class="${n}" data-action="${l}" data-column="${t.key}">${i}</td>`:`<td class="${n}" data-column="${t.key}">${i}</td>`}),this.actions?e+=this.buildActionsTemplate():this.contextMenu&&(e+=this.buildContextMenuTemplate()),e}buildCellTemplate(e,t=0){const s=`model.${e.key}`,n=e.formatter||e.format,i=e.editable?` class="cell-content" data-field="${e.key}"`:"";if(n){if("string"==typeof n)return e.editable?`<span${i}>{{{${s}|${n}}}}</span>`:`{{{${s}|${n}}}}`;if("function"==typeof n){const n=e.editable?"cell-content":"",i=e.editable?` data-field="${e.key}"`:"";return`<span class="${n}" data-formatter="${e.key}" data-formatter-id="${t}"${i}>{{${s}}}</span>`}}return e.template?e.editable?`<span${i}>${e.template}</span>`:e.template:e.editable?`<span${i}>{{{${s}}}}</span>`:`{{{${s}}}}`}buildActionsTemplate(){return this.actions&&0!==this.actions.length?`<td><div class="btn-group btn-group-sm">${this.actions.map(e=>{if("string"==typeof e)switch(e){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 e)return`\n <button class="btn btn-sm ${e.class||"btn-outline-primary"}"\n data-id="${this.model.id}"\n data-action="${e.action}"\n title="${e.label||""}">\n ${e.icon?`<i class="${e.icon}"></i>`:""}\n ${e.label&&!e.icon?e.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(e=>{if(e.separator||e.divider)return'<li><hr class="dropdown-divider"></li>';let t="dropdown-item";return("delete"===e.action||e.danger)&&(t+=" text-danger"),e.disabled&&(t+=" disabled"),`\n <li>\n <a class="${t}" href="#"\n data-id="{{model.id}}"\n data-action="${e.action}"\n ${e.disabled?'aria-disabled="true" tabindex="-1"':""}>\n ${e.icon?`<i class="${e.icon} me-2"></i>`:""}\n ${e.label}\n </a>\n </li>\n `}).join("")}async onAfterRender(){await super.onAfterRender(),this.columns.forEach((e,t)=>{if(e.formatter&&"function"==typeof e.formatter){let n=this.element.querySelector(`[data-formatter-id="${t}"]`);if(n||(n=this.element.querySelector(`[data-formatter="${e.key}"]`)),n){const t=this.model.get?this.model.get(e.key):this.model[e.key],i={value:t,row:this.model,model:this.model,column:e,table:this.tableView,index:this.index};try{n.innerHTML=e.formatter(t,i)}catch(s){console.error(`Error formatting cell for column ${e.key}:`,s)}}}}),this.selected&&this.element.classList.add("selected");const e=this.model.get?this.model.get("id"):this.model.id;e&&this.element.setAttribute("data-id",e)}async onActionEditCell(e,t){e.stopPropagation();const s=t.getAttribute("data-column"),n=this.columns.find(e=>e.key===s);n&&n.editable&&(this.editingCells.has(s)||await this.enterEditMode(s,n,t))}async onActionRowClick(e,t){e.target.closest(".btn-group")||e.target.closest(".dropdown")||e.target.closest(".cell-editor")||(this.emit("row:click",{row:this,model:this.model,column:t.getAttribute("data-column"),event:e}),this.tableView&&this.tableView.emit("row:click",{row:this,model:this.model,column:t.getAttribute("data-column"),event:e}))}async onActionView(e,t){e.stopPropagation(),this.emit("row:view",{row:this,model:this.model,event:e}),this.tableView&&this.tableView.emit("row:view",{row:this,model:this.model,event:e})}async onActionEdit(e,t){return e.stopPropagation(),this.emit("row:edit",{row:this,model:this.model,event:e}),this.tableView&&this.tableView.emit("row:edit",{row:this,model:this.model,event:e}),!0}async onActionDelete(e,t){e.stopPropagation(),this.emit("row:delete",{row:this,model:this.model,event:e}),this.tableView&&this.tableView.emit("row:delete",{row:this,model:this.model,event:e})}async enterEditMode(e,t,s){const n=s.querySelector(".cell-content");if(!n)return;this.editingCells.add(e);const i=this.model.get?this.model.get(e):this.model[e],l=this.createCellEditor(t,i),a=n.innerHTML;n.style.display="none";const o=document.createElement("div");o.className="cell-editor",o.innerHTML=l,s.appendChild(o);const c=o.querySelector("input, select, .form-check-input");c&&(c.focus(),"text"!==c.type&&"textarea"!==c.type||c.select()),o.dataset.originalContent=a,o.dataset.columnKey=e,this.setupEditorEvents(o,e,t),this.emit("cell:edit",{row:this,model:this.model,column:e,originalValue:i})}createCellEditor(e,t){const s=e.editableOptions||{};switch(s.type){case"select":return this.createSelectEditor(s,t);case"switch":case"checkbox":return this.createSwitchEditor(s,t);case"textarea":return this.createTextareaEditor(s,t);default:return this.createTextEditor(s,t)}}createTextEditor(e,t){const s=e.placeholder||"";return`\n <div class="d-flex gap-1 align-items-center">\n <input type="${e.inputType||"text"}"\n class="form-control form-control-sm cell-input"\n value="${this.escapeHtml(t||"")}"\n placeholder="${s}">\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(e,t){const s=e.placeholder||"";return`\n <div class="d-flex gap-1">\n <textarea class="form-control form-control-sm cell-input"\n rows="${e.rows||2}"\n placeholder="${s}">${this.escapeHtml(t||"")}</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(e,t){const s=e.options||[];let n="";return s.forEach(e=>{if("string"==typeof e)n+=`<option value="${e}" ${e===t?"selected":""}>${e}</option>`;else if("object"==typeof e&&void 0!==e.value){const s=e.value===t?"selected":"";n+=`<option value="${e.value}" ${s}>${e.label||e.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(e,t){const s=t?"checked":"";return`\n <div class="d-flex gap-2 align-items-center">\n <div class="form-check ${"switch"===e.type?"form-switch":""}">\n <input class="form-check-input cell-input" type="checkbox" ${s}>\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(e,t,s){const n=e.querySelector(".cell-input"),i=e.querySelector(".cell-save"),l=e.querySelector(".cell-cancel");!n||"text"!==n.type&&"email"!==n.type&&"number"!==n.type||n.addEventListener("keydown",n=>{"Enter"===n.key?(n.preventDefault(),this.saveCellEdit(e,t,s)):"Escape"===n.key&&(n.preventDefault(),this.cancelCellEdit(e,t))}),!n||"checkbox"!==n.type&&"SELECT"!==n.tagName||!1===s.autoSave||n.addEventListener("change",()=>{this.saveCellEdit(e,t,s)}),i?.addEventListener("click",()=>{this.saveCellEdit(e,t,s)}),l?.addEventListener("click",()=>{this.cancelCellEdit(e,t)})}async saveCellEdit(e,t,s){const n=e.querySelector(".cell-input");if(!n)return;let i;i="checkbox"===n.type?n.checked:(n.tagName,n.value);const l=this.model.get?this.model.get(t):this.model[t];try{this.model.save?await this.model.save({[t]:i}):this.model[t]=i,this.exitEditMode(e,t,i),this.emit("cell:save",{row:this,model:this.model,column:t,oldValue:l,newValue:i})}catch(a){console.error("Failed to save cell edit:",a),this.emit("cell:save:error",{row:this,model:this.model,column:t,oldValue:l,newValue:i,error:a}),e.classList.add("saving-error"),setTimeout(()=>e.classList.remove("saving-error"),3e3)}}cancelCellEdit(e,t){const s=e.dataset.originalContent;this.exitEditMode(e,t,null,s),this.emit("cell:cancel",{row:this,model:this.model,column:t})}exitEditMode(e,t,n=null,i=null){const l=e.closest("td").querySelector(".cell-content");if(l){if(null!==n){const e=this.columns.find(e=>e.key===t);let i=n;e&&e.formatter&&"string"==typeof e.formatter&&(i=s.pipe(n,e.formatter)),l.innerHTML=this.escapeHtml(i)}else i&&(l.innerHTML=i);l.style.display=""}e.remove(),this.editingCells.delete(t)}escapeHtml(e){if(null==e)return"";const t=document.createElement("div");return t.textContent=e,t.innerHTML}select(){super.select(),this.addClass("selected");const e=this.element?.querySelector(".mojo-select-cell");e&&e.classList.add("selected")}deselect(){super.deselect(),this.removeClass("selected");const e=this.element?.querySelector(".mojo-select-cell");e&&e.classList.remove("selected")}}class TableView extends l{constructor(e={}){super({className:"table-view-component",itemClass:e.itemClass||TableRow,selectionMode:e.selectable?"multiple":"none",emptyMessage:e.emptyMessage||"No data available",addButtonIcon:e.addButtonIcon||"bi bi-plus-circle",...e}),this.isFullscreen=!1,this.columns=e.columns||[],this.actions=e.actions||null,this.contextMenu=e.contextMenu||null,this.batchActions=e.batchActions||null,this.searchable=!1!==e.searchable,this.sortable=!1!==e.sortable,this.filterable=!1!==e.filterable,this.paginated=!1!==e.paginated,this.paginationMode=e.paginationMode||"pages",this.persistSelection=!0===e.persistSelection,this.clickAction=e.clickAction||"view",this.fetchOnView=!1!==e.fetchOnView,this.itemView=e.itemView,this.addForm=e.addForm,this.editForm=e.editForm,this.deleteTemplate=e.deleteTemplate,this.formDialogConfig=e.formDialogConfig||{},this.viewDialogOptions=e.viewDialogOptions||{},this.exportOptions=e.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=e.exportSource||"remote",this.filters={},this.additionalFilters=e.filters||[],this.hideActivePills=!0===e.hideActivePills,this.hideActivePillNames=e.hideActivePillNames||[],this.rowAction=e.rowAction||"row-click",this.batchBarLocation=e.batchBarLocation||"bottom",this.options.addButtonLabel=e.addButtonLabel||"Add",this.toolbarButtons=e.toolbarButtons||[],this.toolbarRight=e.toolbarRight||null,this.title=e.title||null,this.eyebrow=e.eyebrow||null,this.showRefresh=!1!==e.showRefresh,this.showFullscreen=!1!==e.showFullscreen,this.tableOptions={striped:!0,bordered:!1,hover:!0,responsive:!1,size:null,...e.tableOptions},this.searchPlacement=e.searchPlacement||"toolbar",this.searchPlaceholder=e.searchPlaceholder||"Search...",this.initializeColumns(),this.extractColumnFilters(),this.footerTotalColumns=this.columns.filter(e=>!0===e.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(e=>{!e.key&&e.name&&(e.key=e.name),e.label||e.title||(e.label=e.key.charAt(0).toUpperCase()+e.key.slice(1))})}getResponsiveClasses(e){if(!e)return"";const t=["sm","md","lg","xl","xxl"];if("string"==typeof e)return t.includes(e)?`d-none d-${e}-table-cell`:(console.warn(`Invalid visibility breakpoint: ${e}. Valid options are: ${t.join(", ")}`),"");if("object"==typeof e){const s=[];if(e.hide){if(!t.includes(e.hide))return console.warn(`Invalid hide breakpoint: ${e.hide}. Valid options are: ${t.join(", ")}`),"";s.push(`d-table-cell d-${e.hide}-none`)}if(e.show){if(!t.includes(e.show))return console.warn(`Invalid show breakpoint: ${e.show}. Valid options are: ${t.join(", ")}`),"";e.hide?s.push(`d-${e.show}-table-cell`):s.push(`d-none d-${e.show}-table-cell`)}return s.join(" ")}return""}getAlignClass(e){if(!e)return"";return{left:"text-start",start:"text-start",center:"text-center",right:"text-end",end:"text-end"}[String(e).toLowerCase()]||(console.warn(`Invalid column align: ${e}. Valid options are: left, center, right`),"")}parseColumnKey(e){const t=e.split("|");return{fieldKey:t[0],formatter:t[1]||null}}updateFooterTotals(){if(!this.hasFooterTotals||!this.element)return;const e=this.calculateFooterTotals();let t=0;this.columns.forEach(s=>{if(s.footer_total){const n=`col_${t}`,i=this.element.querySelector(`[data-total-column="${n}"]`);if(i&&e[n]){const t=this.parseColumnKey(s.key).formatter||s.formatter;let l;l=t&&"string"==typeof t?this.formatValue(e[n].value,t):e[n].value,i.textContent=l}t++}})}formatValue(e,t){try{return s.pipe(e,t)}catch(n){return console.warn("Error formatting value:",n),e}}calculateFooterTotals(){if(!this.hasFooterTotals||!this.collection||0===this.collection.length)return{};const e={};return this.footerTotalColumns.forEach((t,s)=>{const{fieldKey:n,formatter:i}=this.parseColumnKey(t.key);let l=0;this.collection.forEach(e=>{const t=e.get?e.get(n):e[n],s=parseFloat(t)||0;l+=s}),e[`col_${s}`]={value:l,formatter:i||t.formatter,fieldKey:n,originalKey:t.key}}),e}extractColumnFilters(){this.filters={},this.columns.forEach(e=>{if(e.filter){const{fieldKey:t}=this.parseColumnKey(e.key);this.filters[t]={...e.filter,label:e.filter.label||e.label||t}}})}isSelectable(){return this.batchActions&&this.batchActions.length>0&&"multiple"===this.selectionMode}buildTableTemplate(){const e="top"===this.batchBarLocation?this.buildBatchActionsPanel():"",t="bottom"===this.batchBarLocation?this.buildBatchActionsPanel():"",s=(()=>{const e=this.tableOptions&&null!=this.tableOptions.fontSize?this.tableOptions.fontSize:this.options&&this.options.fontSize,t="sm"===e?"0.9rem":"xs"===e?"0.8rem":e?String(e):null;return t?` style="font-size: ${t};"`:""})();return`\n <div class="mojo-table-wrapper">\n ${this.buildToolbarTemplate()}\n ${e}\n <div class="table-container"${s}>\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 ${t}\n ${this.paginated?this.buildPaginationTemplate():""}\n </div>\n `}buildTableClasses(){let e=["table"];return this.tableOptions.striped&&e.push("table-striped"),this.tableOptions.bordered&&e.push("table-bordered"),this.tableOptions.hover&&e.push("table-hover"),this.tableOptions.responsive&&e.push("table-responsive"),this.tableOptions.background&&e.push(`table-${this.tableOptions.background}`),"sm"===this.tableOptions.size&&e.push("table-sm"),"lg"===this.tableOptions.size&&e.push("table-lg"),e.join(" ")}buildActionButtonsTemplate(){let e=super.buildActionButtonsTemplate();if(this.showFullscreen&&this.isFullscreenSupported()){const t='\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 ',s=e.indexOf('data-action="refresh"');if(-1!==s){const n=e.indexOf("</button>",s)+9;e=e.slice(0,n)+t+e.slice(n)}else e=t+e}return e}buildTableHeaderTemplate(){let e="";return this.isSelectable()&&(e+='\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(t=>{const{fieldKey:s}=this.parseColumnKey(t.key),n=this.sortable&&!1!==t.sortable,i=this.getSortBy()===s?this.getSortDirection():null,l=this.getSortIcon(i),a=t.label||t.title||s,o=this.getResponsiveClasses(t.visibility),c=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="${s}">\n ${l}\n </button>\n <ul class="dropdown-menu dropdown-menu-end">\n <li><a class="dropdown-item ${"asc"===i?"active":""}"\n data-action="sort" data-field="${s}" 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"===i?"active":""}"\n data-action="sort" data-field="${s}" 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===i?"active":""}"\n data-action="sort" data-field="${s}" data-direction="none">\n <i class="bi bi-x-circle me-2"></i>No Sort\n </a></li>\n </ul>\n </div>\n `:"",r=this.getAlignClass(t.align);e+=`\n <th class="${n?"sortable":""} ${o} ${r}">\n <div class="d-flex align-items-center ${"text-center"===r?"justify-content-center":"text-end"===r?"justify-content-end":""}">\n <span>${a}</span>\n ${c}\n </div>\n </th>\n `}),this.actions?e+="<th>Actions</th>":this.contextMenu&&(e+='<th style="width: 1px;"></th>'),`\n <thead>\n <tr>\n ${e}\n </tr>\n </thead>\n `}buildTableFooterTemplate(){let e="";this.isSelectable()&&(e+="<td></td>");let t=0;return this.columns.forEach((s,n)=>{const i=this.getResponsiveClasses(s.visibility),l=this.getAlignClass(s.align);if(s.footer_total){const n=`col_${t}`,a=this.parseColumnKey(s.key).formatter||s.formatter;let o;o=a&&"string"==typeof a?`{{{footerTotals.${n}.value|${a}}}}`:`{{footerTotals.${n}.value}}`,e+=`<td class="table-footer-total ${i} ${l}" data-total-column="${n}">${o}</td>`,t++}else e+=0===n?`<td class="table-footer-label ${i} ${l}"><strong>Totals</strong></td>`:`<td class="${i} ${l}"></td>`}),(this.actions||this.contextMenu)&&(e+="<td></td>"),`\n <tfoot>\n <tr class="table-totals-row">\n ${e}\n </tr>\n </tfoot>\n `}buildBatchActionsPanel(){if(!this.batchActions||0===this.batchActions.length)return"";if("top"===this.batchBarLocation){let e="";return this.batchActions.forEach(t=>{e+=`\n <button class="btn btn-sm btn-outline-secondary" data-action="batch-${t.action}" title="${t.label}">\n <i class="${t.icon} me-1"></i>\n <span class="d-none d-lg-inline">${t.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 ${e}\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 e="";return this.batchActions.forEach(t=>{e+=`\n <div class="batch-select-action text-center px-2" data-action="batch-${t.action}">\n <div class="batch-action-icon fs-3">\n <i class="${t.icon}"></i>\n </div>\n <div class="batch-action-title small">${t.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 ${e}\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 `}}_createItemView(e,t){if(this.itemViews.has(e.id))return this.itemViews.get(e.id);const s=new this.itemClass({model:e,index:t,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(e.id,s),this._wireItemViewListeners(s),s.on("item:select",()=>this.updateBatchActionsPanel()),s.on("item:deselect",()=>this.updateBatchActionsPanel()),s.on("cell:edit",this._onCellEdit.bind(this)),s.on("cell:save",this._onCellSave.bind(this)),s.on("cell:cancel",this._onCellCancel.bind(this)),s}async onBeforeRender(){await super.onBeforeRender(),this.footerTotals=this.calculateFooterTotals()}async onAfterRender(){await super.onAfterRender(),this.hasFooterTotals&&this.updateFooterTotals(),this.updateSortIcons()}_onCellEdit(e){this.emit("cell:edit",e)}async _onCellSave(e){this.emit("cell:save",e)}_onCellCancel(e){this.emit("cell:cancel",e)}_defaultGroupHeaderTemplate(){return'<th colspan="{{colspan}}" class="list-group-header-cell">{{key}}</th>'}_groupHeaderViewOptions(e,t,s){const n=this.columns?.length||0,i=this.isSelectable()?1:0,l=this.actions||this.contextMenu?1:0;return{tagName:"tr",className:`list-group-header-row list-group-header-row--${this.groupHeaderStyle}`,colspan:Math.max(1,n+i+l)}}isFullscreenSupported(){return!!(document.fullscreenEnabled||document.mozFullScreenEnabled||document.webkitFullscreenEnabled||document.msFullscreenEnabled)}async onActionToggleFullscreen(e,t){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(e){console.warn("Could not enter fullscreen:",e)}}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(e){console.warn("Could not exit fullscreen:",e)}}updateFullscreenButton(){const e=this.element?.querySelector(".btn-fullscreen"),t=e?.querySelector("i");e&&t&&(this.isFullscreen?(t.className="bi bi-fullscreen-exit",e.title="Exit Fullscreen"):(t.className="bi bi-fullscreen",e.title="Enter Fullscreen"))}setupFullscreenListeners(){if(this._fullscreenHandler)return;const e=()=>{!(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",e),document.addEventListener("mozfullscreenchange",e),document.addEventListener("webkitfullscreenchange",e),document.addEventListener("msfullscreenchange",e),this._fullscreenHandler=e}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 onActionAdd(e,t){return this.emit("table:add",{event:e}),super.onActionAdd(e,t)}async onActionExport(e,t){const s=t.getAttribute("data-format")||"json";return this.emit("table:export",{format:s,source:this.exportSource,event:e}),super.onActionExport(e,t)}getSortBy(){const e=this.collection?.params?.sort;return e?e.startsWith("-")?e.slice(1):e:null}getSortDirection(){const e=this.collection?.params?.sort;return e&&e.startsWith("-")?"desc":"asc"}getSortIcon(e){return"asc"===e?'<i class="bi bi-sort-alpha-down text-primary"></i>':"desc"===e?'<i class="bi bi-sort-alpha-down-alt text-primary"></i>':'<i class="bi bi-three-dots-vertical text-muted"></i>'}async onActionSort(e,t){e.preventDefault();const s=t.getAttribute("data-field"),n=t.getAttribute("data-direction");if(this.collection){let e;if(e="none"===n?void 0:"desc"===n?`-${s}`:s,this.collection.setParams({...this.collection.params,sort:e,start:0}),this.collection.restEnabled)await this.collection.fetch();else{if(e){const t=e.startsWith("-"),s=t?e.slice(1):e;this.collection.sort((e,n)=>{const i=e.get(s),l=n.get(s);return i<l?t?1:-1:i>l?t?-1:1:0})}this.render()}}this.updateSortIcons(),this.emit("table:sort",{field:s,event:e}),this.emit("params-changed")}updateSortIcons(){if(!this.element)return;const e=this.getSortBy(),t=this.getSortDirection();this.columns.forEach(s=>{if(this.sortable&&!1!==s.sortable){const{fieldKey:n}=this.parseColumnKey(s.key),i=this.element.querySelector(`[data-bs-toggle="dropdown"][data-column="${n}"]`);if(i){const s=e===n,l=this.getSortIcon(s?t:null);i.innerHTML=l;const a=i.nextElementSibling;if(a){const i=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"]`);i&&i.classList.toggle("active",s&&"asc"===t),l&&l.classList.toggle("active",s&&"desc"===t),o&&o.classList.toggle("active",!s||e!==n)}}}})}async onActionSelectAll(e,t){e.stopPropagation();const s=this.itemViews.size>0&&Array.from(this.itemViews.values()).every(e=>e.selected);s?this.clearSelection():this.forEachItem(e=>{e.selected||e.select()});const n=this.element?.querySelector(".mojo-select-all-cell");n&&n.classList.toggle("selected",!s),this.updateBatchActionsPanel()}updateBatchActionsPanel(){if(!this.batchActions||0===this.batchActions.length)return;const e=this.getSelectedItems().length;if("top"===this.batchBarLocation){const t=this.element?.querySelector(".batch-actions-panel-top"),s=this.element?.querySelector(".batch-select-count");t&&s&&(s.textContent=e,e>0?t.classList.remove("d-none"):t.classList.add("d-none"))}else{const t=this.element?.querySelector(".batch-actions-panel"),s=this.element?.querySelector(".batch-select-count");t&&s&&(s.textContent=e,t.style.display=e>0?"block":"none")}const t=this.element?.querySelector(".mojo-select-all-cell");if(t){const e=this.itemViews.size>0&&Array.from(this.itemViews.values()).every(e=>e.selected),s=Array.from(this.itemViews.values()).some(e=>e.selected);t.classList.toggle("selected",e),t.classList.toggle("indeterminate",!e&&s);const n=t.querySelector("i");n&&(n.className=!e&&s?"bi bi-dash":"bi bi-check")}}async onActionBatch(e,t){const s=t.getAttribute("data-action").replace("batch-",""),n=this.getSelectedItems();this.emit("batch:action",{action:s,items:n,event:e})}async onActionClearSelection(e,t){this.clearSelection(),this.updateBatchActionsPanel()}getFilterDisplayValue(e,t){if("search"===e)return`"${t}"`;const s=this.filters[e]||this.additionalFilters.find(t=>(t.name||t.key)===e);if(s&&"daterange"===s.type&&"object"==typeof t)return`${t.start||""} to ${t.end||""}`;if(s&&"select"===s.type&&s.options){if("object"==typeof s.options[0]){const e=s.options.find(e=>e.value===t);return e?e.label:t}return t}return t}}function c(e){const t=e.replace(/-/g,"+").replace(/_/g,"/"),s=t+"=".repeat((4-t.length%4)%4);return Uint8Array.from(atob(s),e=>e.charCodeAt(0))}function r(e){return btoa(String.fromCharCode(...new Uint8Array(e))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}class Passkey extends t{constructor(e={},t={}){super(e,{endpoint:"/api/account/passkeys",...t})}static suggestName(){const e=navigator.userAgent;let t="Device";/iPad/.test(e)?t="iPad":/iPhone/.test(e)?t="iPhone":/Macintosh|MacIntel/.test(e)?t="Mac":/Android/.test(e)?t="Android":/Windows/.test(e)?t="Windows PC":/Linux/.test(e)&&(t="Linux");let s="";return/Edg\//.test(e)?s="Edge":/Chrome\//.test(e)&&!/Chromium/.test(e)?s="Chrome":/Safari\//.test(e)&&!/Chrome/.test(e)?s="Safari":/Firefox\//.test(e)&&(s="Firefox"),s?`${t} — ${s}`:t}static async register(e){const t=await Passkey.registerBegin();if(!t?.data?.challenge_id||!t?.data?.publicKey)return{success:!1,error:t?.error||"Could not start registration."};const{challenge_id:s,publicKey:n}=t.data;"string"==typeof n.challenge&&(n.challenge=c(n.challenge)),"string"==typeof n.user?.id&&(n.user.id=c(n.user.id)),n.excludeCredentials&&(n.excludeCredentials=n.excludeCredentials.map(e=>({...e,id:"string"==typeof e.id?c(e.id):e.id})));const i=await navigator.credentials.create({publicKey:n});if(!i)return{success:!1,error:"Passkey creation was cancelled."};const l={id:i.id,rawId:r(i.rawId),type:i.type,response:{clientDataJSON:r(i.response.clientDataJSON),attestationObject:r(i.response.attestationObject)}};i.response.getTransports&&(l.transports=i.response.getTransports());const a=await Passkey.registerComplete({challenge_id:s,credential:l,friendly_name:e||"My Passkey"});return a?.data?.id?{success:!0,passkey:a.data}:{success:!1,error:a?.error||"Registration could not be completed."}}static async registerBegin(e={}){try{return await n.POST("/api/account/passkeys/register/begin",{},e.params,{dataOnly:!0})}catch(t){return{success:!1,error:t?.message||"Failed to begin passkey registration"}}}static async registerComplete(e={},t={}){if(!e.challenge_id||!e.credential)return{success:!1,error:"Missing challenge_id or credential data"};try{return await n.POST("/api/account/passkeys/register/complete",e,t.params,{dataOnly:!0})}catch(s){return{success:!1,error:s?.message||"Failed to complete passkey registration"}}}}class PasskeyList extends e{constructor(e={}){super({ModelClass:Passkey,endpoint:"/api/account/passkeys",size:10,...e})}}const d={edit:{fields:[{name:"friendly_name",type:"text",label:"Name",placeholder:"My iPhone",required:!0,columns:12,help:"A friendly name to identify this passkey"},{name:"is_enabled",type:"switch",label:"Enabled",columns:12,help:"Disable to prevent this passkey from being used for authentication"}]}};export{Log as L,MemberList as M,Passkey as P,TableView as T,PasskeyList as a,d as b,TableRow as c,Member as d,LogList as e,a as f};
2
- //# sourceMappingURL=Passkeys-B4bndv5b.js.map
1
+ import{C as e,M as t,d as s,r as n}from"./User-DgB81g6W.js";import{L as i,a as l}from"./ListView-McEedPG8.js";class Log extends t{constructor(e={}){super(e,{endpoint:"/api/logs"})}}class LogList extends e{constructor(e={}){super({ModelClass:Log,endpoint:"/api/logs",size:10,...e})}}class Member extends t{constructor(e={}){super(e,{endpoint:"/api/group/member"})}hasPermission(e){if(Array.isArray(e))return e.some(e=>this.hasPermission(e));const t=this.get("permissions");return!!t&&1==t[e]}async fetchForGroup(e){return await this.fetch({url:`/api/group/${e}/member`})}}class MemberList extends e{constructor(e={}){super({ModelClass:Member,endpoint:"/api/group/member",size:10,...e})}}const a={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.BASE_PERMISSIONS=[{name:"manage_group",label:"Group Admin"},{name:"view_metrics",label:"View Metrics"},{name:"view_logs",label:"View Logs"},{name:"view_tickets",label:"View Tickets"},{name:"view_members",label:"View Members"},{name:"manage_members",label:"Manage Members"},{name:"view_billing",label:"View Billing"}],Member.APP_PERMISSIONS=[],Member.PERMISSIONS=[],Member.PERMISSION_FIELDS=[];const o=e=>({name:`permissions.${e.name}`,type:"switch",label:e.label,columns:6,...e.tooltip?{tooltip:e.tooltip}:{}});Member.rebuildPermissions=function(){Member.PERMISSIONS.length=0,Member.PERMISSIONS.push(...Member.BASE_PERMISSIONS,...Member.APP_PERMISSIONS),Member.PERMISSION_FIELDS.length=0,Member.PERMISSION_FIELDS.push(...Member.PERMISSIONS.map(o))},Member.rebuildPermissions(),Member.EDIT_FORM=a.edit,Member.ADD_FORM=a.create;class TableRow extends i{constructor(e={}){super({tagName:"tr",className:"table-row",enableTooltips:!0,...e}),this.columns=e.columns||[],this.actions=e.actions||null,this.contextMenu=e.contextMenu||null,this.batchActions=e.batchActions||null,this.tableView=e.tableView||e.listView||null,this.editingCells=/* @__PURE__ */new Set,this.template=this.buildRowTemplate()}getResponsiveClasses(e){if(!e)return"";const t=["sm","md","lg","xl","xxl"];if("string"==typeof e)return t.includes(e)?`d-none d-${e}-table-cell`:(console.warn(`Invalid visibility breakpoint: ${e}. Valid options are: ${t.join(", ")}`),"");if("object"==typeof e){const s=[];if(e.hide){if(!t.includes(e.hide))return console.warn(`Invalid hide breakpoint: ${e.hide}. Valid options are: ${t.join(", ")}`),"";s.push(`d-table-cell d-${e.hide}-none`)}if(e.show){if(!t.includes(e.show))return console.warn(`Invalid show breakpoint: ${e.show}. Valid options are: ${t.join(", ")}`),"";e.hide?s.push(`d-${e.show}-table-cell`):s.push(`d-none d-${e.show}-table-cell`)}return s.join(" ")}return""}buildRowTemplate(){let e="";return this.tableView&&this.tableView.isSelectable()&&(e+='\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((t,s)=>{const n=[t.class||t.className||"",this.getResponsiveClasses(t.visibility),t.editable?"editable-cell":"",this.tableView&&this.tableView.getAlignClass?this.tableView.getAlignClass(t.align):""].filter(e=>e).join(" "),i=this.buildCellTemplate(t,s);let l=t.action;!l&&t.editable?l="edit-cell":!l&&this.tableView.rowAction&&(l=this.tableView.rowAction),e+=l?`<td class="${n}" data-action="${l}" data-column="${t.key}">${i}</td>`:`<td class="${n}" data-column="${t.key}">${i}</td>`}),this.actions?e+=this.buildActionsTemplate():this.contextMenu&&(e+=this.buildContextMenuTemplate()),e}buildCellTemplate(e,t=0){const s=`model.${e.key}`,n=e.formatter||e.format,i=e.editable?` class="cell-content" data-field="${e.key}"`:"";if(n){if("string"==typeof n)return e.editable?`<span${i}>{{{${s}|${n}}}}</span>`:`{{{${s}|${n}}}}`;if("function"==typeof n){const n=e.editable?"cell-content":"",i=e.editable?` data-field="${e.key}"`:"";return`<span class="${n}" data-formatter="${e.key}" data-formatter-id="${t}"${i}>{{${s}}}</span>`}}return e.template?e.editable?`<span${i}>${e.template}</span>`:e.template:e.editable?`<span${i}>{{{${s}}}}</span>`:`{{{${s}}}}`}buildActionsTemplate(){return this.actions&&0!==this.actions.length?`<td><div class="btn-group btn-group-sm">${this.actions.map(e=>{if("string"==typeof e)switch(e){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 e)return`\n <button class="btn btn-sm ${e.class||"btn-outline-primary"}"\n data-id="${this.model.id}"\n data-action="${e.action}"\n title="${e.label||""}">\n ${e.icon?`<i class="${e.icon}"></i>`:""}\n ${e.label&&!e.icon?e.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(e=>{if(e.separator||e.divider)return'<li><hr class="dropdown-divider"></li>';let t="dropdown-item";return("delete"===e.action||e.danger)&&(t+=" text-danger"),e.disabled&&(t+=" disabled"),`\n <li>\n <a class="${t}" href="#"\n data-id="{{model.id}}"\n data-action="${e.action}"\n ${e.disabled?'aria-disabled="true" tabindex="-1"':""}>\n ${e.icon?`<i class="${e.icon} me-2"></i>`:""}\n ${e.label}\n </a>\n </li>\n `}).join("")}async onAfterRender(){await super.onAfterRender(),this.columns.forEach((e,t)=>{if(e.formatter&&"function"==typeof e.formatter){let n=this.element.querySelector(`[data-formatter-id="${t}"]`);if(n||(n=this.element.querySelector(`[data-formatter="${e.key}"]`)),n){const t=this.model.get?this.model.get(e.key):this.model[e.key],i={value:t,row:this.model,model:this.model,column:e,table:this.tableView,index:this.index};try{n.innerHTML=e.formatter(t,i)}catch(s){console.error(`Error formatting cell for column ${e.key}:`,s)}}}}),this.selected&&this.element.classList.add("selected");const e=this.model.get?this.model.get("id"):this.model.id;e&&this.element.setAttribute("data-id",e)}async onActionEditCell(e,t){e.stopPropagation();const s=t.getAttribute("data-column"),n=this.columns.find(e=>e.key===s);n&&n.editable&&(this.editingCells.has(s)||await this.enterEditMode(s,n,t))}async onActionRowClick(e,t){e.target.closest(".btn-group")||e.target.closest(".dropdown")||e.target.closest(".cell-editor")||(this.emit("row:click",{row:this,model:this.model,column:t.getAttribute("data-column"),event:e}),this.tableView&&this.tableView.emit("row:click",{row:this,model:this.model,column:t.getAttribute("data-column"),event:e}))}async onActionView(e,t){e.stopPropagation(),this.emit("row:view",{row:this,model:this.model,event:e}),this.tableView&&this.tableView.emit("row:view",{row:this,model:this.model,event:e})}async onActionEdit(e,t){return e.stopPropagation(),this.emit("row:edit",{row:this,model:this.model,event:e}),this.tableView&&this.tableView.emit("row:edit",{row:this,model:this.model,event:e}),!0}async onActionDelete(e,t){e.stopPropagation(),this.emit("row:delete",{row:this,model:this.model,event:e}),this.tableView&&this.tableView.emit("row:delete",{row:this,model:this.model,event:e})}async enterEditMode(e,t,s){const n=s.querySelector(".cell-content");if(!n)return;this.editingCells.add(e);const i=this.model.get?this.model.get(e):this.model[e],l=this.createCellEditor(t,i),a=n.innerHTML;n.style.display="none";const o=document.createElement("div");o.className="cell-editor",o.innerHTML=l,s.appendChild(o);const c=o.querySelector("input, select, .form-check-input");c&&(c.focus(),"text"!==c.type&&"textarea"!==c.type||c.select()),o.dataset.originalContent=a,o.dataset.columnKey=e,this.setupEditorEvents(o,e,t),this.emit("cell:edit",{row:this,model:this.model,column:e,originalValue:i})}createCellEditor(e,t){const s=e.editableOptions||{};switch(s.type){case"select":return this.createSelectEditor(s,t);case"switch":case"checkbox":return this.createSwitchEditor(s,t);case"textarea":return this.createTextareaEditor(s,t);default:return this.createTextEditor(s,t)}}createTextEditor(e,t){const s=e.placeholder||"";return`\n <div class="d-flex gap-1 align-items-center">\n <input type="${e.inputType||"text"}"\n class="form-control form-control-sm cell-input"\n value="${this.escapeHtml(t||"")}"\n placeholder="${s}">\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(e,t){const s=e.placeholder||"";return`\n <div class="d-flex gap-1">\n <textarea class="form-control form-control-sm cell-input"\n rows="${e.rows||2}"\n placeholder="${s}">${this.escapeHtml(t||"")}</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(e,t){const s=e.options||[];let n="";return s.forEach(e=>{if("string"==typeof e)n+=`<option value="${e}" ${e===t?"selected":""}>${e}</option>`;else if("object"==typeof e&&void 0!==e.value){const s=e.value===t?"selected":"";n+=`<option value="${e.value}" ${s}>${e.label||e.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(e,t){const s=t?"checked":"";return`\n <div class="d-flex gap-2 align-items-center">\n <div class="form-check ${"switch"===e.type?"form-switch":""}">\n <input class="form-check-input cell-input" type="checkbox" ${s}>\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(e,t,s){const n=e.querySelector(".cell-input"),i=e.querySelector(".cell-save"),l=e.querySelector(".cell-cancel");!n||"text"!==n.type&&"email"!==n.type&&"number"!==n.type||n.addEventListener("keydown",n=>{"Enter"===n.key?(n.preventDefault(),this.saveCellEdit(e,t,s)):"Escape"===n.key&&(n.preventDefault(),this.cancelCellEdit(e,t))}),!n||"checkbox"!==n.type&&"SELECT"!==n.tagName||!1===s.autoSave||n.addEventListener("change",()=>{this.saveCellEdit(e,t,s)}),i?.addEventListener("click",()=>{this.saveCellEdit(e,t,s)}),l?.addEventListener("click",()=>{this.cancelCellEdit(e,t)})}async saveCellEdit(e,t,s){const n=e.querySelector(".cell-input");if(!n)return;let i;i="checkbox"===n.type?n.checked:(n.tagName,n.value);const l=this.model.get?this.model.get(t):this.model[t];try{this.model.save?await this.model.save({[t]:i}):this.model[t]=i,this.exitEditMode(e,t,i),this.emit("cell:save",{row:this,model:this.model,column:t,oldValue:l,newValue:i})}catch(a){console.error("Failed to save cell edit:",a),this.emit("cell:save:error",{row:this,model:this.model,column:t,oldValue:l,newValue:i,error:a}),e.classList.add("saving-error"),setTimeout(()=>e.classList.remove("saving-error"),3e3)}}cancelCellEdit(e,t){const s=e.dataset.originalContent;this.exitEditMode(e,t,null,s),this.emit("cell:cancel",{row:this,model:this.model,column:t})}exitEditMode(e,t,n=null,i=null){const l=e.closest("td").querySelector(".cell-content");if(l){if(null!==n){const e=this.columns.find(e=>e.key===t);let i=n;e&&e.formatter&&"string"==typeof e.formatter&&(i=s.pipe(n,e.formatter)),l.innerHTML=this.escapeHtml(i)}else i&&(l.innerHTML=i);l.style.display=""}e.remove(),this.editingCells.delete(t)}escapeHtml(e){if(null==e)return"";const t=document.createElement("div");return t.textContent=e,t.innerHTML}select(){super.select(),this.addClass("selected");const e=this.element?.querySelector(".mojo-select-cell");e&&e.classList.add("selected")}deselect(){super.deselect(),this.removeClass("selected");const e=this.element?.querySelector(".mojo-select-cell");e&&e.classList.remove("selected")}}class TableView extends l{constructor(e={}){super({className:"table-view-component",itemClass:e.itemClass||TableRow,selectionMode:e.selectable?"multiple":"none",emptyMessage:e.emptyMessage||"No data available",addButtonIcon:e.addButtonIcon||"bi bi-plus-circle",...e}),this.isFullscreen=!1,this.columns=e.columns||[],this.actions=e.actions||null,this.contextMenu=e.contextMenu||null,this.batchActions=e.batchActions||null,this.searchable=!1!==e.searchable,this.sortable=!1!==e.sortable,this.filterable=!1!==e.filterable,this.paginated=!1!==e.paginated,this.paginationMode=e.paginationMode||"pages",this.persistSelection=!0===e.persistSelection,this.clickAction=e.clickAction||"view",this.fetchOnView=!1!==e.fetchOnView,this.itemView=e.itemView,this.addForm=e.addForm,this.editForm=e.editForm,this.deleteTemplate=e.deleteTemplate,this.formDialogConfig=e.formDialogConfig||{},this.viewDialogOptions=e.viewDialogOptions||{},this.exportOptions=e.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=e.exportSource||"remote",this.filters={},this.additionalFilters=e.filters||[],this.hideActivePills=!0===e.hideActivePills,this.hideActivePillNames=e.hideActivePillNames||[],this.rowAction=e.rowAction||"row-click",this.batchBarLocation=e.batchBarLocation||"bottom",this.options.addButtonLabel=e.addButtonLabel||"Add",this.toolbarButtons=e.toolbarButtons||[],this.toolbarRight=e.toolbarRight||null,this.title=e.title||null,this.eyebrow=e.eyebrow||null,this.showRefresh=!1!==e.showRefresh,this.showFullscreen=!1!==e.showFullscreen,this.tableOptions={striped:!0,bordered:!1,hover:!0,responsive:!1,size:null,...e.tableOptions},this.searchPlacement=e.searchPlacement||"toolbar",this.searchPlaceholder=e.searchPlaceholder||"Search...",this.initializeColumns(),this.extractColumnFilters(),this.footerTotalColumns=this.columns.filter(e=>!0===e.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(e=>{!e.key&&e.name&&(e.key=e.name),e.label||e.title||(e.label=e.key.charAt(0).toUpperCase()+e.key.slice(1))})}getResponsiveClasses(e){if(!e)return"";const t=["sm","md","lg","xl","xxl"];if("string"==typeof e)return t.includes(e)?`d-none d-${e}-table-cell`:(console.warn(`Invalid visibility breakpoint: ${e}. Valid options are: ${t.join(", ")}`),"");if("object"==typeof e){const s=[];if(e.hide){if(!t.includes(e.hide))return console.warn(`Invalid hide breakpoint: ${e.hide}. Valid options are: ${t.join(", ")}`),"";s.push(`d-table-cell d-${e.hide}-none`)}if(e.show){if(!t.includes(e.show))return console.warn(`Invalid show breakpoint: ${e.show}. Valid options are: ${t.join(", ")}`),"";e.hide?s.push(`d-${e.show}-table-cell`):s.push(`d-none d-${e.show}-table-cell`)}return s.join(" ")}return""}getAlignClass(e){if(!e)return"";return{left:"text-start",start:"text-start",center:"text-center",right:"text-end",end:"text-end"}[String(e).toLowerCase()]||(console.warn(`Invalid column align: ${e}. Valid options are: left, center, right`),"")}parseColumnKey(e){const t=e.split("|");return{fieldKey:t[0],formatter:t[1]||null}}updateFooterTotals(){if(!this.hasFooterTotals||!this.element)return;const e=this.calculateFooterTotals();let t=0;this.columns.forEach(s=>{if(s.footer_total){const n=`col_${t}`,i=this.element.querySelector(`[data-total-column="${n}"]`);if(i&&e[n]){const t=this.parseColumnKey(s.key).formatter||s.formatter;let l;l=t&&"string"==typeof t?this.formatValue(e[n].value,t):e[n].value,i.textContent=l}t++}})}formatValue(e,t){try{return s.pipe(e,t)}catch(n){return console.warn("Error formatting value:",n),e}}calculateFooterTotals(){if(!this.hasFooterTotals||!this.collection||0===this.collection.length)return{};const e={};return this.footerTotalColumns.forEach((t,s)=>{const{fieldKey:n,formatter:i}=this.parseColumnKey(t.key);let l=0;this.collection.forEach(e=>{const t=e.get?e.get(n):e[n],s=parseFloat(t)||0;l+=s}),e[`col_${s}`]={value:l,formatter:i||t.formatter,fieldKey:n,originalKey:t.key}}),e}extractColumnFilters(){this.filters={},this.columns.forEach(e=>{if(e.filter){const{fieldKey:t}=this.parseColumnKey(e.key);this.filters[t]={...e.filter,label:e.filter.label||e.label||t}}})}isSelectable(){return this.batchActions&&this.batchActions.length>0&&"multiple"===this.selectionMode}buildTableTemplate(){const e="top"===this.batchBarLocation?this.buildBatchActionsPanel():"",t="bottom"===this.batchBarLocation?this.buildBatchActionsPanel():"",s=(()=>{const e=this.tableOptions&&null!=this.tableOptions.fontSize?this.tableOptions.fontSize:this.options&&this.options.fontSize,t="sm"===e?"0.9rem":"xs"===e?"0.8rem":e?String(e):null;return t?` style="font-size: ${t};"`:""})();return`\n <div class="mojo-table-wrapper">\n ${this.buildToolbarTemplate()}\n ${e}\n <div class="table-container"${s}>\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 ${t}\n ${this.paginated?this.buildPaginationTemplate():""}\n </div>\n `}buildTableClasses(){let e=["table"];return this.tableOptions.striped&&e.push("table-striped"),this.tableOptions.bordered&&e.push("table-bordered"),this.tableOptions.hover&&e.push("table-hover"),this.tableOptions.responsive&&e.push("table-responsive"),this.tableOptions.background&&e.push(`table-${this.tableOptions.background}`),"sm"===this.tableOptions.size&&e.push("table-sm"),"lg"===this.tableOptions.size&&e.push("table-lg"),e.join(" ")}buildActionButtonsTemplate(){let e=super.buildActionButtonsTemplate();if(this.showFullscreen&&this.isFullscreenSupported()){const t='\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 ',s=e.indexOf('data-action="refresh"');if(-1!==s){const n=e.indexOf("</button>",s)+9;e=e.slice(0,n)+t+e.slice(n)}else e=t+e}return e}buildTableHeaderTemplate(){let e="";return this.isSelectable()&&(e+='\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(t=>{const{fieldKey:s}=this.parseColumnKey(t.key),n=this.sortable&&!1!==t.sortable,i=this.getSortBy()===s?this.getSortDirection():null,l=this.getSortIcon(i),a=t.label||t.title||s,o=this.getResponsiveClasses(t.visibility),c=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="${s}">\n ${l}\n </button>\n <ul class="dropdown-menu dropdown-menu-end">\n <li><a class="dropdown-item ${"asc"===i?"active":""}"\n data-action="sort" data-field="${s}" 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"===i?"active":""}"\n data-action="sort" data-field="${s}" 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===i?"active":""}"\n data-action="sort" data-field="${s}" data-direction="none">\n <i class="bi bi-x-circle me-2"></i>No Sort\n </a></li>\n </ul>\n </div>\n `:"",r=this.getAlignClass(t.align);e+=`\n <th class="${n?"sortable":""} ${o} ${r}">\n <div class="d-flex align-items-center ${"text-center"===r?"justify-content-center":"text-end"===r?"justify-content-end":""}">\n <span>${a}</span>\n ${c}\n </div>\n </th>\n `}),this.actions?e+="<th>Actions</th>":this.contextMenu&&(e+='<th style="width: 1px;"></th>'),`\n <thead>\n <tr>\n ${e}\n </tr>\n </thead>\n `}buildTableFooterTemplate(){let e="";this.isSelectable()&&(e+="<td></td>");let t=0;return this.columns.forEach((s,n)=>{const i=this.getResponsiveClasses(s.visibility),l=this.getAlignClass(s.align);if(s.footer_total){const n=`col_${t}`,a=this.parseColumnKey(s.key).formatter||s.formatter;let o;o=a&&"string"==typeof a?`{{{footerTotals.${n}.value|${a}}}}`:`{{footerTotals.${n}.value}}`,e+=`<td class="table-footer-total ${i} ${l}" data-total-column="${n}">${o}</td>`,t++}else e+=0===n?`<td class="table-footer-label ${i} ${l}"><strong>Totals</strong></td>`:`<td class="${i} ${l}"></td>`}),(this.actions||this.contextMenu)&&(e+="<td></td>"),`\n <tfoot>\n <tr class="table-totals-row">\n ${e}\n </tr>\n </tfoot>\n `}buildBatchActionsPanel(){if(!this.batchActions||0===this.batchActions.length)return"";if("top"===this.batchBarLocation){let e="";return this.batchActions.forEach(t=>{e+=`\n <button class="btn btn-sm btn-outline-secondary" data-action="batch-${t.action}" title="${t.label}">\n <i class="${t.icon} me-1"></i>\n <span class="d-none d-lg-inline">${t.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 ${e}\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 e="";return this.batchActions.forEach(t=>{e+=`\n <div class="batch-select-action text-center px-2" data-action="batch-${t.action}">\n <div class="batch-action-icon fs-3">\n <i class="${t.icon}"></i>\n </div>\n <div class="batch-action-title small">${t.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 ${e}\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 `}}_createItemView(e,t){if(this.itemViews.has(e.id))return this.itemViews.get(e.id);const s=new this.itemClass({model:e,index:t,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(e.id,s),this._wireItemViewListeners(s),s.on("item:select",()=>this.updateBatchActionsPanel()),s.on("item:deselect",()=>this.updateBatchActionsPanel()),s.on("cell:edit",this._onCellEdit.bind(this)),s.on("cell:save",this._onCellSave.bind(this)),s.on("cell:cancel",this._onCellCancel.bind(this)),s}async onBeforeRender(){await super.onBeforeRender(),this.footerTotals=this.calculateFooterTotals()}async onAfterRender(){await super.onAfterRender(),this.hasFooterTotals&&this.updateFooterTotals(),this.updateSortIcons()}_onCellEdit(e){this.emit("cell:edit",e)}async _onCellSave(e){this.emit("cell:save",e)}_onCellCancel(e){this.emit("cell:cancel",e)}_defaultGroupHeaderTemplate(){return'<th colspan="{{colspan}}" class="list-group-header-cell">{{key}}</th>'}_groupHeaderViewOptions(e,t,s){const n=this.columns?.length||0,i=this.isSelectable()?1:0,l=this.actions||this.contextMenu?1:0;return{tagName:"tr",className:`list-group-header-row list-group-header-row--${this.groupHeaderStyle}`,colspan:Math.max(1,n+i+l)}}isFullscreenSupported(){return!!(document.fullscreenEnabled||document.mozFullScreenEnabled||document.webkitFullscreenEnabled||document.msFullscreenEnabled)}async onActionToggleFullscreen(e,t){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(e){console.warn("Could not enter fullscreen:",e)}}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(e){console.warn("Could not exit fullscreen:",e)}}updateFullscreenButton(){const e=this.element?.querySelector(".btn-fullscreen"),t=e?.querySelector("i");e&&t&&(this.isFullscreen?(t.className="bi bi-fullscreen-exit",e.title="Exit Fullscreen"):(t.className="bi bi-fullscreen",e.title="Enter Fullscreen"))}setupFullscreenListeners(){if(this._fullscreenHandler)return;const e=()=>{!(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",e),document.addEventListener("mozfullscreenchange",e),document.addEventListener("webkitfullscreenchange",e),document.addEventListener("msfullscreenchange",e),this._fullscreenHandler=e}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 onActionAdd(e,t){return this.emit("table:add",{event:e}),super.onActionAdd(e,t)}async onActionExport(e,t){const s=t.getAttribute("data-format")||"json";return this.emit("table:export",{format:s,source:this.exportSource,event:e}),super.onActionExport(e,t)}getSortBy(){const e=this.collection?.params?.sort;return e?e.startsWith("-")?e.slice(1):e:null}getSortDirection(){const e=this.collection?.params?.sort;return e&&e.startsWith("-")?"desc":"asc"}getSortIcon(e){return"asc"===e?'<i class="bi bi-sort-alpha-down text-primary"></i>':"desc"===e?'<i class="bi bi-sort-alpha-down-alt text-primary"></i>':'<i class="bi bi-three-dots-vertical text-muted"></i>'}async onActionSort(e,t){e.preventDefault();const s=t.getAttribute("data-field"),n=t.getAttribute("data-direction");if(this.collection){let e;if(e="none"===n?void 0:"desc"===n?`-${s}`:s,this.collection.setParams({...this.collection.params,sort:e,start:0}),this.collection.restEnabled)await this.collection.fetch();else{if(e){const t=e.startsWith("-"),s=t?e.slice(1):e;this.collection.sort((e,n)=>{const i=e.get(s),l=n.get(s);return i<l?t?1:-1:i>l?t?-1:1:0})}this.render()}}this.updateSortIcons(),this.emit("table:sort",{field:s,event:e}),this.emit("params-changed")}updateSortIcons(){if(!this.element)return;const e=this.getSortBy(),t=this.getSortDirection();this.columns.forEach(s=>{if(this.sortable&&!1!==s.sortable){const{fieldKey:n}=this.parseColumnKey(s.key),i=this.element.querySelector(`[data-bs-toggle="dropdown"][data-column="${n}"]`);if(i){const s=e===n,l=this.getSortIcon(s?t:null);i.innerHTML=l;const a=i.nextElementSibling;if(a){const i=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"]`);i&&i.classList.toggle("active",s&&"asc"===t),l&&l.classList.toggle("active",s&&"desc"===t),o&&o.classList.toggle("active",!s||e!==n)}}}})}async onActionSelectAll(e,t){e.stopPropagation();const s=this.itemViews.size>0&&Array.from(this.itemViews.values()).every(e=>e.selected);s?this.clearSelection():this.forEachItem(e=>{e.selected||e.select()});const n=this.element?.querySelector(".mojo-select-all-cell");n&&n.classList.toggle("selected",!s),this.updateBatchActionsPanel()}updateBatchActionsPanel(){if(!this.batchActions||0===this.batchActions.length)return;const e=this.getSelectedItems().length;if("top"===this.batchBarLocation){const t=this.element?.querySelector(".batch-actions-panel-top"),s=this.element?.querySelector(".batch-select-count");t&&s&&(s.textContent=e,e>0?t.classList.remove("d-none"):t.classList.add("d-none"))}else{const t=this.element?.querySelector(".batch-actions-panel"),s=this.element?.querySelector(".batch-select-count");t&&s&&(s.textContent=e,t.style.display=e>0?"block":"none")}const t=this.element?.querySelector(".mojo-select-all-cell");if(t){const e=this.itemViews.size>0&&Array.from(this.itemViews.values()).every(e=>e.selected),s=Array.from(this.itemViews.values()).some(e=>e.selected);t.classList.toggle("selected",e),t.classList.toggle("indeterminate",!e&&s);const n=t.querySelector("i");n&&(n.className=!e&&s?"bi bi-dash":"bi bi-check")}}async onActionBatch(e,t){const s=t.getAttribute("data-action").replace("batch-",""),n=this.getSelectedItems();this.emit("batch:action",{action:s,items:n,event:e})}async onActionClearSelection(e,t){this.clearSelection(),this.updateBatchActionsPanel()}getFilterDisplayValue(e,t){if("search"===e)return`"${t}"`;const s=this.filters[e]||this.additionalFilters.find(t=>(t.name||t.key)===e);if(s&&"daterange"===s.type&&"object"==typeof t)return`${t.start||""} to ${t.end||""}`;if(s&&"select"===s.type&&s.options){if("object"==typeof s.options[0]){const e=s.options.find(e=>e.value===t);return e?e.label:t}return t}return t}}function c(e){const t=e.replace(/-/g,"+").replace(/_/g,"/"),s=t+"=".repeat((4-t.length%4)%4);return Uint8Array.from(atob(s),e=>e.charCodeAt(0))}function r(e){return btoa(String.fromCharCode(...new Uint8Array(e))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}class Passkey extends t{constructor(e={},t={}){super(e,{endpoint:"/api/account/passkeys",...t})}static suggestName(){const e=navigator.userAgent;let t="Device";/iPad/.test(e)?t="iPad":/iPhone/.test(e)?t="iPhone":/Macintosh|MacIntel/.test(e)?t="Mac":/Android/.test(e)?t="Android":/Windows/.test(e)?t="Windows PC":/Linux/.test(e)&&(t="Linux");let s="";return/Edg\//.test(e)?s="Edge":/Chrome\//.test(e)&&!/Chromium/.test(e)?s="Chrome":/Safari\//.test(e)&&!/Chrome/.test(e)?s="Safari":/Firefox\//.test(e)&&(s="Firefox"),s?`${t} — ${s}`:t}static async register(e){const t=await Passkey.registerBegin();if(!t?.data?.challenge_id||!t?.data?.publicKey)return{success:!1,error:t?.error||"Could not start registration."};const{challenge_id:s,publicKey:n}=t.data;"string"==typeof n.challenge&&(n.challenge=c(n.challenge)),"string"==typeof n.user?.id&&(n.user.id=c(n.user.id)),n.excludeCredentials&&(n.excludeCredentials=n.excludeCredentials.map(e=>({...e,id:"string"==typeof e.id?c(e.id):e.id})));const i=await navigator.credentials.create({publicKey:n});if(!i)return{success:!1,error:"Passkey creation was cancelled."};const l={id:i.id,rawId:r(i.rawId),type:i.type,response:{clientDataJSON:r(i.response.clientDataJSON),attestationObject:r(i.response.attestationObject)}};i.response.getTransports&&(l.transports=i.response.getTransports());const a=await Passkey.registerComplete({challenge_id:s,credential:l,friendly_name:e||"My Passkey"});return a?.data?.id?{success:!0,passkey:a.data}:{success:!1,error:a?.error||"Registration could not be completed."}}static async registerBegin(e={}){try{return await n.POST("/api/account/passkeys/register/begin",{},e.params,{dataOnly:!0})}catch(t){return{success:!1,error:t?.message||"Failed to begin passkey registration"}}}static async registerComplete(e={},t={}){if(!e.challenge_id||!e.credential)return{success:!1,error:"Missing challenge_id or credential data"};try{return await n.POST("/api/account/passkeys/register/complete",e,t.params,{dataOnly:!0})}catch(s){return{success:!1,error:s?.message||"Failed to complete passkey registration"}}}}class PasskeyList extends e{constructor(e={}){super({ModelClass:Passkey,endpoint:"/api/account/passkeys",size:10,...e})}}const d={edit:{fields:[{name:"friendly_name",type:"text",label:"Name",placeholder:"My iPhone",required:!0,columns:12,help:"A friendly name to identify this passkey"},{name:"is_enabled",type:"switch",label:"Enabled",columns:12,help:"Disable to prevent this passkey from being used for authentication"}]}};export{Log as L,MemberList as M,Passkey as P,TableView as T,PasskeyList as a,d as b,TableRow as c,Member as d,LogList as e,a as f};
2
+ //# sourceMappingURL=Passkeys-C7YPKCKQ.js.map