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{V as t,C as e,a as i}from"./User-DNifTiFu.js";import{a as s}from"./Modal-Dgtnpj85.js";import{F as n}from"./FormView-DaZFbDWr.js";class SegmentControl extends t{constructor(t={}){const{options:e=[],value:i,size:s="sm",ariaLabel:n="Segment control",...l}=t;super({tagName:"div",className:"segment-control",...l}),this.items=e,this.value=void 0!==i?i:e[0]&&e[0].value,this.size="md"===s?"":"sm",this.ariaLabel=n,this.template=()=>this._buildTemplate()}_buildTemplate(){const t=this.size?`btn-group-${this.size}`:"",e=this.items.map(t=>{const e=t.value===this.value,i=e?"btn btn-primary":"btn btn-outline-secondary",s=t.icon?`<i class="bi ${this.escapeHtml(t.icon)} me-1"></i>`:"";return`<button type="button"\n class="${i}"\n data-action="select"\n data-value="${this.escapeHtml(String(t.value))}"\n ${e?'aria-pressed="true"':'aria-pressed="false"'}>${s}${this.escapeHtml(t.label)}</button>`}).join("");return`<div class="btn-group ${t}" role="group" aria-label="${this.escapeHtml(this.ariaLabel)}">${e}</div>`}async onActionSelect(t,e){const i=e.dataset.value;if(i===this.value)return;const s=this.value;this.value=i,this._paintActive(),this.emit("change",{value:i,previous:s})}_paintActive(){this.element&&this.element.querySelectorAll("button[data-value]").forEach(t=>{const e=t.dataset.value===String(this.value);t.classList.toggle("btn-primary",e),t.classList.toggle("btn-outline-secondary",!e),t.setAttribute("aria-pressed",e?"true":"false")})}setValue(t,{silent:e=!1}={}){const i=this.items.find(e=>String(e.value)===String(t));if(!i)return!1;const s=this.value;return i.value===s||(this.value=i.value,this._paintActive(),e||this.emit("change",{value:this.value,previous:s})),!0}getValue(){return this.value}}const l={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 l[i]?{field:e.slice(0,-1).join("__"),lookup:i}:{field:t,lookup:null}}function a(t,e,i){if(!t||null==e)return"";const{field:s,lookup:n}=o(t),a=l[n];if(e&&"object"==typeof e&&!Array.isArray(e)){const t=void 0!==e.start&&null!==e.start&&""!==e.start,s=void 0!==e.end&&null!==e.end&&""!==e.end;return t||s?t&&s?`${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(!n||"exact"===n)return`${i} is '${r}'`;if("in"===n||"not_in"===n){const t=r.split(",").map(t=>t.trim()).filter(t=>t);if(0===t.length)return`${i} ${a.display}`;const e=t.map(t=>`'${t}'`).join(", ");return`${i} ${a.display} ${e}`}if("range"===n){const t=r.split(",").map(t=>t.trim()).filter(t=>t);return 2===t.length?`${i} between '${t[0]}' and '${t[1]}'`:`${i} ${a.display} '${r}'`}return"isnull"===n?`${i} ${"function"==typeof a.display?a.display(r):a.display}`:a?`${i} ${a.display} '${r}'`:`${i} is '${r}'`}const r={LOOKUPS:l,parseFilterKey:o,formatFilterDisplay:a,getLookupDescription:function(t){const e=l[t];return e?e.description:"Exact match"},isValidLookup:function(t){return t&&l.hasOwnProperty(t)},getAvailableLookups:function(){return Object.keys(l)},buildFilterKey:function(t,e=null){return t?e?`${t}__${e}`:t:""}};class ListViewItem extends t{constructor(t={}){super({className:"list-view-item",...t}),this.selected=!1,this.index=t.index??0,this.listView=t.listView??null,this.clickable=!0===t.clickable,this.clickable&&this.element&&this.addClass("clickable"),this.template||(this.template='\n <div class="list-item-content" data-action="select">\n {{#model}}\n {{#id}}<span class="item-id">{{id}}</span>{{/id}}\n {{#name}}<span class="item-name">{{name}}</span>{{/name}}\n {{#title}}<span class="item-title">{{title}}</span>{{/title}}\n {{#label}}<span class="item-label">{{label}}</span>{{/label}}\n {{#description}}<p class="item-description">{{description}}</p>{{/description}}\n {{/model}}\n {{^model}}\n <span class="item-empty">No data</span>\n {{/model}}\n </div>\n ')}async onActionSelect(t,e){t.stopPropagation(),this.selected?this.deselect():this.select()}async onActionView(t,e){t.stopPropagation(),this._emitRowEvent("row:view",t)}async onActionEdit(t,e){t.stopPropagation(),this._emitRowEvent("row:edit",t)}async onActionDelete(t,e){t.stopPropagation(),this._emitRowEvent("row:delete",t)}_emitRowEvent(t,e){const i={item:this,model:this.model,index:this.index,event:e,data:this.model?.toJSON?this.model.toJSON():this.model};this.emit(t,i),this.listView&&this.listView.emit(t,i)}select(){this.selected||(this.selected=!0,this.addClass("selected"),this.emit("item:select",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model}),this.listView&&this.listView.emit("item:select",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model}))}deselect(){this.selected&&(this.selected=!1,this.removeClass("selected"),this.emit("item:deselect",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model}),this.listView&&this.listView.emit("item:deselect",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model}))}async onAfterRender(){await super.onAfterRender(),this.clickable&&this.element&&(this.addClass("clickable"),this._wireClickableHandler()),this.listView?.rowStripe&&"function"==typeof this.listView._applyRowStripe&&this.listView._applyRowStripe(this)}_wireClickableHandler(){!this._clickableHandler&&this.element&&(this._clickableHandler=t=>{if(t.target?.closest?.("[data-action]"))return;const e=t.target?.tagName;"INPUT"!==e&&"TEXTAREA"!==e&&"SELECT"!==e&&(this.emit("item:click",{item:this,model:this.model,index:this.index,action:"row-click",event:t,data:this.model?.toJSON?this.model.toJSON():this.model}),this.listView&&this.listView.emit("item:click",{item:this,model:this.model,index:this.index,action:"row-click",event:t,data:this.model?.toJSON?this.model.toJSON():this.model}))},this.element.addEventListener("click",this._clickableHandler))}async onActionDefault(t,e,i){this.emit("item:click",{item:this,model:this.model,index:this.index,action:t,data:this.model?.toJSON?this.model.toJSON():this.model}),this.listView&&this.listView.emit("item:click",{item:this,model:this.model,index:this.index,action:t,data:this.model?.toJSON?this.model.toJSON():this.model})}setIndex(t){return this.index=t,this.element.setAttribute("data-index",t),this}setSelected(t){return t?this.select():this.deselect(),this}async destroy(){this._clickableHandler&&this.element&&(this.element.removeEventListener("click",this._clickableHandler),this._clickableHandler=null),this.listView=null,await super.destroy()}}class ListGroupHeaderView extends t{constructor(t={}){super({tagName:t.tagName||"div",className:t.className||"list-group-header",...t}),this.key=t.key??"",this.index=t.index??0,this.colspan=t.colspan??1,this.template||(this.template="{{key}}")}}class ListView extends t{static GROUP_HEADER_STYLES=["banner","mark","band","rule"];static ROW_STRIPE_TOKENS=["danger","warning","success","info","primary","secondary"];constructor(t={}){super({className:t.className||"list-view",...t}),this.collection=null,this.itemViews=/* @__PURE__ */new Map,this.selectedItems=/* @__PURE__ */new Set,this.itemTemplate=t.itemTemplate||null,this.itemClass=t.itemClass||ListViewItem,this.selectionMode=t.selectionMode||"none",this.emptyMessage=t.emptyMessage||"No items to display",this.loading=!1,this.isEmpty=!0,this.searchable=!0===t.searchable,this.filterable=!0===t.filterable,this.paginated=!0===t.paginated;let e=t.paginationMode;e||(e=this.paginated?"more":"none"),this.paginationMode=e,this.searchPlacement=t.searchPlacement||"toolbar",this.searchPlaceholder=t.searchPlaceholder||"Search...",this.sortOptions=Array.isArray(t.sortOptions)?t.sortOptions:[],this.filters={},this.additionalFilters=t.filters||[],this.hideActivePills=!0===t.hideActivePills,this.hideActivePillNames=t.hideActivePillNames||[],this.title=t.title||null,this.eyebrow=t.eyebrow||null,this.showRefresh=!1!==t.showRefresh,this.toolbarButtons=t.toolbarButtons||[],this.toolbarRight=t.toolbarRight||null,this._toolbarRightMounted=!1,this.dayRangeFilter=this._normalizeDayRangeFilter(t.dayRangeFilter),this.dayRangeControl=null,this.exportOptions=t.exportOptions||null,this.exportSource=t.exportSource||"remote",this.persistSelection=void 0===t.persistSelection?"more"===this.paginationMode:!0===t.persistSelection,this.onItemClick="function"==typeof t.onItemClick?t.onItemClick:null,this.clickAction=t.clickAction||"none",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.fetchOnView=!1!==t.fetchOnView,this.clickable=!0===t.clickable||!!this.onItemClick||this.clickAction&&"none"!==this.clickAction,this.rowStripe="function"==typeof t.rowStripe?t.rowStripe:null,this.groupBy="function"==typeof t.groupBy||"string"==typeof t.groupBy?t.groupBy:null,this.groupHeaderTemplate=t.groupHeaderTemplate||null,this.groupHeaderLabel="function"==typeof t.groupHeaderLabel?t.groupHeaderLabel:null,this.groupHeaderClass=t.groupHeaderClass||ListGroupHeaderView,this.groupHeaderStyle=ListView.GROUP_HEADER_STYLES.includes(t.groupHeaderStyle)?t.groupHeaderStyle:"banner",this.groupHeaderViews=/* @__PURE__ */new Map,this._renderOrder=[],this.loadingMore=!1,this._isToolbarEnabled()||"none"!==this.paginationMode?this.template=this.buildListTemplate():this.template||(this.template=this._defaultBareTemplate())}async onInit(){this._initCollection(this.options.collection||this.options.Collection),this.dayRangeFilter&&(this._seedDayRangeParams(),this.dayRangeControl=new SegmentControl({containerId:"toolbar-day-range",options:this.dayRangeFilter.options,value:this.dayRangeFilter.value,ariaLabel:this.dayRangeFilter.ariaLabel}),this.dayRangeControl.on("change",this._onDayRangeChange,this),this.addChild(this.dayRangeControl))}async onAfterMount(){await super.onAfterMount(),!this.collection||!this.options.fetchOnMount&&this.collection.lastFetchTime||this.collection.fetch()}async onBeforeRender(){this.searchValue=this.getActiveFilters().search||"",this.hasMore=this._computeHasMore()}async onAfterRender(){if(await super.onAfterRender(),this.toolbarRight&&!this._toolbarRightMounted&&(this.toolbarRight.containerId="toolbar-right",this.addChild(this.toolbarRight),await this.toolbarRight.render(),this._toolbarRightMounted=!0),this._toolbarRightMounted&&this.toolbarRight&&!this.element?.contains(this.toolbarRight.element)&&(this._toolbarRightMounted=!1),this.paginated&&"pages"===this.paginationMode&&this.collection){const t=this.collection.meta?.count||this.collection.length(),e=this.collection.params?.start||0,i=this.collection.params?.size||10,s=Math.min(e+i,t),n=this.element.querySelector('[data-value="start"]'),l=this.element.querySelector('[data-value="end"]'),o=this.element.querySelector('[data-value="total"]');n&&(n.textContent=0===t?0:e+1),l&&(l.textContent=s),o&&(o.textContent=t);const a=this.element.querySelector('[data-change-action="page-size"]');a&&(a.value=i),this.renderPagination()}this._isToolbarEnabled()&&(this.updateFilterPills(),this.setupSearchClearListener())}_defaultBareTemplate(){return'\n <div class="list-view-container">\n {{#loading}}\n <div class="list-loading">\n <div class="spinner-border spinner-border-sm" role="status">\n <span class="visually-hidden">Loading...</span>\n </div>\n Loading...\n </div>\n {{/loading}}\n {{^loading}}\n {{#isEmpty}}\n <div class="list-empty">\n {{emptyMessage}}\n </div>\n {{/isEmpty}}\n {{^isEmpty}}\n <div class="list-items" data-container="items"></div>\n {{/isEmpty}}\n {{/loading}}\n </div>\n '}buildListTemplate(){return`\n <div class="list-view-wrapper">\n ${this.buildToolbarTemplate()}\n <div class="list-view-container">\n {{#loading}}\n <div class="list-loading text-center py-4">\n <div class="spinner-border spinner-border-sm" role="status">\n <span class="visually-hidden">Loading...</span>\n </div>\n Loading...\n </div>\n {{/loading}}\n {{^loading}}\n {{#isEmpty}}\n <div class="list-empty text-center py-4 text-muted">\n {{emptyMessage}}\n </div>\n {{/isEmpty}}\n {{^isEmpty}}\n <div class="list-items" data-container="items"></div>\n {{/isEmpty}}\n {{/loading}}\n </div>\n ${"more"===this.paginationMode?this.buildShowMoreTemplate():""}\n ${"pages"===this.paginationMode?this.buildPaginationTemplate():""}\n </div>\n `}_isToolbarEnabled(){return!!(this.title||this.eyebrow||this.searchable||this.filterable&&this._hasAnyFilters()||this.sortOptions&&this.sortOptions.length>0||this.toolbarRight||this.dayRangeFilter||this.toolbarButtons&&this.toolbarButtons.length>0||this.options.showAdd||this.options.showExport)}_hasAnyFilters(){return this.filters&&Object.keys(this.filters).length>0||this.additionalFilters&&this.additionalFilters.length>0}buildToolbarTemplate(){if(!this._isToolbarEnabled())return"";const t=this._buildTitleBlockTemplate(),e=this.toolbarRight?'<div data-container="toolbar-right"></div>':"",i=this.dayRangeFilter?'<div data-container="toolbar-day-range"></div>':"",s=this.sortOptions&&this.sortOptions.length>0?this.buildSortDropdownTemplate():"";return`\n <div class="table-action-buttons mb-3">\n <div class="d-flex align-items-center gap-3 flex-wrap">\n ${t}\n \n <div class="d-flex align-items-center gap-2 flex-wrap ${t?"ms-auto":""}">\n ${this.buildActionButtonsTemplate()}\n ${s}\n ${this.filterable?this.buildFilterDropdownTemplate():""}\n ${this.searchable&&"toolbar"===this.searchPlacement?this.buildSearchTemplate():""}\n ${i}\n ${e}\n </div>\n \n </div>\n <div data-container="filter-pills"></div>\n </div>\n `}_buildTitleBlockTemplate(){return this.title||this.eyebrow?'<div class="rs-table-title-block">{{#eyebrow}}<div class="text-body-secondary text-uppercase small fw-semibold rs-table-eyebrow" style="letter-spacing: 0.05em; line-height: 1.2;">{{eyebrow}}</div>{{/eyebrow}}{{#title}}<h5 class="mb-0 rs-table-title">{{title}}</h5>{{/title}}</div>':""}buildActionButtonsTemplate(){const t=[];if(this.showRefresh&&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.options.showAdd){const e=this.escapeHtml(this.options.addButtonLabel||"Add"),i=this.escapeHtml(this.options.addButtonIcon||"bi bi-plus-circle");t.push(`\n <button class="btn btn-sm btn-success btn-add"\n data-action="add"\n title="${e}">\n <i class="${i} me-1"></i>\n <span class="d-none d-lg-inline">${e}</span>\n </button>\n `)}if(this.options.showExport){const e=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"}];if(e.length>1){const i=e.map(t=>`\n <li>\n <a class="dropdown-item" href="#" data-action="export"\n data-format="${this.escapeHtml(t.format)}">\n <i class="${this.escapeHtml(t.icon||"bi bi-file-earmark-arrow-down")} me-2"></i>\n ${this.escapeHtml(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">${i}</ul>\n </div>\n `)}else{const i=1===e.length?e[0].format:"json";t.push(`\n <button class="btn btn-sm btn-outline-secondary btn-export"\n data-action="export"\n data-format="${this.escapeHtml(i)}"\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:s="Button",icon:n="",action:l="",handler:o=null,variant:a="outline-secondary",title:r=s,className:c="",permissions:d=null}=e;if(d&&!this.checkPermissions(d))return;const h=this.escapeHtml(n),m=this.escapeHtml(s),p=this.escapeHtml(r),u=this.escapeHtml(a),g=this.escapeHtml(c),f=this.escapeHtml(l),b=n?`<i class="${h} me-1"></i>`:"",y=`<span class="d-none d-lg-inline">${m}</span>`;let w="";o?w=`data-action="custom-toolbar-button" data-button-index="${i}"`:l&&(w=`data-action="${f}"`);const v=`btn btn-sm btn-${u} ${g}`.trim();t.push(`\n <button class="${v}" ${w} title="${p}">\n ${b}${y}\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="${this.escapeHtml(this.searchPlaceholder)}"\n data-filter="search"\n data-change-action="apply-search"\n value="{{searchValue}}"\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 `}buildSortDropdownTemplate(){const t=this.collection?.params?.sort||"";return`\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-sort-down me-1"></i>\n <span class="d-none d-lg-inline">Sort</span>\n </button>\n <ul class="dropdown-menu dropdown-menu-end">\n ${this.sortOptions.map(e=>{const i=`${"desc"===e.dir?"-":""}${e.key}`;return`\n <li><a class="dropdown-item ${t===i?"active":""}" href="#"\n data-action="sort-option" data-sort="${this.escapeHtml(i)}">\n ${this.escapeHtml(e.label||e.key)}\n </a></li>\n `}).join("")}\n ${t?'\n <li><hr class="dropdown-divider"></li>\n <li><a class="dropdown-item" href="#" data-action="sort-option" data-sort="">\n <i class="bi bi-x-circle me-1"></i>Clear sort\n </a></li>\n ':""}\n </ul>\n </div>\n `}buildFilterDropdownTemplate(){return this._hasAnyFilters()?`\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=Object.prototype.hasOwnProperty.call(e,t.key),s=i?"active":"",n=this.getFilterIcon(t.type||t.config?.type);return`\n <button class="dropdown-item ${s}"\n data-action="add-filter"\n data-filter-key="${this.escapeHtml(t.key)}">\n <i class="bi bi-${this.escapeHtml(n)} me-2"></i>\n ${this.escapeHtml(t.label||t.key)}\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 `}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),s=this.getFilterLabel(i),n=this.escapeHtml(a(t,e,s)),l=this.escapeHtml(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 p-0 ms-1 list-filter-pill-text"\n style="font-size: 0.65rem; line-height: 1; text-decoration: none;"\n data-action="edit-filter"\n data-filter="${l}"\n title="Edit filter">\n ${n}\n </button>\n\n <button type="button" class="btn-close ms-1"\n style="font-size: 0.6rem; width: 0.5rem; height: 0.5rem;"\n data-action="remove-filter"\n data-filter="${l}"\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 `:""}buildPaginationTemplate(){return'\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="List pagination">\n <ul class="pagination pagination-sm mb-0 justify-content-center" data-container="pagination"></ul>\n </nav>\n </div>\n </div>\n '}buildShowMoreTemplate(){return'\n {{#hasMore}}\n <div class="list-show-more-row text-center mt-3">\n <button class="btn btn-outline-secondary btn-sm" data-action="show-more"\n {{#loadingMore}}disabled{{/loadingMore}}>\n {{#loadingMore}}\n <span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>\n Loading…\n {{/loadingMore}}\n {{^loadingMore}}\n <i class="bi bi-arrow-down-circle me-1"></i>Show more\n {{/loadingMore}}\n </button>\n </div>\n {{/hasMore}}\n '}_initCollection(t){if(t)if(t instanceof e)this.setCollection(t);else if("function"==typeof t){const e=new t;this.setCollection(e)}else if(Array.isArray(t)){const i=new e(t);this.setCollection(i)}}_normalizeDayRangeFilter(t){if(!t)return null;const e={field:"created",value:"7d",options:[{value:"1d",label:"1d"},{value:"7d",label:"7d"},{value:"30d",label:"30d"},{value:"90d",label:"90d"}],ariaLabel:"Time range"};return!0===t?e:{...e,...t}}_dayRangeDays(t){const e=/^(\d+)d$/.exec(String(t||""));return e?parseInt(e[1],10):null}_seedDayRangeParams(){if(!this.dayRangeFilter||!this.collection)return;const t=this._dayRangeDays(this.dayRangeFilter.value);if(null==t)return;const e=Math.floor(Date.now()/1e3)-86400*t;this.collection.params[`${this.dayRangeFilter.field}__gte`]=e}async _onDayRangeChange({value:t,previous:e}){const i=this.dayRangeFilter?.field||"created",s=this._dayRangeDays(t);let n={};if(null!=s&&this.collection){const t=Math.floor(Date.now()/1e3)-86400*s;this.collection.params[`${i}__gte`]=t,this.collection.params.start=0,n={[`${i}__gte`]:t}}if(this.emit("range:change",{field:i,value:t,previous:e,params:n}),this.emit("params-changed"),this.collection?.restEnabled)try{await this.collection.fetch()}catch(l){console.error("Failed to fetch day-range data:",l),await this.render()}else await this.render()}getRange(){return this.dayRangeControl?.getValue()??null}setRange(t,{silent:e=!1}={}){if(!this.dayRangeControl)return!1;const i=this.dayRangeControl.getValue();return!!this.dayRangeControl.setValue(t,{silent:!0})&&(e||this._onDayRangeChange({value:t,previous:i}),!0)}setCollection(t){return this.collection===t||(this.collection&&(this.collection.off("add",this._onModelsAdded,this),this.collection.off("remove",this._onModelsRemoved,this),this.collection.off("reset",this._onCollectionReset,this),this.collection.off("fetch:start",this._onFetchStart,this),this.collection.off("fetch:end",this._onFetchEnd,this)),this.collection=t,this.options.defaultQuery&&!this.options.collectionParams&&(this.collection.params={...this.collection.params,...this.options.defaultQuery}),this.options.collectionParams&&(this.collection.params={...this.collection.params,...this.options.collectionParams}),this.options.pageSize&&this.collection&&(this.collection.params={...this.collection.params,size:this.options.pageSize}),this.collection&&(this.collection.on("add",this._onModelsAdded,this),this.collection.on("remove",this._onModelsRemoved,this),this.collection.on("reset",this._onCollectionReset,this),this.collection.on("fetch:start",this._onFetchStart,this),this.collection.on("fetch:end",this._onFetchEnd,this),!this.collection.restEnabled||this.collection.lastFetchTime||this.collection.options?.preloaded?this._buildItems():this.loading=!0)),this}async _renderChildren(){await super._renderChildren();const t=this.getChildElement("items");if(!t)return;const e=[];if(this._renderOrder.length>0)for(const i of this._renderOrder)t.appendChild(i.view.element),e.push(Promise.resolve(i.view.render(!1)).catch(()=>{}));else this.forEachItem(i=>{t.appendChild(i.element),e.push(Promise.resolve(i.render(!1)).catch(()=>{}))});await Promise.all(e)}_buildItems(){if(this._clearItems(),!this.collection||this.collection.isEmpty())return this.isEmpty=!0,void this.emit("list:empty");this.isEmpty=!1,this.collection.forEach((t,e)=>{this._createItemView(t,e)}),this._applyPersistedSelections(),this._buildGroupHeaders(),this.emit("list:loaded",{count:this.collection.length()}),this.isMounted()&&this.render()}_buildGroupHeaders(){if(this.groupHeaderViews.forEach(t=>this.removeChild(t.id)),this.groupHeaderViews.clear(),this._renderOrder.length=0,!this.groupBy||!this.collection||this.collection.isEmpty())return;const t="function"==typeof this.groupBy?this.groupBy:t=>t.get(this.groupBy);let e,i=!1;this.collection.forEach((s,n)=>{const l=this.itemViews.get(s.id);if(!l)return;let o;try{o=t(s)}catch(a){console.warn("ListView: groupBy resolver threw — treating as ungrouped tail",a),o=null}if(o&&(!i||o!==e)){const t=this._createGroupHeaderView(s,o,n);this._renderOrder.push({type:"header",view:t}),e=o,i=!0}this._renderOrder.push({type:"item",view:l})})}_rebuildGroupHeaders(){this._buildGroupHeaders()}_createGroupHeaderView(t,e,i){const s=this.groupHeaderLabel?this.groupHeaderLabel(e):e,n=new this.groupHeaderClass({template:this.groupHeaderTemplate||this._defaultGroupHeaderTemplate(),model:t,key:s,index:i,...this._groupHeaderViewOptions(t,e,i)});return this.groupHeaderViews.set(n.id,n),n}_groupHeaderViewOptions(t,e,i){return{className:`list-group-header list-group-header--${this.groupHeaderStyle}`}}_defaultGroupHeaderTemplate(){return"{{key}}"}_createItemView(t,e){if(this.itemViews.has(t.id))return this.itemViews.get(t.id);const i=new this.itemClass({model:t,index:e,listView:this,template:this.itemTemplate,clickable:this.clickable});return this.itemViews.set(t.id,i),this._wireItemViewListeners(i),i}_wireItemViewListeners(t){t.on("item:select",this._onItemSelect.bind(this)),t.on("item:deselect",this._onItemDeselect.bind(this)),t.on("item:click",t=>{"row-click"===t.action&&(this.emit("row:click",t),this._dispatchRowClick(t))}),t.on("row:click",t=>{this._dispatchRowClick(t)}),t.on("row:view",this._onRowView.bind(this)),t.on("row:edit",this._onRowEdit.bind(this)),t.on("row:delete",this._onRowDelete.bind(this))}_dispatchRowClick(t){return"function"==typeof this.options.onRowClick?this.options.onRowClick(t.model,t.event):"function"==typeof this.clickAction?this.clickAction(t.model,t.event):this.onItemClick?this.onItemClick(t.model,t.event):"view"===this.clickAction?this._onRowView(t):"edit"===this.clickAction?this._onRowEdit(t):void("select"===this.clickAction&&(this.selectedItems.has(t.model.id)?this.deselectItem(t.model.id):this.selectItem(t.model.id)))}_applyPersistedSelections(){this.persistSelection&&0!==this.selectedItems.size&&this.itemViews.forEach((t,e)=>{this.selectedItems.has(e)&&!t.selected&&(t.selected=!0,t.element&&t.addClass("selected"))})}_clearItems(){this.forEachItem(t=>{this.removeChild(t.id)}),this.itemViews.clear(),this.groupHeaderViews.forEach(t=>this.removeChild(t.id)),this.groupHeaderViews.clear(),this._renderOrder.length=0,this.persistSelection||this.selectedItems.clear()}_onModelsAdded(t){const{models:e}=t;e.forEach(t=>{const e=this.collection.models.indexOf(t);this._createItemView(t,e)}),this._applyPersistedSelections(),this.groupBy&&this._rebuildGroupHeaders(),this.isEmpty=this.collection.isEmpty(),!this.loading&&this.isMounted()&&this.render()}_onModelsRemoved(t){const{models:e}=t;e.forEach(t=>{const e=this.itemViews.get(t.id);e&&(this.removeChild(e.id),this.itemViews.delete(t.id),this.selectedItems.delete(t.id))}),this.isEmpty=this.collection.isEmpty(),!this.loading&&this.isMounted()&&this.render(),this.isEmpty&&this.emit("list:empty")}_onCollectionReset(t){this._buildItems()}_onFetchStart(){this.loading=!0,this.loadingMore||this.isMounted()&&this.render()}_onFetchEnd(){this.loading=!1,this.loadingMore||this.isMounted()&&this.render()}_onItemSelect(t){const{model:e,item:i}=t;"none"!==this.selectionMode?("single"===this.selectionMode&&(this.itemViews.forEach((t,i)=>{i!==e.id&&t.selected&&t.deselect()}),this.selectedItems.clear()),this.selectedItems.add(e.id),this.emit("selection:change",{selected:Array.from(this.selectedItems),item:i,model:e})):i.deselect()}_onItemDeselect(t){const{model:e}=t;this.selectedItems.delete(e.id),this.emit("selection:change",{selected:Array.from(this.selectedItems),item:t.item,model:e})}getSelectedItems(){const t=[];return this.selectedItems.forEach(e=>{const i=this.itemViews.get(e);i&&t.push({view:i,model:i.model,data:i.model?.toJSON?i.model.toJSON():i.model})}),t}forEachItem(t,e){if("function"!=typeof t)throw new TypeError("Callback must be a function");let i=0;return this.itemViews.forEach(s=>{t.call(e,s,s.model,i++)}),this}clearSelection(){this.forEachItem(t=>{t.selected&&t.deselect()}),this.selectedItems.clear(),this.emit("selection:change",{selected:[]})}selectItem(t){const e=this.itemViews.get(t);return e&&e.select(),this}deselectItem(t){const e=this.itemViews.get(t);return e&&e.deselect(),this}setItemTemplate(t,e=!1){return this.itemTemplate=t,e&&this.itemViews.size>0&&this.forEachItem(e=>{e.setTemplate(t),e.isMounted()&&e.render()}),this}async refresh(){if(this.collection&&this.collection.restEnabled)return await this.collection.fetch();this._buildItems()}_stripeClassFor(t){if(!this.rowStripe||!t)return null;let e;try{e=this.rowStripe(t)}catch(i){return console.warn("ListView: rowStripe callback threw — treating as no stripe",i),null}return null==e||""===e||"string"!=typeof e?null:ListView.ROW_STRIPE_TOKENS.includes(e)?`list-row-stripe-${e}`:e}_applyRowStripe(t){if(!t||!t.element)return;const e=t.element.classList;for(const n of Array.from(e))n.startsWith("list-row-stripe")&&e.remove(n);const i=this._stripeClassFor(t.model);if(i)try{e.add(i)}catch(s){console.warn("ListView: rowStripe returned an invalid class name, skipping",i,s)}}refreshStripes(){return this.rowStripe?(this.forEachItem(t=>this._applyRowStripe(t)),this):this}async _onRowView(t){if(this.emit("row:view",t),this.options.onItemView)return void(await this.options.onItemView(t.model,t.event));if(this.fetchOnView)try{s.loading(),await t.model.fetch()}catch(i){return s.hideLoading(!0),void s.showError(i?.data?.error||i?.message||"Failed to load item details")}finally{s.hideLoading(!0)}const e=this.getItemViewClass(t.model);if(e){const i=new e({model:t.model,collection:this.collection});await s.dialog({header:!1,body:i,size:"lg",centered:!1,...this.getFormDialogConfig(this.getModelClass(t.model)),...e.DIALOG_OPTIONS,...this.viewDialogOptions})}else await s.data({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 s.modelForm({model:t.model,...i,...this.getFormDialogConfig(e)});if(!n)return;if(!n.success||!n?.result?.data.status)return void s.showError(n?.result?.data?.error||n?.result?.message||"An error occurred")}else{const e=await s.dialog({title:`Edit ${this.getModelName(t.model)} #${t.model.id}`,body:new n({model:t.model,fields:this.options.formFields||[]})});if(e){const i=await t.model.save(e);if(!i.data?.status)return void s.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 s.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?.restEnabled?this.collection.fetch():this._buildItems())}async onActionAdd(t,e){if(this.options.onAdd)return this.emit("list:add",{event:t}),void(await this.options.onAdd(t));this.emit("list: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 s.form({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 s.showError(i?.data.error||"An error occurred");this.collection&&this.collection.add(t),await this.refresh()}}else{const t=new i,e=await s.dialog({title:`Add ${this.getModelName()}`,body:new n({model:t,fields:this.options.formFields||[]})});if(e){const i=await t.save(e);if(!i?.data.status)return void s.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("list:export",{format:i,source:this.exportSource,event:t}),"remote"===this.exportSource?this.collection?await this.collection.download(i):console.warn("ListView: Cannot export from remote without a collection."):this.options.onExport?await this.options.onExport(this.collection?.toJSON()||[],i):console.warn("ListView: onExport handler not implemented for local export.")}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.render(t,e):""}async onActionRefresh(t,e){await this.refresh()}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():await this.render()),this.updateFilterPills(),this.emit("list:search",{searchTerm:i}),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("list:search",{searchTerm:""}),this.emit("params-changed")}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)})})}updateFilterPills(){const t=this.element?.querySelector('[data-container="filter-pills"]');t&&(t.innerHTML=this.buildActivePills())}updateSearchInputs(t){const e=this.element?.querySelectorAll('[data-filter="search"]');e&&e.forEach(e=>{e.value=t||""})}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,s=this.collection.params?.start||0,n=Math.floor(s/i)+1,l=Math.ceil(e/i);if(l<=1)return void(t.innerHTML="");const o=n>1?n-1:l,a=n<l?n+1:1,r=[];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-left"></i>\n </a>\n </li>\n `);const c=/* @__PURE__ */new Set([1,l]);for(let m=n-1;m<=n+1;m++)m>=1&&m<=l&&c.add(m);const d=Array.from(c).sort((t,e)=>t-e);let h=0;for(const m of d)h&&m-h>1&&r.push('<li class="page-item disabled"><span class="page-link">…</span></li>'),r.push(`\n <li class="page-item ${m===n?"active":""}">\n <a class="page-link" href="#" data-action="page" data-page="${m}">${m}</a>\n </li>\n `),h=m;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-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),s=this.collection.params?.size||10,n=this.collection.meta?.count||this.collection.length(),l=Math.max(1,Math.ceil(n/s));let o=isNaN(i)?1:i;o<1&&(o=l),o>l&&(o=1),this.collection.setParams({...this.collection.params,start:(o-1)*s}),this.collection.restEnabled?await this.collection.fetch():this.render(),this.emit("list:page",{page:o,event:t}),this.emit("params-changed")}async onChangePageSize(t,e){const i=parseInt(e.value,10);this.collection&&(this.collection.setParams({...this.collection.params,start:0,size:i}),this.collection.restEnabled&&await this.collection.fetch(),this.render()),this.emit("list:pagesize",{size:i}),this.emit("params-changed")}_computeHasMore(){if("more"!==this.paginationMode)return!1;if(!this.collection)return!1;const t=this.collection.meta?.count;return"number"==typeof t&&this.collection.length()<t}async onActionShowMore(t,e){if(!this.loadingMore)if(this.collection&&"function"==typeof this.collection.fetchMore){this.loadingMore=!0,this.isMounted()&&await this.render();try{const t=await this.collection.fetchMore();this.emit("list:show-more",{response:t})}catch(i){console.error("ListView: fetchMore failed",i)}finally{this.loadingMore=!1,this.isMounted()&&await this.render()}}else console.warn("ListView: collection does not support fetchMore()")}async onActionSortOption(t,e){t.preventDefault();const i=e.getAttribute("data-sort");this.collection&&(this.collection.setParams({...this.collection.params,sort:i||void 0,start:0}),this.collection.restEnabled?await this.collection.fetch():this.render()),this.emit("list:sort",{sort:i}),this.emit("params-changed")}async onActionCustomToolbarButton(t,e){const i=parseInt(e.getAttribute("data-button-index"),10),s=this.toolbarButtons[i];s&&"function"==typeof s.handler&&await s.handler.call(this,t,e)}async onActionAddFilter(t,e){const i=e.getAttribute("data-filter-key"),n=this.getFilterConfig(i),l=this.getActiveFilters()[i];if(!n)return void console.warn("No filter config found for key:",i);const o=await s.form({title:`${void 0!==l&&""!==l?"Edit":"Add"} ${this.getFilterLabel(i)} Filter`,size:"md",fields:[this.buildFilterDialogField(n,l,i)]});if(o){const t=this.extractFilterValue(n,o);this.setFilter(i,t),await this.applyFilters()}}async onActionEditFilter(t,e){const i=e.getAttribute("data-filter"),{field:n}=o(i),l=this.getFilterConfig(n)||this.getFilterConfig(i),a=this.getActiveFilters(),r=a[i]||a[n];if(!l)return void console.warn("No filter config found for key:",i,"or field:",n);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 s.form({title:`Edit ${this.getFilterLabel(n)} Filter`,size:"md",data:c,fields:[this.buildFilterDialogField(l,r,n)]});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:s}=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:s}),this.emit("params-changed")}async onActionClearAllFilters(t,e){if(!this.collection)return;const{start:i,size:s,sort:n}=this.collection.params,l={start:i,size:s};n&&(l.sort=n),Array.isArray(this.hideActivePillNames)&&this.hideActivePillNames.length>0&&this.hideActivePillNames.forEach(t=>{void 0!==this.collection.params[t]&&(l[t]=this.collection.params[t]);const e=this.getFilterConfig(t);if(e&&"daterange"===e.type){const t=e.startName||"dr_start",i=e.endName||"dr_end",s=e.fieldName||"dr_field";void 0!==this.collection.params[t]&&(l[t]=this.collection.params[t]),void 0!==this.collection.params[i]&&(l[i]=this.collection.params[i]),void 0!==this.collection.params[s]&&(l[s]=this.collection.params[s])}}),this.collection.params=l,this.updateSearchInputs(""),this.collection.restEnabled&&await this.collection.fetch(),this.render(),this.updateFilterPills(),this.emit("filters:clear"),this.emit("params-changed")}async applyFilters(){if(this.collection&&(this.collection.params.start=0),this.collection?.restEnabled)try{await this.collection.fetch(),await this.render()}catch(t){console.error("Failed to fetch filtered data:",t),await this.render()}else await this.render();this.updateFilterPills(),this.emit("params-changed")}getActiveFilters(){if(!this.collection?.params)return{};const{start:t,size:e,sort:i,...s}=this.collection.params,n={},l=/* @__PURE__ */new Set;return this.getAllAvailableFilters().forEach(t=>{if("daterange"===t.config?.type){const e=t.key,i=t.config.startName||"dr_start",o=t.config.endName||"dr_end",a=t.config.fieldName||"dr_field";s[a]===e&&(s[i]||s[o])&&(n[e]={start:s[i]||"",end:s[o]||""},l.add(i),l.add(o),l.add(a))}}),Object.keys(s).forEach(t=>{l.has(t)||(n[t]=s[t])}),Object.keys(n).forEach(t=>{const e=`${t}__in`;Object.prototype.hasOwnProperty.call(n,e)&&delete n[t]}),n}setFilter(t,e){if(!this.collection)return;const i=this.getFilterConfig(t);if(i&&"daterange"===i.type){const s=i.startName||"dr_start",n=i.endName||"dr_end",l=i.fieldName||"dr_field";delete this.collection.params[s],delete this.collection.params[n],delete this.collection.params[l],e&&"object"==typeof e&&(e.start||e.end)&&(e.start&&(this.collection.params[s]=e.start),e.end&&(this.collection.params[n]=e.end),this.collection.params[l]=t)}else{const{field:i}=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 Object.entries(this.filters||{}).forEach(([e,i])=>{t.push({key:e,label:i.label||e,type:i.type,config:i})}),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){if(this.filters&&this.filters[t])return this.filters[t];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)}getFilterIcon(t){return{text:"search",select:"funnel",date:"calendar",daterange:"calendar-range",number:"123",boolean:"toggle-on"}[t]||"filter"}buildFilterDialogField(t,e,i){const{name:s,value:n,...l}=t,o={...l,name:"filter_value",label:l.label,value:e,placeholder:l.placeholder||l.placeHolder};if("daterange"===t.type){if(o.startName=o.startName||"dr_start",o.endName=o.endName||"dr_end",o.fieldName=o.fieldName||"dr_field",o.format=o.format||"YYYY-MM-DD",o.displayFormat=o.displayFormat||"MMM DD, YYYY",o.separator=o.separator||" to ",o.label=o.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,s=new Date(i);if(!isNaN(s))return s.toISOString().slice(0,10)}const i=new Date(e);return isNaN(i)?e:i.toISOString().slice(0,10)};o.startDate=t(e.start||e.from||e.begin||""),o.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))),o.value=i,o.placeholder||o.placeHolder||(t.placeholder||t.placeHolder?o.placeholder=t.placeholder||t.placeHolder:t.label&&(o.placeholder=`Select ${t.label}...`))}else if("boolean"===t.type||"switch"===t.type||"toggle"===t.type){o.type="select";const i=null!=e&&""!==e?e:t.defaultValue,s=!0===i?"true":!1===i?"false":null==i?"":String(i);o.value=s;const n=t.trueLabel||"True",l=t.falseLabel||"False";o.options=[{value:"true",text:n},{value:"false",text:l}],o.placeholder||o.placeHolder||(o.placeholder=t.label?`Filter by ${t.label}…`:"Select…")}return o}extractFilterValue(t,e){if("daterange"===t.type){const i=t.startName||"dr_start",s=t.endName||"dr_end";return{start:e[i],end:e[s]}}return t.type,e.filter_value}setTitle(t){this.title=t||null;const e=this.element?.querySelector(".rs-table-title");e&&(e.textContent=this.title||"")}setEyebrow(t){this.eyebrow=t||null;const e=this.element?.querySelector(".rs-table-eyebrow");e&&(e.textContent=this.eyebrow||"")}checkPermissions(t){return!0}escapeHtml(t){return null==t?"":String(t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}async destroy(){this.collection&&(this.collection.off("add",this._onModelsAdded,this),this.collection.off("remove",this._onModelsRemoved,this),this.collection.off("reset",this._onCollectionReset,this),this.collection.off("fetch:start",this._onFetchStart,this),this.collection.off("fetch:end",this._onFetchEnd,this)),this._clearItems(),await super.destroy()}}export{r as D,ListViewItem as L,SegmentControl as S,ListView as a,l as b,ListGroupHeaderView as c,a as f,o as p};
2
- //# sourceMappingURL=ListView-C-jiqALE.js.map
1
+ import{V as t,C as e,a as i}from"./User-DgB81g6W.js";import{a as s}from"./Modal-DfAzkbgB.js";import{F as n}from"./FormView-C6XZFZ6s.js";class SegmentControl extends t{constructor(t={}){const{options:e=[],value:i,size:s="sm",ariaLabel:n="Segment control",...l}=t;super({tagName:"div",className:"segment-control",...l}),this.items=e,this.value=void 0!==i?i:e[0]&&e[0].value,this.size="md"===s?"":"sm",this.ariaLabel=n,this.template=()=>this._buildTemplate()}_buildTemplate(){const t=this.size?`btn-group-${this.size}`:"",e=this.items.map(t=>{const e=t.value===this.value,i=e?"btn btn-primary":"btn btn-outline-secondary",s=t.icon?`<i class="bi ${this.escapeHtml(t.icon)} me-1"></i>`:"";return`<button type="button"\n class="${i}"\n data-action="select"\n data-value="${this.escapeHtml(String(t.value))}"\n ${e?'aria-pressed="true"':'aria-pressed="false"'}>${s}${this.escapeHtml(t.label)}</button>`}).join("");return`<div class="btn-group ${t}" role="group" aria-label="${this.escapeHtml(this.ariaLabel)}">${e}</div>`}async onActionSelect(t,e){const i=e.dataset.value;if(i===this.value)return;const s=this.value;this.value=i,this._paintActive(),this.emit("change",{value:i,previous:s})}_paintActive(){this.element&&this.element.querySelectorAll("button[data-value]").forEach(t=>{const e=t.dataset.value===String(this.value);t.classList.toggle("btn-primary",e),t.classList.toggle("btn-outline-secondary",!e),t.setAttribute("aria-pressed",e?"true":"false")})}setValue(t,{silent:e=!1}={}){const i=this.items.find(e=>String(e.value)===String(t));if(!i)return!1;const s=this.value;return i.value===s||(this.value=i.value,this._paintActive(),e||this.emit("change",{value:this.value,previous:s})),!0}getValue(){return this.value}}const l={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 l[i]?{field:e.slice(0,-1).join("__"),lookup:i}:{field:t,lookup:null}}function a(t,e,i){if(!t||null==e)return"";const{field:s,lookup:n}=o(t),a=l[n];if(e&&"object"==typeof e&&!Array.isArray(e)){const t=void 0!==e.start&&null!==e.start&&""!==e.start,s=void 0!==e.end&&null!==e.end&&""!==e.end;return t||s?t&&s?`${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(!n||"exact"===n)return`${i} is '${r}'`;if("in"===n||"not_in"===n){const t=r.split(",").map(t=>t.trim()).filter(t=>t);if(0===t.length)return`${i} ${a.display}`;const e=t.map(t=>`'${t}'`).join(", ");return`${i} ${a.display} ${e}`}if("range"===n){const t=r.split(",").map(t=>t.trim()).filter(t=>t);return 2===t.length?`${i} between '${t[0]}' and '${t[1]}'`:`${i} ${a.display} '${r}'`}return"isnull"===n?`${i} ${"function"==typeof a.display?a.display(r):a.display}`:a?`${i} ${a.display} '${r}'`:`${i} is '${r}'`}const r={LOOKUPS:l,parseFilterKey:o,formatFilterDisplay:a,getLookupDescription:function(t){const e=l[t];return e?e.description:"Exact match"},isValidLookup:function(t){return t&&l.hasOwnProperty(t)},getAvailableLookups:function(){return Object.keys(l)},buildFilterKey:function(t,e=null){return t?e?`${t}__${e}`:t:""}};class ListViewItem extends t{constructor(t={}){super({className:"list-view-item",...t}),this.selected=!1,this.index=t.index??0,this.listView=t.listView??null,this.clickable=!0===t.clickable,this.clickable&&this.element&&this.addClass("clickable"),this.template||(this.template='\n <div class="list-item-content" data-action="select">\n {{#model}}\n {{#id}}<span class="item-id">{{id}}</span>{{/id}}\n {{#name}}<span class="item-name">{{name}}</span>{{/name}}\n {{#title}}<span class="item-title">{{title}}</span>{{/title}}\n {{#label}}<span class="item-label">{{label}}</span>{{/label}}\n {{#description}}<p class="item-description">{{description}}</p>{{/description}}\n {{/model}}\n {{^model}}\n <span class="item-empty">No data</span>\n {{/model}}\n </div>\n ')}async onActionSelect(t,e){t.stopPropagation(),this.selected?this.deselect():this.select()}async onActionView(t,e){t.stopPropagation(),this._emitRowEvent("row:view",t)}async onActionEdit(t,e){t.stopPropagation(),this._emitRowEvent("row:edit",t)}async onActionDelete(t,e){t.stopPropagation(),this._emitRowEvent("row:delete",t)}_emitRowEvent(t,e){const i={item:this,model:this.model,index:this.index,event:e,data:this.model?.toJSON?this.model.toJSON():this.model};this.emit(t,i),this.listView&&this.listView.emit(t,i)}select(){this.selected||(this.selected=!0,this.addClass("selected"),this.emit("item:select",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model}),this.listView&&this.listView.emit("item:select",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model}))}deselect(){this.selected&&(this.selected=!1,this.removeClass("selected"),this.emit("item:deselect",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model}),this.listView&&this.listView.emit("item:deselect",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model}))}async onAfterRender(){await super.onAfterRender(),this.clickable&&this.element&&(this.addClass("clickable"),this._wireClickableHandler()),this.listView?.rowStripe&&"function"==typeof this.listView._applyRowStripe&&this.listView._applyRowStripe(this)}_wireClickableHandler(){!this._clickableHandler&&this.element&&(this._clickableHandler=t=>{if(t.target?.closest?.("[data-action]"))return;const e=t.target?.tagName;"INPUT"!==e&&"TEXTAREA"!==e&&"SELECT"!==e&&(this.emit("item:click",{item:this,model:this.model,index:this.index,action:"row-click",event:t,data:this.model?.toJSON?this.model.toJSON():this.model}),this.listView&&this.listView.emit("item:click",{item:this,model:this.model,index:this.index,action:"row-click",event:t,data:this.model?.toJSON?this.model.toJSON():this.model}))},this.element.addEventListener("click",this._clickableHandler))}async onActionDefault(t,e,i){this.emit("item:click",{item:this,model:this.model,index:this.index,action:t,data:this.model?.toJSON?this.model.toJSON():this.model}),this.listView&&this.listView.emit("item:click",{item:this,model:this.model,index:this.index,action:t,data:this.model?.toJSON?this.model.toJSON():this.model})}setIndex(t){return this.index=t,this.element.setAttribute("data-index",t),this}setSelected(t){return t?this.select():this.deselect(),this}async destroy(){this._clickableHandler&&this.element&&(this.element.removeEventListener("click",this._clickableHandler),this._clickableHandler=null),this.listView=null,await super.destroy()}}class ListGroupHeaderView extends t{constructor(t={}){super({tagName:t.tagName||"div",className:t.className||"list-group-header",...t}),this.key=t.key??"",this.index=t.index??0,this.colspan=t.colspan??1,this.template||(this.template="{{key}}")}}class ListView extends t{static GROUP_HEADER_STYLES=["banner","mark","band","rule"];static ROW_STRIPE_TOKENS=["danger","warning","success","info","primary","secondary"];constructor(t={}){super({className:t.className||"list-view",...t}),this.collection=null,this.itemViews=/* @__PURE__ */new Map,this.selectedItems=/* @__PURE__ */new Set,this.itemTemplate=t.itemTemplate||null,this.itemClass=t.itemClass||ListViewItem,this.selectionMode=t.selectionMode||"none",this.emptyMessage=t.emptyMessage||"No items to display",this.loading=!1,this.isEmpty=!0,this.searchable=!0===t.searchable,this.filterable=!0===t.filterable,this.paginated=!0===t.paginated;let e=t.paginationMode;e||(e=this.paginated?"more":"none"),this.paginationMode=e,this.searchPlacement=t.searchPlacement||"toolbar",this.searchPlaceholder=t.searchPlaceholder||"Search...",this.sortOptions=Array.isArray(t.sortOptions)?t.sortOptions:[],this.filters={},this.additionalFilters=t.filters||[],this.hideActivePills=!0===t.hideActivePills,this.hideActivePillNames=t.hideActivePillNames||[],this.title=t.title||null,this.eyebrow=t.eyebrow||null,this.showRefresh=!1!==t.showRefresh,this.toolbarButtons=t.toolbarButtons||[],this.toolbarRight=t.toolbarRight||null,this._toolbarRightMounted=!1,this.dayRangeFilter=this._normalizeDayRangeFilter(t.dayRangeFilter),this.dayRangeControl=null,this.exportOptions=t.exportOptions||null,this.exportSource=t.exportSource||"remote",this.persistSelection=void 0===t.persistSelection?"more"===this.paginationMode:!0===t.persistSelection,this.onItemClick="function"==typeof t.onItemClick?t.onItemClick:null,this.clickAction=t.clickAction||"none",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.fetchOnView=!1!==t.fetchOnView,this.clickable=!0===t.clickable||!!this.onItemClick||this.clickAction&&"none"!==this.clickAction,this.rowStripe="function"==typeof t.rowStripe?t.rowStripe:null,this.groupBy="function"==typeof t.groupBy||"string"==typeof t.groupBy?t.groupBy:null,this.groupHeaderTemplate=t.groupHeaderTemplate||null,this.groupHeaderLabel="function"==typeof t.groupHeaderLabel?t.groupHeaderLabel:null,this.groupHeaderClass=t.groupHeaderClass||ListGroupHeaderView,this.groupHeaderStyle=ListView.GROUP_HEADER_STYLES.includes(t.groupHeaderStyle)?t.groupHeaderStyle:"banner",this.groupHeaderViews=/* @__PURE__ */new Map,this._renderOrder=[],this.loadingMore=!1,this._isToolbarEnabled()||"none"!==this.paginationMode?this.template=this.buildListTemplate():this.template||(this.template=this._defaultBareTemplate())}async onInit(){this._initCollection(this.options.collection||this.options.Collection),this.dayRangeFilter&&(this._seedDayRangeParams(),this.dayRangeControl=new SegmentControl({containerId:"toolbar-day-range",options:this.dayRangeFilter.options,value:this.dayRangeFilter.value,ariaLabel:this.dayRangeFilter.ariaLabel}),this.dayRangeControl.on("change",this._onDayRangeChange,this),this.addChild(this.dayRangeControl))}async onAfterMount(){await super.onAfterMount(),!this.collection||!this.options.fetchOnMount&&this.collection.lastFetchTime||this.collection.fetch()}async onBeforeRender(){this.searchValue=this.getActiveFilters().search||"",this.hasMore=this._computeHasMore()}async onAfterRender(){if(await super.onAfterRender(),this.toolbarRight&&!this._toolbarRightMounted&&(this.toolbarRight.containerId="toolbar-right",this.addChild(this.toolbarRight),await this.toolbarRight.render(),this._toolbarRightMounted=!0),this._toolbarRightMounted&&this.toolbarRight&&!this.element?.contains(this.toolbarRight.element)&&(this._toolbarRightMounted=!1),this.paginated&&"pages"===this.paginationMode&&this.collection){const t=this.collection.meta?.count||this.collection.length(),e=this.collection.params?.start||0,i=this.collection.params?.size||10,s=Math.min(e+i,t),n=this.element.querySelector('[data-value="start"]'),l=this.element.querySelector('[data-value="end"]'),o=this.element.querySelector('[data-value="total"]');n&&(n.textContent=0===t?0:e+1),l&&(l.textContent=s),o&&(o.textContent=t);const a=this.element.querySelector('[data-change-action="page-size"]');a&&(a.value=i),this.renderPagination()}this._isToolbarEnabled()&&(this.updateFilterPills(),this.setupSearchClearListener())}_defaultBareTemplate(){return'\n <div class="list-view-container">\n {{#loading}}\n <div class="list-loading">\n <div class="spinner-border spinner-border-sm" role="status">\n <span class="visually-hidden">Loading...</span>\n </div>\n Loading...\n </div>\n {{/loading}}\n {{^loading}}\n {{#isEmpty}}\n <div class="list-empty">\n {{emptyMessage}}\n </div>\n {{/isEmpty}}\n {{^isEmpty}}\n <div class="list-items" data-container="items"></div>\n {{/isEmpty}}\n {{/loading}}\n </div>\n '}buildListTemplate(){return`\n <div class="list-view-wrapper">\n ${this.buildToolbarTemplate()}\n <div class="list-view-container">\n {{#loading}}\n <div class="list-loading text-center py-4">\n <div class="spinner-border spinner-border-sm" role="status">\n <span class="visually-hidden">Loading...</span>\n </div>\n Loading...\n </div>\n {{/loading}}\n {{^loading}}\n {{#isEmpty}}\n <div class="list-empty text-center py-4 text-muted">\n {{emptyMessage}}\n </div>\n {{/isEmpty}}\n {{^isEmpty}}\n <div class="list-items" data-container="items"></div>\n {{/isEmpty}}\n {{/loading}}\n </div>\n ${"more"===this.paginationMode?this.buildShowMoreTemplate():""}\n ${"pages"===this.paginationMode?this.buildPaginationTemplate():""}\n </div>\n `}_isToolbarEnabled(){return!!(this.title||this.eyebrow||this.searchable||this.filterable&&this._hasAnyFilters()||this.sortOptions&&this.sortOptions.length>0||this.toolbarRight||this.dayRangeFilter||this.toolbarButtons&&this.toolbarButtons.length>0||this.options.showAdd||this.options.showExport)}_hasAnyFilters(){return this.filters&&Object.keys(this.filters).length>0||this.additionalFilters&&this.additionalFilters.length>0}buildToolbarTemplate(){if(!this._isToolbarEnabled())return"";const t=this._buildTitleBlockTemplate(),e=this.toolbarRight?'<div data-container="toolbar-right"></div>':"",i=this.dayRangeFilter?'<div data-container="toolbar-day-range"></div>':"",s=this.sortOptions&&this.sortOptions.length>0?this.buildSortDropdownTemplate():"";return`\n <div class="table-action-buttons mb-3">\n <div class="d-flex align-items-center gap-3 flex-wrap">\n ${t}\n \n <div class="d-flex align-items-center gap-2 flex-wrap ${t?"ms-auto":""}">\n ${this.buildActionButtonsTemplate()}\n ${s}\n ${this.filterable?this.buildFilterDropdownTemplate():""}\n ${this.searchable&&"toolbar"===this.searchPlacement?this.buildSearchTemplate():""}\n ${i}\n ${e}\n </div>\n \n </div>\n <div data-container="filter-pills"></div>\n </div>\n `}_buildTitleBlockTemplate(){return this.title||this.eyebrow?'<div class="rs-table-title-block">{{#eyebrow}}<div class="text-body-secondary text-uppercase small fw-semibold rs-table-eyebrow" style="letter-spacing: 0.05em; line-height: 1.2;">{{eyebrow}}</div>{{/eyebrow}}{{#title}}<h5 class="mb-0 rs-table-title">{{title}}</h5>{{/title}}</div>':""}buildActionButtonsTemplate(){const t=[];if(this.showRefresh&&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.options.showAdd){const e=this.escapeHtml(this.options.addButtonLabel||"Add"),i=this.escapeHtml(this.options.addButtonIcon||"bi bi-plus-circle");t.push(`\n <button class="btn btn-sm btn-success btn-add"\n data-action="add"\n title="${e}">\n <i class="${i} me-1"></i>\n <span class="d-none d-lg-inline">${e}</span>\n </button>\n `)}if(this.options.showExport){const e=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"}];if(e.length>1){const i=e.map(t=>`\n <li>\n <a class="dropdown-item" href="#" data-action="export"\n data-format="${this.escapeHtml(t.format)}">\n <i class="${this.escapeHtml(t.icon||"bi bi-file-earmark-arrow-down")} me-2"></i>\n ${this.escapeHtml(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">${i}</ul>\n </div>\n `)}else{const i=1===e.length?e[0].format:"json";t.push(`\n <button class="btn btn-sm btn-outline-secondary btn-export"\n data-action="export"\n data-format="${this.escapeHtml(i)}"\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:s="Button",icon:n="",action:l="",handler:o=null,variant:a="outline-secondary",title:r=s,className:c="",permissions:d=null}=e;if(d&&!this.checkPermissions(d))return;const h=this.escapeHtml(n),m=this.escapeHtml(s),p=this.escapeHtml(r),u=this.escapeHtml(a),g=this.escapeHtml(c),f=this.escapeHtml(l),b=n?`<i class="${h} me-1"></i>`:"",y=`<span class="d-none d-lg-inline">${m}</span>`;let w="";o?w=`data-action="custom-toolbar-button" data-button-index="${i}"`:l&&(w=`data-action="${f}"`);const v=`btn btn-sm btn-${u} ${g}`.trim();t.push(`\n <button class="${v}" ${w} title="${p}">\n ${b}${y}\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="${this.escapeHtml(this.searchPlaceholder)}"\n data-filter="search"\n data-change-action="apply-search"\n value="{{searchValue}}"\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 `}buildSortDropdownTemplate(){const t=this.collection?.params?.sort||"";return`\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-sort-down me-1"></i>\n <span class="d-none d-lg-inline">Sort</span>\n </button>\n <ul class="dropdown-menu dropdown-menu-end">\n ${this.sortOptions.map(e=>{const i=`${"desc"===e.dir?"-":""}${e.key}`;return`\n <li><a class="dropdown-item ${t===i?"active":""}" href="#"\n data-action="sort-option" data-sort="${this.escapeHtml(i)}">\n ${this.escapeHtml(e.label||e.key)}\n </a></li>\n `}).join("")}\n ${t?'\n <li><hr class="dropdown-divider"></li>\n <li><a class="dropdown-item" href="#" data-action="sort-option" data-sort="">\n <i class="bi bi-x-circle me-1"></i>Clear sort\n </a></li>\n ':""}\n </ul>\n </div>\n `}buildFilterDropdownTemplate(){return this._hasAnyFilters()?`\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=Object.prototype.hasOwnProperty.call(e,t.key),s=i?"active":"",n=this.getFilterIcon(t.type||t.config?.type);return`\n <button class="dropdown-item ${s}"\n data-action="add-filter"\n data-filter-key="${this.escapeHtml(t.key)}">\n <i class="bi bi-${this.escapeHtml(n)} me-2"></i>\n ${this.escapeHtml(t.label||t.key)}\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 `}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),s=this.getFilterLabel(i),n=this.escapeHtml(a(t,e,s)),l=this.escapeHtml(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 p-0 ms-1 list-filter-pill-text"\n style="font-size: 0.65rem; line-height: 1; text-decoration: none;"\n data-action="edit-filter"\n data-filter="${l}"\n title="Edit filter">\n ${n}\n </button>\n\n <button type="button" class="btn-close ms-1"\n style="font-size: 0.6rem; width: 0.5rem; height: 0.5rem;"\n data-action="remove-filter"\n data-filter="${l}"\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 `:""}buildPaginationTemplate(){return'\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="List pagination">\n <ul class="pagination pagination-sm mb-0 justify-content-center" data-container="pagination"></ul>\n </nav>\n </div>\n </div>\n '}buildShowMoreTemplate(){return'\n {{#hasMore}}\n <div class="list-show-more-row text-center mt-3">\n <button class="btn btn-outline-secondary btn-sm" data-action="show-more"\n {{#loadingMore}}disabled{{/loadingMore}}>\n {{#loadingMore}}\n <span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>\n Loading…\n {{/loadingMore}}\n {{^loadingMore}}\n <i class="bi bi-arrow-down-circle me-1"></i>Show more\n {{/loadingMore}}\n </button>\n </div>\n {{/hasMore}}\n '}_initCollection(t){if(t)if(t instanceof e)this.setCollection(t);else if("function"==typeof t){const e=new t;this.setCollection(e)}else if(Array.isArray(t)){const i=new e(t);this.setCollection(i)}}_normalizeDayRangeFilter(t){if(!t)return null;const e={field:"created",value:"7d",options:[{value:"1d",label:"1d"},{value:"7d",label:"7d"},{value:"30d",label:"30d"},{value:"90d",label:"90d"}],ariaLabel:"Time range"};return!0===t?e:{...e,...t}}_dayRangeDays(t){const e=/^(\d+)d$/.exec(String(t||""));return e?parseInt(e[1],10):null}_seedDayRangeParams(){if(!this.dayRangeFilter||!this.collection)return;const t=this._dayRangeDays(this.dayRangeFilter.value);if(null==t)return;const e=Math.floor(Date.now()/1e3)-86400*t;this.collection.params[`${this.dayRangeFilter.field}__gte`]=e}async _onDayRangeChange({value:t,previous:e}){const i=this.dayRangeFilter?.field||"created",s=this._dayRangeDays(t);let n={};if(null!=s&&this.collection){const t=Math.floor(Date.now()/1e3)-86400*s;this.collection.params[`${i}__gte`]=t,this.collection.params.start=0,n={[`${i}__gte`]:t}}if(this.emit("range:change",{field:i,value:t,previous:e,params:n}),this.emit("params-changed"),this.collection?.restEnabled)try{await this.collection.fetch()}catch(l){console.error("Failed to fetch day-range data:",l),await this.render()}else await this.render()}getRange(){return this.dayRangeControl?.getValue()??null}setRange(t,{silent:e=!1}={}){if(!this.dayRangeControl)return!1;const i=this.dayRangeControl.getValue();return!!this.dayRangeControl.setValue(t,{silent:!0})&&(e||this._onDayRangeChange({value:t,previous:i}),!0)}setCollection(t){return this.collection===t||(this.collection&&(this.collection.off("add",this._onModelsAdded,this),this.collection.off("remove",this._onModelsRemoved,this),this.collection.off("reset",this._onCollectionReset,this),this.collection.off("fetch:start",this._onFetchStart,this),this.collection.off("fetch:end",this._onFetchEnd,this)),this.collection=t,this.options.defaultQuery&&!this.options.collectionParams&&(this.collection.params={...this.collection.params,...this.options.defaultQuery}),this.options.collectionParams&&(this.collection.params={...this.collection.params,...this.options.collectionParams}),this.options.pageSize&&this.collection&&(this.collection.params={...this.collection.params,size:this.options.pageSize}),this.collection&&(this.collection.on("add",this._onModelsAdded,this),this.collection.on("remove",this._onModelsRemoved,this),this.collection.on("reset",this._onCollectionReset,this),this.collection.on("fetch:start",this._onFetchStart,this),this.collection.on("fetch:end",this._onFetchEnd,this),!this.collection.restEnabled||this.collection.lastFetchTime||this.collection.options?.preloaded?this._buildItems():this.loading=!0)),this}async _renderChildren(){await super._renderChildren();const t=this.getChildElement("items");if(!t)return;const e=[];if(this._renderOrder.length>0)for(const i of this._renderOrder)t.appendChild(i.view.element),e.push(Promise.resolve(i.view.render(!1)).catch(()=>{}));else this.forEachItem(i=>{t.appendChild(i.element),e.push(Promise.resolve(i.render(!1)).catch(()=>{}))});await Promise.all(e)}_buildItems(){if(this._clearItems(),!this.collection||this.collection.isEmpty())return this.isEmpty=!0,void this.emit("list:empty");this.isEmpty=!1,this.collection.forEach((t,e)=>{this._createItemView(t,e)}),this._applyPersistedSelections(),this._buildGroupHeaders(),this.emit("list:loaded",{count:this.collection.length()}),this.isMounted()&&this.render()}_buildGroupHeaders(){if(this.groupHeaderViews.forEach(t=>this.removeChild(t.id)),this.groupHeaderViews.clear(),this._renderOrder.length=0,!this.groupBy||!this.collection||this.collection.isEmpty())return;const t="function"==typeof this.groupBy?this.groupBy:t=>t.get(this.groupBy);let e,i=!1;this.collection.forEach((s,n)=>{const l=this.itemViews.get(s.id);if(!l)return;let o;try{o=t(s)}catch(a){console.warn("ListView: groupBy resolver threw — treating as ungrouped tail",a),o=null}if(o&&(!i||o!==e)){const t=this._createGroupHeaderView(s,o,n);this._renderOrder.push({type:"header",view:t}),e=o,i=!0}this._renderOrder.push({type:"item",view:l})})}_rebuildGroupHeaders(){this._buildGroupHeaders()}_createGroupHeaderView(t,e,i){const s=this.groupHeaderLabel?this.groupHeaderLabel(e):e,n=new this.groupHeaderClass({template:this.groupHeaderTemplate||this._defaultGroupHeaderTemplate(),model:t,key:s,index:i,...this._groupHeaderViewOptions(t,e,i)});return this.groupHeaderViews.set(n.id,n),n}_groupHeaderViewOptions(t,e,i){return{className:`list-group-header list-group-header--${this.groupHeaderStyle}`}}_defaultGroupHeaderTemplate(){return"{{key}}"}_createItemView(t,e){if(this.itemViews.has(t.id))return this.itemViews.get(t.id);const i=new this.itemClass({model:t,index:e,listView:this,template:this.itemTemplate,clickable:this.clickable});return this.itemViews.set(t.id,i),this._wireItemViewListeners(i),i}_wireItemViewListeners(t){t.on("item:select",this._onItemSelect.bind(this)),t.on("item:deselect",this._onItemDeselect.bind(this)),t.on("item:click",t=>{"row-click"===t.action&&(this.emit("row:click",t),this._dispatchRowClick(t))}),t.on("row:click",t=>{this._dispatchRowClick(t)}),t.on("row:view",this._onRowView.bind(this)),t.on("row:edit",this._onRowEdit.bind(this)),t.on("row:delete",this._onRowDelete.bind(this))}_dispatchRowClick(t){return"function"==typeof this.options.onRowClick?this.options.onRowClick(t.model,t.event):"function"==typeof this.clickAction?this.clickAction(t.model,t.event):this.onItemClick?this.onItemClick(t.model,t.event):"view"===this.clickAction?this._onRowView(t):"edit"===this.clickAction?this._onRowEdit(t):void("select"===this.clickAction&&(this.selectedItems.has(t.model.id)?this.deselectItem(t.model.id):this.selectItem(t.model.id)))}_applyPersistedSelections(){this.persistSelection&&0!==this.selectedItems.size&&this.itemViews.forEach((t,e)=>{this.selectedItems.has(e)&&!t.selected&&(t.selected=!0,t.element&&t.addClass("selected"))})}_clearItems(){this.forEachItem(t=>{this.removeChild(t.id)}),this.itemViews.clear(),this.groupHeaderViews.forEach(t=>this.removeChild(t.id)),this.groupHeaderViews.clear(),this._renderOrder.length=0,this.persistSelection||this.selectedItems.clear()}_onModelsAdded(t){const{models:e}=t;e.forEach(t=>{const e=this.collection.models.indexOf(t);this._createItemView(t,e)}),this._applyPersistedSelections(),this.groupBy&&this._rebuildGroupHeaders(),this.isEmpty=this.collection.isEmpty(),!this.loading&&this.isMounted()&&this.render()}_onModelsRemoved(t){const{models:e}=t;e.forEach(t=>{const e=this.itemViews.get(t.id);e&&(this.removeChild(e.id),this.itemViews.delete(t.id),this.selectedItems.delete(t.id))}),this.isEmpty=this.collection.isEmpty(),!this.loading&&this.isMounted()&&this.render(),this.isEmpty&&this.emit("list:empty")}_onCollectionReset(t){this._buildItems()}_onFetchStart(){this.loading=!0,this.loadingMore||this.isMounted()&&this.render()}_onFetchEnd(){this.loading=!1,this.loadingMore||this.isMounted()&&this.render()}_onItemSelect(t){const{model:e,item:i}=t;"none"!==this.selectionMode?("single"===this.selectionMode&&(this.itemViews.forEach((t,i)=>{i!==e.id&&t.selected&&t.deselect()}),this.selectedItems.clear()),this.selectedItems.add(e.id),this.emit("selection:change",{selected:Array.from(this.selectedItems),item:i,model:e})):i.deselect()}_onItemDeselect(t){const{model:e}=t;this.selectedItems.delete(e.id),this.emit("selection:change",{selected:Array.from(this.selectedItems),item:t.item,model:e})}getSelectedItems(){const t=[];return this.selectedItems.forEach(e=>{const i=this.itemViews.get(e);i&&t.push({view:i,model:i.model,data:i.model?.toJSON?i.model.toJSON():i.model})}),t}forEachItem(t,e){if("function"!=typeof t)throw new TypeError("Callback must be a function");let i=0;return this.itemViews.forEach(s=>{t.call(e,s,s.model,i++)}),this}clearSelection(){this.forEachItem(t=>{t.selected&&t.deselect()}),this.selectedItems.clear(),this.emit("selection:change",{selected:[]})}selectItem(t){const e=this.itemViews.get(t);return e&&e.select(),this}deselectItem(t){const e=this.itemViews.get(t);return e&&e.deselect(),this}setItemTemplate(t,e=!1){return this.itemTemplate=t,e&&this.itemViews.size>0&&this.forEachItem(e=>{e.setTemplate(t),e.isMounted()&&e.render()}),this}async refresh(){if(this.collection&&this.collection.restEnabled)return await this.collection.fetch();this._buildItems()}_stripeClassFor(t){if(!this.rowStripe||!t)return null;let e;try{e=this.rowStripe(t)}catch(i){return console.warn("ListView: rowStripe callback threw — treating as no stripe",i),null}return null==e||""===e||"string"!=typeof e?null:ListView.ROW_STRIPE_TOKENS.includes(e)?`list-row-stripe-${e}`:e}_applyRowStripe(t){if(!t||!t.element)return;const e=t.element.classList;for(const n of Array.from(e))n.startsWith("list-row-stripe")&&e.remove(n);const i=this._stripeClassFor(t.model);if(i)try{e.add(i)}catch(s){console.warn("ListView: rowStripe returned an invalid class name, skipping",i,s)}}refreshStripes(){return this.rowStripe?(this.forEachItem(t=>this._applyRowStripe(t)),this):this}async _onRowView(t){if(this.emit("row:view",t),this.options.onItemView)return void(await this.options.onItemView(t.model,t.event));if(this.fetchOnView)try{s.loading(),await t.model.fetch()}catch(i){return s.hideLoading(!0),void s.showError(i?.data?.error||i?.message||"Failed to load item details")}finally{s.hideLoading(!0)}const e=this.getItemViewClass(t.model);if(e){const i=new e({model:t.model,collection:this.collection});await s.dialog({header:!1,body:i,size:"lg",centered:!1,...this.getFormDialogConfig(this.getModelClass(t.model)),...e.DIALOG_OPTIONS,...this.viewDialogOptions})}else await s.data({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 s.modelForm({model:t.model,...i,...this.getFormDialogConfig(e)});if(!n)return;if(!n.success||!n?.result?.data.status)return void s.showError(n?.result?.data?.error||n?.result?.message||"An error occurred")}else{const e=await s.dialog({title:`Edit ${this.getModelName(t.model)} #${t.model.id}`,body:new n({model:t.model,fields:this.options.formFields||[]})});if(e){const i=await t.model.save(e);if(!i.data?.status)return void s.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 s.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?.restEnabled?this.collection.fetch():this._buildItems())}async onActionAdd(t,e){if(this.options.onAdd)return this.emit("list:add",{event:t}),void(await this.options.onAdd(t));this.emit("list: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 s.form({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 s.showError(i?.data.error||"An error occurred");this.collection&&this.collection.add(t),await this.refresh()}}else{const t=new i,e=await s.dialog({title:`Add ${this.getModelName()}`,body:new n({model:t,fields:this.options.formFields||[]})});if(e){const i=await t.save(e);if(!i?.data.status)return void s.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("list:export",{format:i,source:this.exportSource,event:t}),"remote"===this.exportSource?this.collection?await this.collection.download(i):console.warn("ListView: Cannot export from remote without a collection."):this.options.onExport?await this.options.onExport(this.collection?.toJSON()||[],i):console.warn("ListView: onExport handler not implemented for local export.")}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.render(t,e):""}async onActionRefresh(t,e){await this.refresh()}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():await this.render()),this.updateFilterPills(),this.emit("list:search",{searchTerm:i}),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("list:search",{searchTerm:""}),this.emit("params-changed")}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)})})}updateFilterPills(){const t=this.element?.querySelector('[data-container="filter-pills"]');t&&(t.innerHTML=this.buildActivePills())}updateSearchInputs(t){const e=this.element?.querySelectorAll('[data-filter="search"]');e&&e.forEach(e=>{e.value=t||""})}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,s=this.collection.params?.start||0,n=Math.floor(s/i)+1,l=Math.ceil(e/i);if(l<=1)return void(t.innerHTML="");const o=n>1?n-1:l,a=n<l?n+1:1,r=[];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-left"></i>\n </a>\n </li>\n `);const c=/* @__PURE__ */new Set([1,l]);for(let m=n-1;m<=n+1;m++)m>=1&&m<=l&&c.add(m);const d=Array.from(c).sort((t,e)=>t-e);let h=0;for(const m of d)h&&m-h>1&&r.push('<li class="page-item disabled"><span class="page-link">…</span></li>'),r.push(`\n <li class="page-item ${m===n?"active":""}">\n <a class="page-link" href="#" data-action="page" data-page="${m}">${m}</a>\n </li>\n `),h=m;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-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),s=this.collection.params?.size||10,n=this.collection.meta?.count||this.collection.length(),l=Math.max(1,Math.ceil(n/s));let o=isNaN(i)?1:i;o<1&&(o=l),o>l&&(o=1),this.collection.setParams({...this.collection.params,start:(o-1)*s}),this.collection.restEnabled?await this.collection.fetch():this.render(),this.emit("list:page",{page:o,event:t}),this.emit("params-changed")}async onChangePageSize(t,e){const i=parseInt(e.value,10);this.collection&&(this.collection.setParams({...this.collection.params,start:0,size:i}),this.collection.restEnabled&&await this.collection.fetch(),this.render()),this.emit("list:pagesize",{size:i}),this.emit("params-changed")}_computeHasMore(){if("more"!==this.paginationMode)return!1;if(!this.collection)return!1;const t=this.collection.meta?.count;return"number"==typeof t&&this.collection.length()<t}async onActionShowMore(t,e){if(!this.loadingMore)if(this.collection&&"function"==typeof this.collection.fetchMore){this.loadingMore=!0,this.isMounted()&&await this.render();try{const t=await this.collection.fetchMore();this.emit("list:show-more",{response:t})}catch(i){console.error("ListView: fetchMore failed",i)}finally{this.loadingMore=!1,this.isMounted()&&await this.render()}}else console.warn("ListView: collection does not support fetchMore()")}async onActionSortOption(t,e){t.preventDefault();const i=e.getAttribute("data-sort");this.collection&&(this.collection.setParams({...this.collection.params,sort:i||void 0,start:0}),this.collection.restEnabled?await this.collection.fetch():this.render()),this.emit("list:sort",{sort:i}),this.emit("params-changed")}async onActionCustomToolbarButton(t,e){const i=parseInt(e.getAttribute("data-button-index"),10),s=this.toolbarButtons[i];s&&"function"==typeof s.handler&&await s.handler.call(this,t,e)}async onActionAddFilter(t,e){const i=e.getAttribute("data-filter-key"),n=this.getFilterConfig(i),l=this.getActiveFilters()[i];if(!n)return void console.warn("No filter config found for key:",i);const o=await s.form({title:`${void 0!==l&&""!==l?"Edit":"Add"} ${this.getFilterLabel(i)} Filter`,size:"md",fields:[this.buildFilterDialogField(n,l,i)]});if(o){const t=this.extractFilterValue(n,o);this.setFilter(i,t),await this.applyFilters()}}async onActionEditFilter(t,e){const i=e.getAttribute("data-filter"),{field:n}=o(i),l=this.getFilterConfig(n)||this.getFilterConfig(i),a=this.getActiveFilters(),r=a[i]||a[n];if(!l)return void console.warn("No filter config found for key:",i,"or field:",n);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 s.form({title:`Edit ${this.getFilterLabel(n)} Filter`,size:"md",data:c,fields:[this.buildFilterDialogField(l,r,n)]});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:s}=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:s}),this.emit("params-changed")}async onActionClearAllFilters(t,e){if(!this.collection)return;const{start:i,size:s,sort:n}=this.collection.params,l={start:i,size:s};n&&(l.sort=n),Array.isArray(this.hideActivePillNames)&&this.hideActivePillNames.length>0&&this.hideActivePillNames.forEach(t=>{void 0!==this.collection.params[t]&&(l[t]=this.collection.params[t]);const e=this.getFilterConfig(t);if(e&&"daterange"===e.type){const t=e.startName||"dr_start",i=e.endName||"dr_end",s=e.fieldName||"dr_field";void 0!==this.collection.params[t]&&(l[t]=this.collection.params[t]),void 0!==this.collection.params[i]&&(l[i]=this.collection.params[i]),void 0!==this.collection.params[s]&&(l[s]=this.collection.params[s])}}),this.collection.params=l,this.updateSearchInputs(""),this.collection.restEnabled&&await this.collection.fetch(),this.render(),this.updateFilterPills(),this.emit("filters:clear"),this.emit("params-changed")}async applyFilters(){if(this.collection&&(this.collection.params.start=0),this.collection?.restEnabled)try{await this.collection.fetch(),await this.render()}catch(t){console.error("Failed to fetch filtered data:",t),await this.render()}else await this.render();this.updateFilterPills(),this.emit("params-changed")}getActiveFilters(){if(!this.collection?.params)return{};const{start:t,size:e,sort:i,...s}=this.collection.params,n={},l=/* @__PURE__ */new Set;return this.getAllAvailableFilters().forEach(t=>{if("daterange"===t.config?.type){const e=t.key,i=t.config.startName||"dr_start",o=t.config.endName||"dr_end",a=t.config.fieldName||"dr_field";s[a]===e&&(s[i]||s[o])&&(n[e]={start:s[i]||"",end:s[o]||""},l.add(i),l.add(o),l.add(a))}}),Object.keys(s).forEach(t=>{l.has(t)||(n[t]=s[t])}),Object.keys(n).forEach(t=>{const e=`${t}__in`;Object.prototype.hasOwnProperty.call(n,e)&&delete n[t]}),n}setFilter(t,e){if(!this.collection)return;const i=this.getFilterConfig(t);if(i&&"daterange"===i.type){const s=i.startName||"dr_start",n=i.endName||"dr_end",l=i.fieldName||"dr_field";delete this.collection.params[s],delete this.collection.params[n],delete this.collection.params[l],e&&"object"==typeof e&&(e.start||e.end)&&(e.start&&(this.collection.params[s]=e.start),e.end&&(this.collection.params[n]=e.end),this.collection.params[l]=t)}else{const{field:i}=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 Object.entries(this.filters||{}).forEach(([e,i])=>{t.push({key:e,label:i.label||e,type:i.type,config:i})}),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){if(this.filters&&this.filters[t])return this.filters[t];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)}getFilterIcon(t){return{text:"search",select:"funnel",date:"calendar",daterange:"calendar-range",number:"123",boolean:"toggle-on"}[t]||"filter"}buildFilterDialogField(t,e,i){const{name:s,value:n,...l}=t,o={...l,name:"filter_value",label:l.label,value:e,placeholder:l.placeholder||l.placeHolder};if("daterange"===t.type){if(o.startName=o.startName||"dr_start",o.endName=o.endName||"dr_end",o.fieldName=o.fieldName||"dr_field",o.format=o.format||"YYYY-MM-DD",o.displayFormat=o.displayFormat||"MMM DD, YYYY",o.separator=o.separator||" to ",o.label=o.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,s=new Date(i);if(!isNaN(s))return s.toISOString().slice(0,10)}const i=new Date(e);return isNaN(i)?e:i.toISOString().slice(0,10)};o.startDate=t(e.start||e.from||e.begin||""),o.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))),o.value=i,o.placeholder||o.placeHolder||(t.placeholder||t.placeHolder?o.placeholder=t.placeholder||t.placeHolder:t.label&&(o.placeholder=`Select ${t.label}...`))}else if("boolean"===t.type||"switch"===t.type||"toggle"===t.type){o.type="select";const i=null!=e&&""!==e?e:t.defaultValue,s=!0===i?"true":!1===i?"false":null==i?"":String(i);o.value=s;const n=t.trueLabel||"True",l=t.falseLabel||"False";o.options=[{value:"true",text:n},{value:"false",text:l}],o.placeholder||o.placeHolder||(o.placeholder=t.label?`Filter by ${t.label}…`:"Select…")}return o}extractFilterValue(t,e){if("daterange"===t.type){const i=t.startName||"dr_start",s=t.endName||"dr_end";return{start:e[i],end:e[s]}}return t.type,e.filter_value}setTitle(t){this.title=t||null;const e=this.element?.querySelector(".rs-table-title");e&&(e.textContent=this.title||"")}setEyebrow(t){this.eyebrow=t||null;const e=this.element?.querySelector(".rs-table-eyebrow");e&&(e.textContent=this.eyebrow||"")}checkPermissions(t){return!0}escapeHtml(t){return null==t?"":String(t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}async destroy(){this.collection&&(this.collection.off("add",this._onModelsAdded,this),this.collection.off("remove",this._onModelsRemoved,this),this.collection.off("reset",this._onCollectionReset,this),this.collection.off("fetch:start",this._onFetchStart,this),this.collection.off("fetch:end",this._onFetchEnd,this)),this._clearItems(),await super.destroy()}}export{r as D,ListViewItem as L,SegmentControl as S,ListView as a,l as b,ListGroupHeaderView as c,a as f,o as p};
2
+ //# sourceMappingURL=ListView-McEedPG8.js.map