web-mojo 2.2.97 → 2.2.99

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 (127) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/admin.cjs.js +1 -1
  3. package/dist/admin.es.js +1 -1
  4. package/dist/auth.cjs.js +1 -1
  5. package/dist/auth.es.js +1 -1
  6. package/dist/charts.cjs.js +1 -1
  7. package/dist/charts.es.js +1 -1
  8. package/dist/chunks/{AssistantPanelView-BR17_qm2.js → AssistantPanelView-7LrbfDui.js} +2 -2
  9. package/dist/chunks/{AssistantPanelView-BR17_qm2.js.map → AssistantPanelView-7LrbfDui.js.map} +1 -1
  10. package/dist/chunks/{AssistantPanelView-DFcOLZn2.js → AssistantPanelView-BVy5oJ22.js} +2 -2
  11. package/dist/chunks/{AssistantPanelView-DFcOLZn2.js.map → AssistantPanelView-BVy5oJ22.js.map} +1 -1
  12. package/dist/chunks/ChatView-BB87MPeq.js +2 -0
  13. package/dist/chunks/ChatView-BB87MPeq.js.map +1 -0
  14. package/dist/chunks/ChatView-DiniUXTJ.js +2 -0
  15. package/dist/chunks/ChatView-DiniUXTJ.js.map +1 -0
  16. package/dist/chunks/{Collection-DXcf6bOu.js → Collection-C39Oy2q0.js} +2 -2
  17. package/dist/chunks/{Collection-DXcf6bOu.js.map → Collection-C39Oy2q0.js.map} +1 -1
  18. package/dist/chunks/{Collection-DwIvN0yq.js → Collection-CyK0u557.js} +2 -2
  19. package/dist/chunks/{Collection-DwIvN0yq.js.map → Collection-CyK0u557.js.map} +1 -1
  20. package/dist/chunks/{ContextMenu-BWDMrvKJ.js → ContextMenu-D-C7V6J6.js} +2 -2
  21. package/dist/chunks/{ContextMenu-BWDMrvKJ.js.map → ContextMenu-D-C7V6J6.js.map} +1 -1
  22. package/dist/chunks/{ContextMenu-oonPaMfx.js → ContextMenu-hjqR1pGW.js} +2 -2
  23. package/dist/chunks/{ContextMenu-oonPaMfx.js.map → ContextMenu-hjqR1pGW.js.map} +1 -1
  24. package/dist/chunks/{DataView-DdrFwbjE.js → DataView-6lhf855r.js} +2 -2
  25. package/dist/chunks/{DataView-DdrFwbjE.js.map → DataView-6lhf855r.js.map} +1 -1
  26. package/dist/chunks/{DataView-BqQhHIhZ.js → DataView-Bb83ww5U.js} +2 -2
  27. package/dist/chunks/{DataView-BqQhHIhZ.js.map → DataView-Bb83ww5U.js.map} +1 -1
  28. package/dist/chunks/{Dialog-BO06wfM8.js → Dialog-B0r9puaZ.js} +3 -3
  29. package/dist/chunks/Dialog-B0r9puaZ.js.map +1 -0
  30. package/dist/chunks/{Dialog-DVabZtKC.js → Dialog-GzCkpMad.js} +3 -3
  31. package/dist/chunks/Dialog-GzCkpMad.js.map +1 -0
  32. package/dist/chunks/{FormView-CW7sg6-U.js → FormView-DEROOklJ.js} +3 -3
  33. package/dist/chunks/{FormView-CW7sg6-U.js.map → FormView-DEROOklJ.js.map} +1 -1
  34. package/dist/chunks/{FormView-DbxACn7-.js → FormView-UmvPskWT.js} +3 -3
  35. package/dist/chunks/{FormView-DbxACn7-.js.map → FormView-UmvPskWT.js.map} +1 -1
  36. package/dist/chunks/{ListView-ZsMVvvUQ.js → ListView-D_hOtfWZ.js} +2 -2
  37. package/dist/chunks/{ListView-ZsMVvvUQ.js.map → ListView-D_hOtfWZ.js.map} +1 -1
  38. package/dist/chunks/{ListView-DLyS4Nfd.js → ListView-Jkke6pU1.js} +2 -2
  39. package/dist/chunks/{ListView-DLyS4Nfd.js.map → ListView-Jkke6pU1.js.map} +1 -1
  40. package/dist/chunks/{MetricsCountryMapView-197-WHEu.js → MetricsCountryMapView-UdvJWArx.js} +2 -2
  41. package/dist/chunks/{MetricsCountryMapView-197-WHEu.js.map → MetricsCountryMapView-UdvJWArx.js.map} +1 -1
  42. package/dist/chunks/{MetricsCountryMapView-BgLr-rjI.js → MetricsCountryMapView-jLWCL6Hm.js} +2 -2
  43. package/dist/chunks/{MetricsCountryMapView-BgLr-rjI.js.map → MetricsCountryMapView-jLWCL6Hm.js.map} +1 -1
  44. package/dist/chunks/{MetricsMiniChartWidget-BMF6wIF9.js → MetricsMiniChartWidget-CV-McxOl.js} +2 -2
  45. package/dist/chunks/{MetricsMiniChartWidget-BMF6wIF9.js.map → MetricsMiniChartWidget-CV-McxOl.js.map} +1 -1
  46. package/dist/chunks/{MetricsMiniChartWidget-DrIFgvOj.js → MetricsMiniChartWidget-DG0DeDuR.js} +2 -2
  47. package/dist/chunks/{MetricsMiniChartWidget-DrIFgvOj.js.map → MetricsMiniChartWidget-DG0DeDuR.js.map} +1 -1
  48. package/dist/chunks/{MiniPieChart-eTjaZQGx.js → MiniPieChart-B3sM4Bjv.js} +2 -2
  49. package/dist/chunks/{MiniPieChart-eTjaZQGx.js.map → MiniPieChart-B3sM4Bjv.js.map} +1 -1
  50. package/dist/chunks/{MiniPieChart-DOFUDyEj.js → MiniPieChart-DL5Rynvm.js} +2 -2
  51. package/dist/chunks/{MiniPieChart-DOFUDyEj.js.map → MiniPieChart-DL5Rynvm.js.map} +1 -1
  52. package/dist/chunks/{MiniSeriesChart-DPwiySbr.js → MiniSeriesChart-C4DPVbU_.js} +2 -2
  53. package/dist/chunks/{MiniSeriesChart-DPwiySbr.js.map → MiniSeriesChart-C4DPVbU_.js.map} +1 -1
  54. package/dist/chunks/{MiniSeriesChart-Cs8F8j0y.js → MiniSeriesChart-DdNMLwfh.js} +2 -2
  55. package/dist/chunks/{MiniSeriesChart-Cs8F8j0y.js.map → MiniSeriesChart-DdNMLwfh.js.map} +1 -1
  56. package/dist/chunks/{Modal-MSmE_82x.js → Modal-jWZGSSOo.js} +2 -2
  57. package/dist/chunks/{Modal-MSmE_82x.js.map → Modal-jWZGSSOo.js.map} +1 -1
  58. package/dist/chunks/{Modal-D_bVwX-V.js → Modal-xSaurlfZ.js} +2 -2
  59. package/dist/chunks/{Modal-D_bVwX-V.js.map → Modal-xSaurlfZ.js.map} +1 -1
  60. package/dist/chunks/{Passkeys-CZ_oTngY.js → Passkeys-BPV1gNkR.js} +2 -2
  61. package/dist/chunks/{Passkeys-CZ_oTngY.js.map → Passkeys-BPV1gNkR.js.map} +1 -1
  62. package/dist/chunks/{Passkeys-C6VT-snT.js → Passkeys-CIRjqi7n.js} +2 -2
  63. package/dist/chunks/{Passkeys-C6VT-snT.js.map → Passkeys-CIRjqi7n.js.map} +1 -1
  64. package/dist/chunks/{TokenManager-BUzSwY3u.js → TokenManager-CKgK0MIz.js} +2 -2
  65. package/dist/chunks/{TokenManager-BUzSwY3u.js.map → TokenManager-CKgK0MIz.js.map} +1 -1
  66. package/dist/chunks/{TokenManager-BzMDp2Ym.js → TokenManager-Cu0YmxAx.js} +2 -2
  67. package/dist/chunks/{TokenManager-BzMDp2Ym.js.map → TokenManager-Cu0YmxAx.js.map} +1 -1
  68. package/dist/chunks/{UserProfileView-NpL2wU6h.js → UserProfileView-BXT8COCF.js} +2 -2
  69. package/dist/chunks/{UserProfileView-NpL2wU6h.js.map → UserProfileView-BXT8COCF.js.map} +1 -1
  70. package/dist/chunks/{UserProfileView-CxrYFgMl.js → UserProfileView-Slj-9C__.js} +2 -2
  71. package/dist/chunks/{UserProfileView-CxrYFgMl.js.map → UserProfileView-Slj-9C__.js.map} +1 -1
  72. package/dist/chunks/{WebApp-DpuN7rZ3.js → WebApp-CXkNqtZX.js} +2 -2
  73. package/dist/chunks/{WebApp-DpuN7rZ3.js.map → WebApp-CXkNqtZX.js.map} +1 -1
  74. package/dist/chunks/{WebApp-CyeQb4cM.js → WebApp-m6YeJFWY.js} +2 -2
  75. package/dist/chunks/{WebApp-CyeQb4cM.js.map → WebApp-m6YeJFWY.js.map} +1 -1
  76. package/dist/chunks/{WebSocketClient-CPJGeiPN.js → WebSocketClient-BQAZr8C4.js} +2 -2
  77. package/dist/chunks/{WebSocketClient-CPJGeiPN.js.map → WebSocketClient-BQAZr8C4.js.map} +1 -1
  78. package/dist/chunks/{WebSocketClient-Cjmr5C_u.js → WebSocketClient-YR5d82h8.js} +2 -2
  79. package/dist/chunks/{WebSocketClient-Cjmr5C_u.js.map → WebSocketClient-YR5d82h8.js.map} +1 -1
  80. package/dist/chunks/admin-BsmX-4pc.js +2 -0
  81. package/dist/chunks/admin-BsmX-4pc.js.map +1 -0
  82. package/dist/chunks/admin-CzhyMUYh.js +2 -0
  83. package/dist/chunks/admin-CzhyMUYh.js.map +1 -0
  84. package/dist/chunks/{index-D3v4btI8.js → index-CSmG_P2r.js} +2 -2
  85. package/dist/chunks/{index-D3v4btI8.js.map → index-CSmG_P2r.js.map} +1 -1
  86. package/dist/chunks/{index-BPn9cZzq.js → index-uo77m1wt.js} +2 -2
  87. package/dist/chunks/{index-BPn9cZzq.js.map → index-uo77m1wt.js.map} +1 -1
  88. package/dist/chunks/{version-hgKnkQjn.js → version-C1YeA5NE.js} +2 -2
  89. package/dist/chunks/{version-hgKnkQjn.js.map → version-C1YeA5NE.js.map} +1 -1
  90. package/dist/chunks/{version-CT8Bg3nZ.js → version-CZlqPaoP.js} +2 -2
  91. package/dist/chunks/{version-CT8Bg3nZ.js.map → version-CZlqPaoP.js.map} +1 -1
  92. package/dist/css/web-mojo.css +1 -1
  93. package/dist/docit.cjs.js +1 -1
  94. package/dist/docit.es.js +1 -1
  95. package/dist/index.cjs.js +1 -1
  96. package/dist/index.cjs.js.map +1 -1
  97. package/dist/index.es.js +1 -1
  98. package/dist/index.es.js.map +1 -1
  99. package/dist/lightbox.cjs.js +1 -1
  100. package/dist/lightbox.cjs.js.map +1 -1
  101. package/dist/lightbox.es.js +1 -1
  102. package/dist/lightbox.es.js.map +1 -1
  103. package/dist/map.cjs.js +1 -1
  104. package/dist/map.es.js +1 -1
  105. package/dist/timeline.cjs.js +1 -1
  106. package/dist/timeline.es.js +1 -1
  107. package/dist/user-profile.cjs.js +1 -1
  108. package/dist/user-profile.es.js +1 -1
  109. package/dist/web-mojo.lite.iife.js +109 -2
  110. package/dist/web-mojo.lite.iife.js.map +1 -1
  111. package/dist/web-mojo.lite.iife.min.js +98 -98
  112. package/dist/web-mojo.lite.iife.min.js.map +1 -1
  113. package/package.json +1 -1
  114. package/dist/chunks/ChatView-DzdJkSRe.js +0 -2
  115. package/dist/chunks/ChatView-DzdJkSRe.js.map +0 -1
  116. package/dist/chunks/ChatView-E-Y3ZAn2.js +0 -2
  117. package/dist/chunks/ChatView-E-Y3ZAn2.js.map +0 -1
  118. package/dist/chunks/Dialog-BO06wfM8.js.map +0 -1
  119. package/dist/chunks/Dialog-DVabZtKC.js.map +0 -1
  120. package/dist/chunks/PDFViewer-Dv2OEqy0.js +0 -2
  121. package/dist/chunks/PDFViewer-Dv2OEqy0.js.map +0 -1
  122. package/dist/chunks/PDFViewer-b0l9yCQw.js +0 -2
  123. package/dist/chunks/PDFViewer-b0l9yCQw.js.map +0 -1
  124. package/dist/chunks/admin-B4J2Qfvt.js +0 -2
  125. package/dist/chunks/admin-B4J2Qfvt.js.map +0 -1
  126. package/dist/chunks/admin-CYy-fnPz.js +0 -2
  127. package/dist/chunks/admin-CYy-fnPz.js.map +0 -1
package/dist/map.cjs.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./chunks/MetricsCountryMapView-197-WHEu.js"),t=require("./chunks/Collection-DwIvN0yq.js"),s=require("./chunks/Dialog-DVabZtKC.js"),i=require("./chunks/FormPlugins-CEjco_Hb.js");class LocationClient{constructor({basePath:e="/api",endpoints:t={}}={}){this.basePath=String(e||""),this.sessionToken=null,this.endpoints={validate:"/location/address/validate",autocomplete:"/location/address/suggestions",details:"/location/address/place-details",geocode:"/location/address/geocode",reverse:"/location/address/reverse-geocode",timezone:"/location/timezone",...t}}setAuthHeader(e){this._authHeader=e}headers(e){let t=null;if("function"==typeof this._authHeader)try{t=this._authHeader()}catch{t=null}else t=this._authHeader;const s={"Content-Type":"application/json",...e||{}};return t&&(s.Authorization=t),s}async jsonGet(e,s){const i=await t.rest.GET(this.fullPath(e),s||{});return i&&void 0!==i.data?i.data:i}async jsonPost(e,s){const i=await t.rest.POST(this.fullPath(e),s??{},{},{});return i&&void 0!==i.data?i.data:i}fullPath(e){return`${this.basePath}${e}`}async _safeJson(e){try{return await e.json()}catch{return null}}validateAddress(e){return this.jsonPost(this.endpoints.validate,e)}async autocomplete(e,t={}){if(!e||0===String(e).trim().length)return{success:!0,data:[],size:0,count:0};this.sessionToken||(this.sessionToken=this._createSessionToken());const s={input:e,session_token:this.sessionToken,...t},i=await this.jsonGet(this.endpoints.autocomplete,s);return i&&i.session_token&&(this.sessionToken=i.session_token),i}placeDetails({place_id:e,session_token:t,id:s}={}){const i={},n=e||s||null;return n&&(i.place_id=n),t&&(i.session_token=t),this.jsonGet(this.endpoints.details,i)}geocode(e){return this.jsonPost(this.endpoints.geocode,{address:e})}reverseGeocode({lat:e,lng:t}){return this.jsonGet(this.endpoints.reverse,{lat:e,lng:t})}timezone({lat:e,lng:t}){return this.jsonGet(this.endpoints.timezone,{lat:e,lng:t})}resetSessionToken(){this.sessionToken=null}normalizeSuggestion(e){return{id:e?.id||e?.place_id||null,place_id:e?.place_id||e?.id||null,description:e?.description||"",main_text:e?.main_text||"",secondary_text:e?.secondary_text||"",types:e?.types||[]}}_createSessionToken(){return"undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}}function n(e,{client:t,field:s="address1",dropdownClass:i="loc-suggest",minChars:n=3,debounceMs:o=200,mapping:a={address1:"address1",city:"city",state_code:"state",postal_code:"postal_code",country_code:"country",latitude:"latitude",longitude:"longitude",formatted_address:"formatted_address",place_id:"place_id"},onSelect:r}={}){if(!e||!e.element)return console.warn("[useLocationAutocomplete] Missing formView or formView.element"),()=>{};if(!t||"function"!=typeof t.autocomplete||"function"!=typeof t.placeDetails)return console.warn("[useLocationAutocomplete] Missing or invalid client. Provide an object with autocomplete() and placeDetails()."),()=>{};const l="string"==typeof s?e.element.querySelector(`input[name="${s}"], #${s}`)||null:s instanceof HTMLElement?s:null;if(!l)return()=>{};const d=document.createElement("div");d.className=i||"loc-suggest",d.style.position="absolute",d.style.zIndex="10000",d.style.display="none",d.style.background="#fff",d.style.border="1px solid #e5e7eb",d.style.borderRadius="8px",d.style.boxShadow="0 8px 24px rgba(0,0,0,.08)",d.style.padding="4px 0",d.style.maxHeight="280px",d.style.overflowY="auto",d.style.minWidth="240px",d.setAttribute("role","listbox"),d.setAttribute("aria-label","Address suggestions");let c=!1,u=null,p=!1;function h(){if(!c)return;const e=l.getBoundingClientRect();d.style.minWidth=`${e.width}px`,d.style.left=`${e.left+window.scrollX}px`,d.style.top=`${e.bottom+window.scrollY+4}px`}function m(){c=!1,d.style.display="none",d.innerHTML="",d.parentNode&&d.parentNode.removeChild(d)}async function y(){const s=l.value.trim();if(s.length<n)m();else try{const i=await t.autocomplete(s),n=(Array.isArray(i?.data)?i.data:[]).map(e=>({id:e.id,place_id:e.place_id,description:e.description,main_text:e.main_text,secondary_text:e.secondary_text,types:e.types}));c||(c=!0,d.style.display="block",document.body.appendChild(d),h()),h(),await async function(s){if(d.innerHTML="",!s||0===s.length){const e=document.createElement("div");return e.style.padding="8px 12px",e.style.color="#6b7280",e.textContent="No results",void d.appendChild(e)}s.forEach((s,i)=>d.appendChild(function(s,i){const n=document.createElement("div");n.setAttribute("role","option"),n.setAttribute("tabindex","-1"),n.style.padding="8px 12px",n.style.cursor="pointer",n.style.display="flex",n.style.flexDirection="column",n.dataset.index=String(i);const d=document.createElement("div");d.style.fontWeight="600",d.style.color="#111827",d.textContent=s.main_text||s.description||"";const c=document.createElement("div");return c.style.fontSize="12px",c.style.color="#6b7280",c.textContent=s.secondary_text||"",n.appendChild(d),c.textContent&&n.appendChild(c),n.addEventListener("mouseenter",()=>{n.style.background="#f3f4f6"}),n.addEventListener("mouseleave",()=>{n.style.background="transparent"}),n.addEventListener("mousedown",i=>{i.preventDefault(),async function(s){try{const i=s.place_id||s.id;let n=null;if(i){const e=await t.placeDetails({place_id:i,id:i,session_token:t.sessionToken});n=e?.address||null}p=!0,clearTimeout(u),n?.formatted_address?(l.value=n.formatted_address,l.dispatchEvent(new Event("input",{bubbles:!0}))):s.description&&(l.value=s.description,l.dispatchEvent(new Event("input",{bubbles:!0})));try{l.blur()}catch{}m(),setTimeout(()=>{p=!1},o+50),n&&a&&"object"==typeof a&&Object.entries(a).forEach(([t,s])=>{if(!s)return;const i=n[t];if(null!=i){try{"function"==typeof e.setFieldValue&&e.setFieldValue(s,String(i))}catch(o){}const t=e.element.querySelector(`input[name="${s}"], #${s}, textarea[name="${s}"], select[name="${s}"]`);t&&(t.value=String(i),t.dispatchEvent(new Event("input",{bubbles:!0})),t.dispatchEvent(new Event("change",{bubbles:!0})))}}),"function"==typeof r&&r(n||null)}catch(i){console.warn("[useLocationAutocomplete] placeDetails error:",i)}finally{m()}}(s)}),n}(s,i)))}(n)}catch(i){console.warn("[useLocationAutocomplete] autocomplete error:",i),m()}}function f(){p||(clearTimeout(u),u=setTimeout(y,o))}function b(){p||l.value.trim().length>=n&&f()}function g(){setTimeout(()=>{d.contains(document.activeElement)||m()},120)}function _(){c&&h()}function v(e){d.contains(e.target)||e.target===l||m()}return l.addEventListener("input",f),l.addEventListener("focus",b),l.addEventListener("blur",g),window.addEventListener("resize",_),window.addEventListener("scroll",_,!0),document.addEventListener("click",v),function(){clearTimeout(u);try{l.removeEventListener("input",f)}catch(e){}try{l.removeEventListener("focus",b)}catch(e){}try{l.removeEventListener("blur",g)}catch(e){}try{window.removeEventListener("resize",_)}catch(e){}try{window.removeEventListener("scroll",_,!0)}catch(e){}try{document.removeEventListener("click",v)}catch(e){}try{m()}catch(e){}}}class LocationDetailsView extends t.View{constructor({details:e={},height:t=260,tileLayer:s="osm"}={}){super({className:"location-details-view"}),this.details=e||{},this.height=Number.isFinite(t)?t:260,this.tileLayer=s||"osm",this.formatted_address=this.details.formatted_address||"",this.place_id=this.details.place_id||"",this.latitude=this._toNumber(this.details.latitude??this.details.lat??null),this.longitude=this._toNumber(this.details.longitude??this.details.lng??null),this.hasCoords=Number.isFinite(this.latitude)&&Number.isFinite(this.longitude),this._mapView=null,this.template='\n <div class="loc-details">\n {{#formatted_address}}\n <div class="mb-2 fw-semibold" style="word-break: break-word;">\n {{formatted_address}}\n </div>\n {{/formatted_address}}\n\n {{#place_id}}\n <div class="text-muted small mb-2">Place ID: {{place_id}}</div>\n {{/place_id}}\n\n {{^formatted_address}}\n <div class="text-muted small mb-2">No formatted address provided</div>\n {{/formatted_address}}\n\n {{#hasCoords}}\n <div data-container="map"></div>\n {{/hasCoords}}\n\n {{^hasCoords}}\n <div class="text-muted small" style="border: 1px dashed #e5e7eb; border-radius: 8px; padding: 12px;">\n No coordinates available for map preview\n </div>\n {{/hasCoords}}\n </div>\n '}async onInit(){if(this.hasCoords){const t=[{lat:this.latitude,lng:this.longitude,popup:this.formatted_address||""}];this._mapView=new e.MapView({markers:t,center:[this.latitude,this.longitude],zoom:13,height:this.height,tileLayer:this.tileLayer,showLayerControl:!0,containerId:"map"}),this.addChild(this._mapView)}}_toNumber(e){if(null==e||""===e)return null;const t=Number(e);return Number.isFinite(t)?t:null}}class LocationPickerView extends t.View{constructor({client:e,minChars:t=3,debounceMs:s=200,placeholder:i="Search address",height:n=240,tileLayer:o="osm"}={}){super({className:"location-picker-view",template:'<div class="location-picker-view-root"></div>'}),this.client=e,this.minChars=t,this.debounceMs=s,this.placeholder=i,this.height=n,this.tileLayer=o,this._selected=null,this._timer=null,this._suggestions=[],this._elements={},this._previewView=null}getSelected(){return this._selected}async onAfterRender(){const e=document.createElement("div");e.style.display="grid",e.style.gap="10px";const t=document.createElement("input");t.type="text",t.className="form-control",t.placeholder=this.placeholder||"Search address",t.setAttribute("aria-label","Address search"),e.appendChild(t);const s=document.createElement("div");s.style.border="1px solid #e5e7eb",s.style.borderRadius="8px",s.style.background="#fff",s.style.boxShadow="0 8px 24px rgba(0,0,0,.08)",s.style.maxHeight="260px",s.style.overflowY="auto",s.style.display="none",e.appendChild(s);const i=document.createElement("div");e.appendChild(i),this.element.appendChild(e),this._elements={root:e,input:t,dd:s,previewHost:i},t.addEventListener("input",()=>{clearTimeout(this._timer),this._timer=setTimeout(()=>this._handleInput(),this.debounceMs)}),t.addEventListener("focus",()=>{(t.value||"").trim().length>=this.minChars&&this._handleInput()}),await this._renderPreview(null)}async _handleInput(){const e=(this._elements.input.value||"").trim();if(e.length<this.minChars)return this._elements.dd.style.display="none",void(this._elements.dd.innerHTML="");try{const t=await this.client.autocomplete(e),s=Array.isArray(t?.data)?t.data:[];this._suggestions=s.map(e=>({id:e.id,place_id:e.place_id,description:e.description,main_text:e.main_text,secondary_text:e.secondary_text,types:e.types})),await this._renderSuggestions()}catch(t){this._suggestions=[],await this._renderSuggestions()}}async _renderSuggestions(){const e=this._elements.dd;if(e.innerHTML="",!this._suggestions.length){const t=document.createElement("div");return t.style.padding="8px 12px",t.style.color="#6b7280",t.textContent="No results",e.appendChild(t),void(e.style.display="block")}this._suggestions.forEach((t,s)=>{const i=document.createElement("div");i.style.padding="8px 12px",i.style.cursor="pointer",i.style.display="flex",i.style.flexDirection="column",i.addEventListener("mouseenter",()=>{i.style.background="#f3f4f6"}),i.addEventListener("mouseleave",()=>{i.style.background="transparent"});const n=document.createElement("div");n.style.fontWeight="600",n.style.color="#111827",n.textContent=t.main_text||t.description||"";const o=document.createElement("div");o.style.fontSize="12px",o.style.color="#6b7280",o.textContent=t.secondary_text||"",i.appendChild(n),o.textContent&&i.appendChild(o),i.addEventListener("mousedown",e=>{e.preventDefault(),this._selectSuggestion(t)}),e.appendChild(i)}),e.style.display="block"}async _selectSuggestion(e){try{const t=e.place_id||e.id;if(!t)return;const s=await this.client.placeDetails({place_id:t,id:t,session_token:this.client.sessionToken}),i=s?.address||null;i?.formatted_address?this._elements.input.value=i.formatted_address:e.description&&(this._elements.input.value=e.description),this._selected=i||null,this._elements.dd.style.display="none",this._elements.dd.innerHTML="",await this._renderPreview(this._selected)}catch(t){}}async _renderPreview(e){if(this._elements.previewHost.innerHTML="",!e){const e=document.createElement("div");return e.style.border="1px dashed #e5e7eb",e.style.borderRadius="8px",e.style.padding="12px",e.style.color="#6b7280",e.textContent="No location selected",void this._elements.previewHost.appendChild(e)}try{this._previewView=new LocationDetailsView({details:e,height:this.height,tileLayer:this.tileLayer}),await this._previewView.render(!0,this._elements.previewHost)}catch{const t=document.createElement("div");t.style.border="1px dashed #e5e7eb",t.style.borderRadius="8px",t.style.padding="12px",t.textContent=e?.formatted_address||"Selected location",this._elements.previewHost.appendChild(t)}}async onBeforeDestroy(){clearTimeout(this._timer),await super.onBeforeDestroy()}}class LocationFormPlugin{constructor({basePath:e="/api",mapping:t,registerFieldType:s=!0,fieldTypeName:i="address",attributeSelector:n="data-location",minChars:o=3,debounceMs:a=200,suppressBrowserAutocomplete:r=!0,autocompleteValue:l="new-password"}={}){this.id="location",this.client=new LocationClient({basePath:e}),this.mapping=t||{address1:"address1",city:"city",state_code:"state",postal_code:"postal_code",country_code:"country",latitude:"latitude",longitude:"longitude",formatted_address:"formatted_address",place_id:"place_id"},this.fieldTypeName=i,this.attributeSelector=n,this.minChars=o,this.debounceMs=a,this.suppressBrowserAutocomplete=!1!==r,this.autocompleteValue=l||"new-password",s&&(this.fieldTypes={[this.fieldTypeName]:(e,t)=>this.renderAddressField(e,t)})}renderAddressField(e,t){const s=this.suppressBrowserAutocomplete?`autocomplete="${this.autocompleteValue}" autocapitalize="off" autocorrect="off" spellcheck="false" inputmode="search"`:"",i={...t,type:"text",placeholder:t.placeholder||"Start typing an address",attrs:this.mergeAttrs(t.attrs,`${this.attributeSelector}="address" ${s} aria-autocomplete="list" role="combobox"`)};if("function"==typeof e.renderTextField)return e.renderTextField(i);if("function"==typeof e.renderInputField)return e.renderInputField(i,"text");const n=e.getFieldId?.(i.name)||`field_${i.name}`;return`\n <div class="mojo-form-control">\n ${i.label?`<label for="${n}" class="${e.options?.labelClass||"form-label"}">${i.label}</label>`:""}\n <input type="text" id="${n}" name="${i.name}" class="${e.options?.inputClass||"form-control"}"\n placeholder="${i.placeholder||""}" ${this.attributeSelector}="address" ${s} />\n </div>\n `}mergeAttrs(e,t){const s=(e||"").trim(),i=(t||"").trim();return s?i?`${s} ${i}`:s:i}onFormViewInit(e){}onAfterRender(e){if(e?.element)try{const t=`input[${this.attributeSelector}="address"]`;e.element.querySelectorAll(t).forEach(t=>{if(t.dataset&&"1"===t.dataset._locationBound)return;if(this.suppressBrowserAutocomplete)try{t.setAttribute("autocomplete",this.autocompleteValue),t.setAttribute("autocapitalize","off"),t.setAttribute("autocorrect","off"),t.setAttribute("spellcheck","false"),t.setAttribute("inputmode","search")}catch(o){}let s;const i=()=>{s=n(e,{client:this.client,field:t,mapping:this.mapping,minChars:this.minChars,debounceMs:this.debounceMs,onSelect:e=>{try{t.blur()}catch(o){}try{s&&s()}catch(o){}setTimeout(()=>{i()},this.debounceMs+50)}}),t.dataset._locationBound="1",this._trackDisposer(e,s)};i()})}catch(t){}}onFieldInit(e,t,s){try{const o=s?.type===this.fieldTypeName,a="address"===t?.getAttribute?.(this.attributeSelector);if(o||a){if(t.dataset&&"1"===t.dataset._locationBound)return;if(this.suppressBrowserAutocomplete)try{t.setAttribute("autocomplete",this.autocompleteValue),t.setAttribute("autocapitalize","off"),t.setAttribute("autocorrect","off"),t.setAttribute("spellcheck","false"),t.setAttribute("inputmode","search")}catch(i){}let s;const o=()=>{s=n(e,{client:this.client,field:t,mapping:this.mapping,minChars:this.minChars,debounceMs:this.debounceMs,onSelect:e=>{try{t.blur()}catch(i){}try{s&&s()}catch(i){}setTimeout(()=>{o()},this.debounceMs+50)}}),t.dataset._locationBound="1",this._trackDisposer(e,s)};o()}}catch(o){}}onFieldChange(e,t,s){}_trackDisposer(e,t){if(!t||"function"!=typeof t)return;if(!e)return;e._locationDisposers||Object.defineProperty(e,"_locationDisposers",{configurable:!0,enumerable:!1,writable:!0,value:[]}),e._locationDisposers.push(t);const s=t=>{if("function"==typeof e.on)try{return e.on(t,()=>{try{e._locationDisposers?.forEach(e=>{try{e()}catch{}})}finally{e._locationDisposers=[]}}),!0}catch{}return!1};s("before:destroy")||s("destroy")}}exports.MapLibreView=e.MapLibreView,exports.MapView=e.MapView,exports.MetricsCountryMapView=e.MetricsCountryMapView,exports.Collection=t.Collection,exports.Model=t.Model,exports.View=t.View,exports.LocationClient=LocationClient,exports.LocationDetailsView=LocationDetailsView,exports.LocationFormPlugin=LocationFormPlugin,exports.registerLocationPlugin=function(e={}){const t=new LocationFormPlugin(e);return i.FormPlugins.register(t)},exports.showLocationDetailsDialog=async function({client:e=new LocationClient({basePath:"/api"}),details:t=null,place_id:i=null,id:n=null,title:o="Location Details",height:a=260,tileLayer:r="osm"}={}){let l=t;if(!l&&(i||n))try{const t=await e.placeDetails({place_id:i,id:n,session_token:e.sessionToken});l=t?.address||null}catch(c){l={formatted_address:"Unable to load place details",error:c?.message||"Unknown error"}}const d=new LocationDetailsView({details:l||{},height:a,tileLayer:r});return s.Dialog.showDialog({title:o,body:d,size:"md",buttons:[{text:"Close",class:"btn-secondary",dismiss:!0,value:"close"}]})},exports.showLocationPickerDialog=async function({client:e=new LocationClient({basePath:"/api"}),title:t="Pick a Location",minChars:i=3,debounceMs:n=200,placeholder:o="Search address",confirmText:a="Select",height:r=240,tileLayer:l="osm"}={}){const d=new LocationPickerView({client:e,minChars:i,debounceMs:n,placeholder:o,height:r,tileLayer:l});return"ok"===await s.Dialog.showDialog({title:t,body:d,size:"md",buttons:[{text:"Cancel",class:"btn-secondary",dismiss:!0,value:"cancel"},{text:a,class:"btn-primary",value:"ok"}]})&&d.getSelected()||null},exports.useLocationAutocomplete=n;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./chunks/MetricsCountryMapView-UdvJWArx.js"),t=require("./chunks/Collection-CyK0u557.js"),s=require("./chunks/Dialog-GzCkpMad.js"),i=require("./chunks/FormPlugins-CEjco_Hb.js");class LocationClient{constructor({basePath:e="/api",endpoints:t={}}={}){this.basePath=String(e||""),this.sessionToken=null,this.endpoints={validate:"/location/address/validate",autocomplete:"/location/address/suggestions",details:"/location/address/place-details",geocode:"/location/address/geocode",reverse:"/location/address/reverse-geocode",timezone:"/location/timezone",...t}}setAuthHeader(e){this._authHeader=e}headers(e){let t=null;if("function"==typeof this._authHeader)try{t=this._authHeader()}catch{t=null}else t=this._authHeader;const s={"Content-Type":"application/json",...e||{}};return t&&(s.Authorization=t),s}async jsonGet(e,s){const i=await t.rest.GET(this.fullPath(e),s||{});return i&&void 0!==i.data?i.data:i}async jsonPost(e,s){const i=await t.rest.POST(this.fullPath(e),s??{},{},{});return i&&void 0!==i.data?i.data:i}fullPath(e){return`${this.basePath}${e}`}async _safeJson(e){try{return await e.json()}catch{return null}}validateAddress(e){return this.jsonPost(this.endpoints.validate,e)}async autocomplete(e,t={}){if(!e||0===String(e).trim().length)return{success:!0,data:[],size:0,count:0};this.sessionToken||(this.sessionToken=this._createSessionToken());const s={input:e,session_token:this.sessionToken,...t},i=await this.jsonGet(this.endpoints.autocomplete,s);return i&&i.session_token&&(this.sessionToken=i.session_token),i}placeDetails({place_id:e,session_token:t,id:s}={}){const i={},n=e||s||null;return n&&(i.place_id=n),t&&(i.session_token=t),this.jsonGet(this.endpoints.details,i)}geocode(e){return this.jsonPost(this.endpoints.geocode,{address:e})}reverseGeocode({lat:e,lng:t}){return this.jsonGet(this.endpoints.reverse,{lat:e,lng:t})}timezone({lat:e,lng:t}){return this.jsonGet(this.endpoints.timezone,{lat:e,lng:t})}resetSessionToken(){this.sessionToken=null}normalizeSuggestion(e){return{id:e?.id||e?.place_id||null,place_id:e?.place_id||e?.id||null,description:e?.description||"",main_text:e?.main_text||"",secondary_text:e?.secondary_text||"",types:e?.types||[]}}_createSessionToken(){return"undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}}function n(e,{client:t,field:s="address1",dropdownClass:i="loc-suggest",minChars:n=3,debounceMs:o=200,mapping:a={address1:"address1",city:"city",state_code:"state",postal_code:"postal_code",country_code:"country",latitude:"latitude",longitude:"longitude",formatted_address:"formatted_address",place_id:"place_id"},onSelect:r}={}){if(!e||!e.element)return console.warn("[useLocationAutocomplete] Missing formView or formView.element"),()=>{};if(!t||"function"!=typeof t.autocomplete||"function"!=typeof t.placeDetails)return console.warn("[useLocationAutocomplete] Missing or invalid client. Provide an object with autocomplete() and placeDetails()."),()=>{};const l="string"==typeof s?e.element.querySelector(`input[name="${s}"], #${s}`)||null:s instanceof HTMLElement?s:null;if(!l)return()=>{};const d=document.createElement("div");d.className=i||"loc-suggest",d.style.position="absolute",d.style.zIndex="10000",d.style.display="none",d.style.background="#fff",d.style.border="1px solid #e5e7eb",d.style.borderRadius="8px",d.style.boxShadow="0 8px 24px rgba(0,0,0,.08)",d.style.padding="4px 0",d.style.maxHeight="280px",d.style.overflowY="auto",d.style.minWidth="240px",d.setAttribute("role","listbox"),d.setAttribute("aria-label","Address suggestions");let c=!1,u=null,p=!1;function h(){if(!c)return;const e=l.getBoundingClientRect();d.style.minWidth=`${e.width}px`,d.style.left=`${e.left+window.scrollX}px`,d.style.top=`${e.bottom+window.scrollY+4}px`}function m(){c=!1,d.style.display="none",d.innerHTML="",d.parentNode&&d.parentNode.removeChild(d)}async function y(){const s=l.value.trim();if(s.length<n)m();else try{const i=await t.autocomplete(s),n=(Array.isArray(i?.data)?i.data:[]).map(e=>({id:e.id,place_id:e.place_id,description:e.description,main_text:e.main_text,secondary_text:e.secondary_text,types:e.types}));c||(c=!0,d.style.display="block",document.body.appendChild(d),h()),h(),await async function(s){if(d.innerHTML="",!s||0===s.length){const e=document.createElement("div");return e.style.padding="8px 12px",e.style.color="#6b7280",e.textContent="No results",void d.appendChild(e)}s.forEach((s,i)=>d.appendChild(function(s,i){const n=document.createElement("div");n.setAttribute("role","option"),n.setAttribute("tabindex","-1"),n.style.padding="8px 12px",n.style.cursor="pointer",n.style.display="flex",n.style.flexDirection="column",n.dataset.index=String(i);const d=document.createElement("div");d.style.fontWeight="600",d.style.color="#111827",d.textContent=s.main_text||s.description||"";const c=document.createElement("div");return c.style.fontSize="12px",c.style.color="#6b7280",c.textContent=s.secondary_text||"",n.appendChild(d),c.textContent&&n.appendChild(c),n.addEventListener("mouseenter",()=>{n.style.background="#f3f4f6"}),n.addEventListener("mouseleave",()=>{n.style.background="transparent"}),n.addEventListener("mousedown",i=>{i.preventDefault(),async function(s){try{const i=s.place_id||s.id;let n=null;if(i){const e=await t.placeDetails({place_id:i,id:i,session_token:t.sessionToken});n=e?.address||null}p=!0,clearTimeout(u),n?.formatted_address?(l.value=n.formatted_address,l.dispatchEvent(new Event("input",{bubbles:!0}))):s.description&&(l.value=s.description,l.dispatchEvent(new Event("input",{bubbles:!0})));try{l.blur()}catch{}m(),setTimeout(()=>{p=!1},o+50),n&&a&&"object"==typeof a&&Object.entries(a).forEach(([t,s])=>{if(!s)return;const i=n[t];if(null!=i){try{"function"==typeof e.setFieldValue&&e.setFieldValue(s,String(i))}catch(o){}const t=e.element.querySelector(`input[name="${s}"], #${s}, textarea[name="${s}"], select[name="${s}"]`);t&&(t.value=String(i),t.dispatchEvent(new Event("input",{bubbles:!0})),t.dispatchEvent(new Event("change",{bubbles:!0})))}}),"function"==typeof r&&r(n||null)}catch(i){console.warn("[useLocationAutocomplete] placeDetails error:",i)}finally{m()}}(s)}),n}(s,i)))}(n)}catch(i){console.warn("[useLocationAutocomplete] autocomplete error:",i),m()}}function f(){p||(clearTimeout(u),u=setTimeout(y,o))}function b(){p||l.value.trim().length>=n&&f()}function g(){setTimeout(()=>{d.contains(document.activeElement)||m()},120)}function _(){c&&h()}function v(e){d.contains(e.target)||e.target===l||m()}return l.addEventListener("input",f),l.addEventListener("focus",b),l.addEventListener("blur",g),window.addEventListener("resize",_),window.addEventListener("scroll",_,!0),document.addEventListener("click",v),function(){clearTimeout(u);try{l.removeEventListener("input",f)}catch(e){}try{l.removeEventListener("focus",b)}catch(e){}try{l.removeEventListener("blur",g)}catch(e){}try{window.removeEventListener("resize",_)}catch(e){}try{window.removeEventListener("scroll",_,!0)}catch(e){}try{document.removeEventListener("click",v)}catch(e){}try{m()}catch(e){}}}class LocationDetailsView extends t.View{constructor({details:e={},height:t=260,tileLayer:s="osm"}={}){super({className:"location-details-view"}),this.details=e||{},this.height=Number.isFinite(t)?t:260,this.tileLayer=s||"osm",this.formatted_address=this.details.formatted_address||"",this.place_id=this.details.place_id||"",this.latitude=this._toNumber(this.details.latitude??this.details.lat??null),this.longitude=this._toNumber(this.details.longitude??this.details.lng??null),this.hasCoords=Number.isFinite(this.latitude)&&Number.isFinite(this.longitude),this._mapView=null,this.template='\n <div class="loc-details">\n {{#formatted_address}}\n <div class="mb-2 fw-semibold" style="word-break: break-word;">\n {{formatted_address}}\n </div>\n {{/formatted_address}}\n\n {{#place_id}}\n <div class="text-muted small mb-2">Place ID: {{place_id}}</div>\n {{/place_id}}\n\n {{^formatted_address}}\n <div class="text-muted small mb-2">No formatted address provided</div>\n {{/formatted_address}}\n\n {{#hasCoords}}\n <div data-container="map"></div>\n {{/hasCoords}}\n\n {{^hasCoords}}\n <div class="text-muted small" style="border: 1px dashed #e5e7eb; border-radius: 8px; padding: 12px;">\n No coordinates available for map preview\n </div>\n {{/hasCoords}}\n </div>\n '}async onInit(){if(this.hasCoords){const t=[{lat:this.latitude,lng:this.longitude,popup:this.formatted_address||""}];this._mapView=new e.MapView({markers:t,center:[this.latitude,this.longitude],zoom:13,height:this.height,tileLayer:this.tileLayer,showLayerControl:!0,containerId:"map"}),this.addChild(this._mapView)}}_toNumber(e){if(null==e||""===e)return null;const t=Number(e);return Number.isFinite(t)?t:null}}class LocationPickerView extends t.View{constructor({client:e,minChars:t=3,debounceMs:s=200,placeholder:i="Search address",height:n=240,tileLayer:o="osm"}={}){super({className:"location-picker-view",template:'<div class="location-picker-view-root"></div>'}),this.client=e,this.minChars=t,this.debounceMs=s,this.placeholder=i,this.height=n,this.tileLayer=o,this._selected=null,this._timer=null,this._suggestions=[],this._elements={},this._previewView=null}getSelected(){return this._selected}async onAfterRender(){const e=document.createElement("div");e.style.display="grid",e.style.gap="10px";const t=document.createElement("input");t.type="text",t.className="form-control",t.placeholder=this.placeholder||"Search address",t.setAttribute("aria-label","Address search"),e.appendChild(t);const s=document.createElement("div");s.style.border="1px solid #e5e7eb",s.style.borderRadius="8px",s.style.background="#fff",s.style.boxShadow="0 8px 24px rgba(0,0,0,.08)",s.style.maxHeight="260px",s.style.overflowY="auto",s.style.display="none",e.appendChild(s);const i=document.createElement("div");e.appendChild(i),this.element.appendChild(e),this._elements={root:e,input:t,dd:s,previewHost:i},t.addEventListener("input",()=>{clearTimeout(this._timer),this._timer=setTimeout(()=>this._handleInput(),this.debounceMs)}),t.addEventListener("focus",()=>{(t.value||"").trim().length>=this.minChars&&this._handleInput()}),await this._renderPreview(null)}async _handleInput(){const e=(this._elements.input.value||"").trim();if(e.length<this.minChars)return this._elements.dd.style.display="none",void(this._elements.dd.innerHTML="");try{const t=await this.client.autocomplete(e),s=Array.isArray(t?.data)?t.data:[];this._suggestions=s.map(e=>({id:e.id,place_id:e.place_id,description:e.description,main_text:e.main_text,secondary_text:e.secondary_text,types:e.types})),await this._renderSuggestions()}catch(t){this._suggestions=[],await this._renderSuggestions()}}async _renderSuggestions(){const e=this._elements.dd;if(e.innerHTML="",!this._suggestions.length){const t=document.createElement("div");return t.style.padding="8px 12px",t.style.color="#6b7280",t.textContent="No results",e.appendChild(t),void(e.style.display="block")}this._suggestions.forEach((t,s)=>{const i=document.createElement("div");i.style.padding="8px 12px",i.style.cursor="pointer",i.style.display="flex",i.style.flexDirection="column",i.addEventListener("mouseenter",()=>{i.style.background="#f3f4f6"}),i.addEventListener("mouseleave",()=>{i.style.background="transparent"});const n=document.createElement("div");n.style.fontWeight="600",n.style.color="#111827",n.textContent=t.main_text||t.description||"";const o=document.createElement("div");o.style.fontSize="12px",o.style.color="#6b7280",o.textContent=t.secondary_text||"",i.appendChild(n),o.textContent&&i.appendChild(o),i.addEventListener("mousedown",e=>{e.preventDefault(),this._selectSuggestion(t)}),e.appendChild(i)}),e.style.display="block"}async _selectSuggestion(e){try{const t=e.place_id||e.id;if(!t)return;const s=await this.client.placeDetails({place_id:t,id:t,session_token:this.client.sessionToken}),i=s?.address||null;i?.formatted_address?this._elements.input.value=i.formatted_address:e.description&&(this._elements.input.value=e.description),this._selected=i||null,this._elements.dd.style.display="none",this._elements.dd.innerHTML="",await this._renderPreview(this._selected)}catch(t){}}async _renderPreview(e){if(this._elements.previewHost.innerHTML="",!e){const e=document.createElement("div");return e.style.border="1px dashed #e5e7eb",e.style.borderRadius="8px",e.style.padding="12px",e.style.color="#6b7280",e.textContent="No location selected",void this._elements.previewHost.appendChild(e)}try{this._previewView=new LocationDetailsView({details:e,height:this.height,tileLayer:this.tileLayer}),await this._previewView.render(!0,this._elements.previewHost)}catch{const t=document.createElement("div");t.style.border="1px dashed #e5e7eb",t.style.borderRadius="8px",t.style.padding="12px",t.textContent=e?.formatted_address||"Selected location",this._elements.previewHost.appendChild(t)}}async onBeforeDestroy(){clearTimeout(this._timer),await super.onBeforeDestroy()}}class LocationFormPlugin{constructor({basePath:e="/api",mapping:t,registerFieldType:s=!0,fieldTypeName:i="address",attributeSelector:n="data-location",minChars:o=3,debounceMs:a=200,suppressBrowserAutocomplete:r=!0,autocompleteValue:l="new-password"}={}){this.id="location",this.client=new LocationClient({basePath:e}),this.mapping=t||{address1:"address1",city:"city",state_code:"state",postal_code:"postal_code",country_code:"country",latitude:"latitude",longitude:"longitude",formatted_address:"formatted_address",place_id:"place_id"},this.fieldTypeName=i,this.attributeSelector=n,this.minChars=o,this.debounceMs=a,this.suppressBrowserAutocomplete=!1!==r,this.autocompleteValue=l||"new-password",s&&(this.fieldTypes={[this.fieldTypeName]:(e,t)=>this.renderAddressField(e,t)})}renderAddressField(e,t){const s=this.suppressBrowserAutocomplete?`autocomplete="${this.autocompleteValue}" autocapitalize="off" autocorrect="off" spellcheck="false" inputmode="search"`:"",i={...t,type:"text",placeholder:t.placeholder||"Start typing an address",attrs:this.mergeAttrs(t.attrs,`${this.attributeSelector}="address" ${s} aria-autocomplete="list" role="combobox"`)};if("function"==typeof e.renderTextField)return e.renderTextField(i);if("function"==typeof e.renderInputField)return e.renderInputField(i,"text");const n=e.getFieldId?.(i.name)||`field_${i.name}`;return`\n <div class="mojo-form-control">\n ${i.label?`<label for="${n}" class="${e.options?.labelClass||"form-label"}">${i.label}</label>`:""}\n <input type="text" id="${n}" name="${i.name}" class="${e.options?.inputClass||"form-control"}"\n placeholder="${i.placeholder||""}" ${this.attributeSelector}="address" ${s} />\n </div>\n `}mergeAttrs(e,t){const s=(e||"").trim(),i=(t||"").trim();return s?i?`${s} ${i}`:s:i}onFormViewInit(e){}onAfterRender(e){if(e?.element)try{const t=`input[${this.attributeSelector}="address"]`;e.element.querySelectorAll(t).forEach(t=>{if(t.dataset&&"1"===t.dataset._locationBound)return;if(this.suppressBrowserAutocomplete)try{t.setAttribute("autocomplete",this.autocompleteValue),t.setAttribute("autocapitalize","off"),t.setAttribute("autocorrect","off"),t.setAttribute("spellcheck","false"),t.setAttribute("inputmode","search")}catch(o){}let s;const i=()=>{s=n(e,{client:this.client,field:t,mapping:this.mapping,minChars:this.minChars,debounceMs:this.debounceMs,onSelect:e=>{try{t.blur()}catch(o){}try{s&&s()}catch(o){}setTimeout(()=>{i()},this.debounceMs+50)}}),t.dataset._locationBound="1",this._trackDisposer(e,s)};i()})}catch(t){}}onFieldInit(e,t,s){try{const o=s?.type===this.fieldTypeName,a="address"===t?.getAttribute?.(this.attributeSelector);if(o||a){if(t.dataset&&"1"===t.dataset._locationBound)return;if(this.suppressBrowserAutocomplete)try{t.setAttribute("autocomplete",this.autocompleteValue),t.setAttribute("autocapitalize","off"),t.setAttribute("autocorrect","off"),t.setAttribute("spellcheck","false"),t.setAttribute("inputmode","search")}catch(i){}let s;const o=()=>{s=n(e,{client:this.client,field:t,mapping:this.mapping,minChars:this.minChars,debounceMs:this.debounceMs,onSelect:e=>{try{t.blur()}catch(i){}try{s&&s()}catch(i){}setTimeout(()=>{o()},this.debounceMs+50)}}),t.dataset._locationBound="1",this._trackDisposer(e,s)};o()}}catch(o){}}onFieldChange(e,t,s){}_trackDisposer(e,t){if(!t||"function"!=typeof t)return;if(!e)return;e._locationDisposers||Object.defineProperty(e,"_locationDisposers",{configurable:!0,enumerable:!1,writable:!0,value:[]}),e._locationDisposers.push(t);const s=t=>{if("function"==typeof e.on)try{return e.on(t,()=>{try{e._locationDisposers?.forEach(e=>{try{e()}catch{}})}finally{e._locationDisposers=[]}}),!0}catch{}return!1};s("before:destroy")||s("destroy")}}exports.MapLibreView=e.MapLibreView,exports.MapView=e.MapView,exports.MetricsCountryMapView=e.MetricsCountryMapView,exports.Collection=t.Collection,exports.Model=t.Model,exports.View=t.View,exports.LocationClient=LocationClient,exports.LocationDetailsView=LocationDetailsView,exports.LocationFormPlugin=LocationFormPlugin,exports.registerLocationPlugin=function(e={}){const t=new LocationFormPlugin(e);return i.FormPlugins.register(t)},exports.showLocationDetailsDialog=async function({client:e=new LocationClient({basePath:"/api"}),details:t=null,place_id:i=null,id:n=null,title:o="Location Details",height:a=260,tileLayer:r="osm"}={}){let l=t;if(!l&&(i||n))try{const t=await e.placeDetails({place_id:i,id:n,session_token:e.sessionToken});l=t?.address||null}catch(c){l={formatted_address:"Unable to load place details",error:c?.message||"Unknown error"}}const d=new LocationDetailsView({details:l||{},height:a,tileLayer:r});return s.Dialog.showDialog({title:o,body:d,size:"md",buttons:[{text:"Close",class:"btn-secondary",dismiss:!0,value:"close"}]})},exports.showLocationPickerDialog=async function({client:e=new LocationClient({basePath:"/api"}),title:t="Pick a Location",minChars:i=3,debounceMs:n=200,placeholder:o="Search address",confirmText:a="Select",height:r=240,tileLayer:l="osm"}={}){const d=new LocationPickerView({client:e,minChars:i,debounceMs:n,placeholder:o,height:r,tileLayer:l});return"ok"===await s.Dialog.showDialog({title:t,body:d,size:"md",buttons:[{text:"Cancel",class:"btn-secondary",dismiss:!0,value:"cancel"},{text:a,class:"btn-primary",value:"ok"}]})&&d.getSelected()||null},exports.useLocationAutocomplete=n;
2
2
  //# sourceMappingURL=map.cjs.js.map
package/dist/map.es.js CHANGED
@@ -1,2 +1,2 @@
1
- import{M as e}from"./chunks/MetricsCountryMapView-BgLr-rjI.js";import{a as t,b as s}from"./chunks/MetricsCountryMapView-BgLr-rjI.js";import{r as i,V as n}from"./chunks/Collection-DXcf6bOu.js";import{C as o,M as a}from"./chunks/Collection-DXcf6bOu.js";import{D as r}from"./chunks/Dialog-BO06wfM8.js";import{F as l}from"./chunks/FormPlugins-DvQ-G5J5.js";class LocationClient{constructor({basePath:e="/api",endpoints:t={}}={}){this.basePath=String(e||""),this.sessionToken=null,this.endpoints={validate:"/location/address/validate",autocomplete:"/location/address/suggestions",details:"/location/address/place-details",geocode:"/location/address/geocode",reverse:"/location/address/reverse-geocode",timezone:"/location/timezone",...t}}setAuthHeader(e){this._authHeader=e}headers(e){let t=null;if("function"==typeof this._authHeader)try{t=this._authHeader()}catch{t=null}else t=this._authHeader;const s={"Content-Type":"application/json",...e||{}};return t&&(s.Authorization=t),s}async jsonGet(e,t){const s=await i.GET(this.fullPath(e),t||{});return s&&void 0!==s.data?s.data:s}async jsonPost(e,t){const s=await i.POST(this.fullPath(e),t??{},{},{});return s&&void 0!==s.data?s.data:s}fullPath(e){return`${this.basePath}${e}`}async _safeJson(e){try{return await e.json()}catch{return null}}validateAddress(e){return this.jsonPost(this.endpoints.validate,e)}async autocomplete(e,t={}){if(!e||0===String(e).trim().length)return{success:!0,data:[],size:0,count:0};this.sessionToken||(this.sessionToken=this._createSessionToken());const s={input:e,session_token:this.sessionToken,...t},i=await this.jsonGet(this.endpoints.autocomplete,s);return i&&i.session_token&&(this.sessionToken=i.session_token),i}placeDetails({place_id:e,session_token:t,id:s}={}){const i={},n=e||s||null;return n&&(i.place_id=n),t&&(i.session_token=t),this.jsonGet(this.endpoints.details,i)}geocode(e){return this.jsonPost(this.endpoints.geocode,{address:e})}reverseGeocode({lat:e,lng:t}){return this.jsonGet(this.endpoints.reverse,{lat:e,lng:t})}timezone({lat:e,lng:t}){return this.jsonGet(this.endpoints.timezone,{lat:e,lng:t})}resetSessionToken(){this.sessionToken=null}normalizeSuggestion(e){return{id:e?.id||e?.place_id||null,place_id:e?.place_id||e?.id||null,description:e?.description||"",main_text:e?.main_text||"",secondary_text:e?.secondary_text||"",types:e?.types||[]}}_createSessionToken(){return"undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}}function d(e,{client:t,field:s="address1",dropdownClass:i="loc-suggest",minChars:n=3,debounceMs:o=200,mapping:a={address1:"address1",city:"city",state_code:"state",postal_code:"postal_code",country_code:"country",latitude:"latitude",longitude:"longitude",formatted_address:"formatted_address",place_id:"place_id"},onSelect:r}={}){if(!e||!e.element)return console.warn("[useLocationAutocomplete] Missing formView or formView.element"),()=>{};if(!t||"function"!=typeof t.autocomplete||"function"!=typeof t.placeDetails)return console.warn("[useLocationAutocomplete] Missing or invalid client. Provide an object with autocomplete() and placeDetails()."),()=>{};const l="string"==typeof s?e.element.querySelector(`input[name="${s}"], #${s}`)||null:s instanceof HTMLElement?s:null;if(!l)return()=>{};const d=document.createElement("div");d.className=i||"loc-suggest",d.style.position="absolute",d.style.zIndex="10000",d.style.display="none",d.style.background="#fff",d.style.border="1px solid #e5e7eb",d.style.borderRadius="8px",d.style.boxShadow="0 8px 24px rgba(0,0,0,.08)",d.style.padding="4px 0",d.style.maxHeight="280px",d.style.overflowY="auto",d.style.minWidth="240px",d.setAttribute("role","listbox"),d.setAttribute("aria-label","Address suggestions");let c=!1,u=null,p=!1;function h(){if(!c)return;const e=l.getBoundingClientRect();d.style.minWidth=`${e.width}px`,d.style.left=`${e.left+window.scrollX}px`,d.style.top=`${e.bottom+window.scrollY+4}px`}function m(){c=!1,d.style.display="none",d.innerHTML="",d.parentNode&&d.parentNode.removeChild(d)}async function y(){const s=l.value.trim();if(s.length<n)m();else try{const i=await t.autocomplete(s),n=(Array.isArray(i?.data)?i.data:[]).map(e=>({id:e.id,place_id:e.place_id,description:e.description,main_text:e.main_text,secondary_text:e.secondary_text,types:e.types}));c||(c=!0,d.style.display="block",document.body.appendChild(d),h()),h(),await async function(s){if(d.innerHTML="",!s||0===s.length){const e=document.createElement("div");return e.style.padding="8px 12px",e.style.color="#6b7280",e.textContent="No results",void d.appendChild(e)}s.forEach((s,i)=>d.appendChild(function(s,i){const n=document.createElement("div");n.setAttribute("role","option"),n.setAttribute("tabindex","-1"),n.style.padding="8px 12px",n.style.cursor="pointer",n.style.display="flex",n.style.flexDirection="column",n.dataset.index=String(i);const d=document.createElement("div");d.style.fontWeight="600",d.style.color="#111827",d.textContent=s.main_text||s.description||"";const c=document.createElement("div");return c.style.fontSize="12px",c.style.color="#6b7280",c.textContent=s.secondary_text||"",n.appendChild(d),c.textContent&&n.appendChild(c),n.addEventListener("mouseenter",()=>{n.style.background="#f3f4f6"}),n.addEventListener("mouseleave",()=>{n.style.background="transparent"}),n.addEventListener("mousedown",i=>{i.preventDefault(),async function(s){try{const i=s.place_id||s.id;let n=null;if(i){const e=await t.placeDetails({place_id:i,id:i,session_token:t.sessionToken});n=e?.address||null}p=!0,clearTimeout(u),n?.formatted_address?(l.value=n.formatted_address,l.dispatchEvent(new Event("input",{bubbles:!0}))):s.description&&(l.value=s.description,l.dispatchEvent(new Event("input",{bubbles:!0})));try{l.blur()}catch{}m(),setTimeout(()=>{p=!1},o+50),n&&a&&"object"==typeof a&&Object.entries(a).forEach(([t,s])=>{if(!s)return;const i=n[t];if(null!=i){try{"function"==typeof e.setFieldValue&&e.setFieldValue(s,String(i))}catch(o){}const t=e.element.querySelector(`input[name="${s}"], #${s}, textarea[name="${s}"], select[name="${s}"]`);t&&(t.value=String(i),t.dispatchEvent(new Event("input",{bubbles:!0})),t.dispatchEvent(new Event("change",{bubbles:!0})))}}),"function"==typeof r&&r(n||null)}catch(i){console.warn("[useLocationAutocomplete] placeDetails error:",i)}finally{m()}}(s)}),n}(s,i)))}(n)}catch(i){console.warn("[useLocationAutocomplete] autocomplete error:",i),m()}}function f(){p||(clearTimeout(u),u=setTimeout(y,o))}function _(){p||l.value.trim().length>=n&&f()}function b(){setTimeout(()=>{d.contains(document.activeElement)||m()},120)}function g(){c&&h()}function v(e){d.contains(e.target)||e.target===l||m()}return l.addEventListener("input",f),l.addEventListener("focus",_),l.addEventListener("blur",b),window.addEventListener("resize",g),window.addEventListener("scroll",g,!0),document.addEventListener("click",v),function(){clearTimeout(u);try{l.removeEventListener("input",f)}catch(e){}try{l.removeEventListener("focus",_)}catch(e){}try{l.removeEventListener("blur",b)}catch(e){}try{window.removeEventListener("resize",g)}catch(e){}try{window.removeEventListener("scroll",g,!0)}catch(e){}try{document.removeEventListener("click",v)}catch(e){}try{m()}catch(e){}}}class LocationDetailsView extends n{constructor({details:e={},height:t=260,tileLayer:s="osm"}={}){super({className:"location-details-view"}),this.details=e||{},this.height=Number.isFinite(t)?t:260,this.tileLayer=s||"osm",this.formatted_address=this.details.formatted_address||"",this.place_id=this.details.place_id||"",this.latitude=this._toNumber(this.details.latitude??this.details.lat??null),this.longitude=this._toNumber(this.details.longitude??this.details.lng??null),this.hasCoords=Number.isFinite(this.latitude)&&Number.isFinite(this.longitude),this._mapView=null,this.template='\n <div class="loc-details">\n {{#formatted_address}}\n <div class="mb-2 fw-semibold" style="word-break: break-word;">\n {{formatted_address}}\n </div>\n {{/formatted_address}}\n\n {{#place_id}}\n <div class="text-muted small mb-2">Place ID: {{place_id}}</div>\n {{/place_id}}\n\n {{^formatted_address}}\n <div class="text-muted small mb-2">No formatted address provided</div>\n {{/formatted_address}}\n\n {{#hasCoords}}\n <div data-container="map"></div>\n {{/hasCoords}}\n\n {{^hasCoords}}\n <div class="text-muted small" style="border: 1px dashed #e5e7eb; border-radius: 8px; padding: 12px;">\n No coordinates available for map preview\n </div>\n {{/hasCoords}}\n </div>\n '}async onInit(){if(this.hasCoords){const t=[{lat:this.latitude,lng:this.longitude,popup:this.formatted_address||""}];this._mapView=new e({markers:t,center:[this.latitude,this.longitude],zoom:13,height:this.height,tileLayer:this.tileLayer,showLayerControl:!0,containerId:"map"}),this.addChild(this._mapView)}}_toNumber(e){if(null==e||""===e)return null;const t=Number(e);return Number.isFinite(t)?t:null}}async function c({client:e=new LocationClient({basePath:"/api"}),details:t=null,place_id:s=null,id:i=null,title:n="Location Details",height:o=260,tileLayer:a="osm"}={}){let l=t;if(!l&&(s||i))try{const t=await e.placeDetails({place_id:s,id:i,session_token:e.sessionToken});l=t?.address||null}catch(c){l={formatted_address:"Unable to load place details",error:c?.message||"Unknown error"}}const d=new LocationDetailsView({details:l||{},height:o,tileLayer:a});return r.showDialog({title:n,body:d,size:"md",buttons:[{text:"Close",class:"btn-secondary",dismiss:!0,value:"close"}]})}class LocationPickerView extends n{constructor({client:e,minChars:t=3,debounceMs:s=200,placeholder:i="Search address",height:n=240,tileLayer:o="osm"}={}){super({className:"location-picker-view",template:'<div class="location-picker-view-root"></div>'}),this.client=e,this.minChars=t,this.debounceMs=s,this.placeholder=i,this.height=n,this.tileLayer=o,this._selected=null,this._timer=null,this._suggestions=[],this._elements={},this._previewView=null}getSelected(){return this._selected}async onAfterRender(){const e=document.createElement("div");e.style.display="grid",e.style.gap="10px";const t=document.createElement("input");t.type="text",t.className="form-control",t.placeholder=this.placeholder||"Search address",t.setAttribute("aria-label","Address search"),e.appendChild(t);const s=document.createElement("div");s.style.border="1px solid #e5e7eb",s.style.borderRadius="8px",s.style.background="#fff",s.style.boxShadow="0 8px 24px rgba(0,0,0,.08)",s.style.maxHeight="260px",s.style.overflowY="auto",s.style.display="none",e.appendChild(s);const i=document.createElement("div");e.appendChild(i),this.element.appendChild(e),this._elements={root:e,input:t,dd:s,previewHost:i},t.addEventListener("input",()=>{clearTimeout(this._timer),this._timer=setTimeout(()=>this._handleInput(),this.debounceMs)}),t.addEventListener("focus",()=>{(t.value||"").trim().length>=this.minChars&&this._handleInput()}),await this._renderPreview(null)}async _handleInput(){const e=(this._elements.input.value||"").trim();if(e.length<this.minChars)return this._elements.dd.style.display="none",void(this._elements.dd.innerHTML="");try{const t=await this.client.autocomplete(e),s=Array.isArray(t?.data)?t.data:[];this._suggestions=s.map(e=>({id:e.id,place_id:e.place_id,description:e.description,main_text:e.main_text,secondary_text:e.secondary_text,types:e.types})),await this._renderSuggestions()}catch(t){this._suggestions=[],await this._renderSuggestions()}}async _renderSuggestions(){const e=this._elements.dd;if(e.innerHTML="",!this._suggestions.length){const t=document.createElement("div");return t.style.padding="8px 12px",t.style.color="#6b7280",t.textContent="No results",e.appendChild(t),void(e.style.display="block")}this._suggestions.forEach((t,s)=>{const i=document.createElement("div");i.style.padding="8px 12px",i.style.cursor="pointer",i.style.display="flex",i.style.flexDirection="column",i.addEventListener("mouseenter",()=>{i.style.background="#f3f4f6"}),i.addEventListener("mouseleave",()=>{i.style.background="transparent"});const n=document.createElement("div");n.style.fontWeight="600",n.style.color="#111827",n.textContent=t.main_text||t.description||"";const o=document.createElement("div");o.style.fontSize="12px",o.style.color="#6b7280",o.textContent=t.secondary_text||"",i.appendChild(n),o.textContent&&i.appendChild(o),i.addEventListener("mousedown",e=>{e.preventDefault(),this._selectSuggestion(t)}),e.appendChild(i)}),e.style.display="block"}async _selectSuggestion(e){try{const t=e.place_id||e.id;if(!t)return;const s=await this.client.placeDetails({place_id:t,id:t,session_token:this.client.sessionToken}),i=s?.address||null;i?.formatted_address?this._elements.input.value=i.formatted_address:e.description&&(this._elements.input.value=e.description),this._selected=i||null,this._elements.dd.style.display="none",this._elements.dd.innerHTML="",await this._renderPreview(this._selected)}catch(t){}}async _renderPreview(e){if(this._elements.previewHost.innerHTML="",!e){const e=document.createElement("div");return e.style.border="1px dashed #e5e7eb",e.style.borderRadius="8px",e.style.padding="12px",e.style.color="#6b7280",e.textContent="No location selected",void this._elements.previewHost.appendChild(e)}try{this._previewView=new LocationDetailsView({details:e,height:this.height,tileLayer:this.tileLayer}),await this._previewView.render(!0,this._elements.previewHost)}catch{const t=document.createElement("div");t.style.border="1px dashed #e5e7eb",t.style.borderRadius="8px",t.style.padding="12px",t.textContent=e?.formatted_address||"Selected location",this._elements.previewHost.appendChild(t)}}async onBeforeDestroy(){clearTimeout(this._timer),await super.onBeforeDestroy()}}async function u({client:e=new LocationClient({basePath:"/api"}),title:t="Pick a Location",minChars:s=3,debounceMs:i=200,placeholder:n="Search address",confirmText:o="Select",height:a=240,tileLayer:l="osm"}={}){const d=new LocationPickerView({client:e,minChars:s,debounceMs:i,placeholder:n,height:a,tileLayer:l});return"ok"===await r.showDialog({title:t,body:d,size:"md",buttons:[{text:"Cancel",class:"btn-secondary",dismiss:!0,value:"cancel"},{text:o,class:"btn-primary",value:"ok"}]})&&d.getSelected()||null}class LocationFormPlugin{constructor({basePath:e="/api",mapping:t,registerFieldType:s=!0,fieldTypeName:i="address",attributeSelector:n="data-location",minChars:o=3,debounceMs:a=200,suppressBrowserAutocomplete:r=!0,autocompleteValue:l="new-password"}={}){this.id="location",this.client=new LocationClient({basePath:e}),this.mapping=t||{address1:"address1",city:"city",state_code:"state",postal_code:"postal_code",country_code:"country",latitude:"latitude",longitude:"longitude",formatted_address:"formatted_address",place_id:"place_id"},this.fieldTypeName=i,this.attributeSelector=n,this.minChars=o,this.debounceMs=a,this.suppressBrowserAutocomplete=!1!==r,this.autocompleteValue=l||"new-password",s&&(this.fieldTypes={[this.fieldTypeName]:(e,t)=>this.renderAddressField(e,t)})}renderAddressField(e,t){const s=this.suppressBrowserAutocomplete?`autocomplete="${this.autocompleteValue}" autocapitalize="off" autocorrect="off" spellcheck="false" inputmode="search"`:"",i={...t,type:"text",placeholder:t.placeholder||"Start typing an address",attrs:this.mergeAttrs(t.attrs,`${this.attributeSelector}="address" ${s} aria-autocomplete="list" role="combobox"`)};if("function"==typeof e.renderTextField)return e.renderTextField(i);if("function"==typeof e.renderInputField)return e.renderInputField(i,"text");const n=e.getFieldId?.(i.name)||`field_${i.name}`;return`\n <div class="mojo-form-control">\n ${i.label?`<label for="${n}" class="${e.options?.labelClass||"form-label"}">${i.label}</label>`:""}\n <input type="text" id="${n}" name="${i.name}" class="${e.options?.inputClass||"form-control"}"\n placeholder="${i.placeholder||""}" ${this.attributeSelector}="address" ${s} />\n </div>\n `}mergeAttrs(e,t){const s=(e||"").trim(),i=(t||"").trim();return s?i?`${s} ${i}`:s:i}onFormViewInit(e){}onAfterRender(e){if(e?.element)try{const t=`input[${this.attributeSelector}="address"]`;e.element.querySelectorAll(t).forEach(t=>{if(t.dataset&&"1"===t.dataset._locationBound)return;if(this.suppressBrowserAutocomplete)try{t.setAttribute("autocomplete",this.autocompleteValue),t.setAttribute("autocapitalize","off"),t.setAttribute("autocorrect","off"),t.setAttribute("spellcheck","false"),t.setAttribute("inputmode","search")}catch(n){}let s;const i=()=>{s=d(e,{client:this.client,field:t,mapping:this.mapping,minChars:this.minChars,debounceMs:this.debounceMs,onSelect:e=>{try{t.blur()}catch(n){}try{s&&s()}catch(n){}setTimeout(()=>{i()},this.debounceMs+50)}}),t.dataset._locationBound="1",this._trackDisposer(e,s)};i()})}catch(t){}}onFieldInit(e,t,s){try{const n=s?.type===this.fieldTypeName,o="address"===t?.getAttribute?.(this.attributeSelector);if(n||o){if(t.dataset&&"1"===t.dataset._locationBound)return;if(this.suppressBrowserAutocomplete)try{t.setAttribute("autocomplete",this.autocompleteValue),t.setAttribute("autocapitalize","off"),t.setAttribute("autocorrect","off"),t.setAttribute("spellcheck","false"),t.setAttribute("inputmode","search")}catch(i){}let s;const n=()=>{s=d(e,{client:this.client,field:t,mapping:this.mapping,minChars:this.minChars,debounceMs:this.debounceMs,onSelect:e=>{try{t.blur()}catch(i){}try{s&&s()}catch(i){}setTimeout(()=>{n()},this.debounceMs+50)}}),t.dataset._locationBound="1",this._trackDisposer(e,s)};n()}}catch(n){}}onFieldChange(e,t,s){}_trackDisposer(e,t){if(!t||"function"!=typeof t)return;if(!e)return;e._locationDisposers||Object.defineProperty(e,"_locationDisposers",{configurable:!0,enumerable:!1,writable:!0,value:[]}),e._locationDisposers.push(t);const s=t=>{if("function"==typeof e.on)try{return e.on(t,()=>{try{e._locationDisposers?.forEach(e=>{try{e()}catch{}})}finally{e._locationDisposers=[]}}),!0}catch{}return!1};s("before:destroy")||s("destroy")}}function p(e={}){const t=new LocationFormPlugin(e);return l.register(t)}export{o as Collection,LocationClient,LocationDetailsView,LocationFormPlugin,t as MapLibreView,e as MapView,s as MetricsCountryMapView,a as Model,n as View,p as registerLocationPlugin,c as showLocationDetailsDialog,u as showLocationPickerDialog,d as useLocationAutocomplete};
1
+ import{M as e}from"./chunks/MetricsCountryMapView-jLWCL6Hm.js";import{a as t,b as s}from"./chunks/MetricsCountryMapView-jLWCL6Hm.js";import{r as i,V as n}from"./chunks/Collection-C39Oy2q0.js";import{C as o,M as a}from"./chunks/Collection-C39Oy2q0.js";import{D as r}from"./chunks/Dialog-B0r9puaZ.js";import{F as l}from"./chunks/FormPlugins-DvQ-G5J5.js";class LocationClient{constructor({basePath:e="/api",endpoints:t={}}={}){this.basePath=String(e||""),this.sessionToken=null,this.endpoints={validate:"/location/address/validate",autocomplete:"/location/address/suggestions",details:"/location/address/place-details",geocode:"/location/address/geocode",reverse:"/location/address/reverse-geocode",timezone:"/location/timezone",...t}}setAuthHeader(e){this._authHeader=e}headers(e){let t=null;if("function"==typeof this._authHeader)try{t=this._authHeader()}catch{t=null}else t=this._authHeader;const s={"Content-Type":"application/json",...e||{}};return t&&(s.Authorization=t),s}async jsonGet(e,t){const s=await i.GET(this.fullPath(e),t||{});return s&&void 0!==s.data?s.data:s}async jsonPost(e,t){const s=await i.POST(this.fullPath(e),t??{},{},{});return s&&void 0!==s.data?s.data:s}fullPath(e){return`${this.basePath}${e}`}async _safeJson(e){try{return await e.json()}catch{return null}}validateAddress(e){return this.jsonPost(this.endpoints.validate,e)}async autocomplete(e,t={}){if(!e||0===String(e).trim().length)return{success:!0,data:[],size:0,count:0};this.sessionToken||(this.sessionToken=this._createSessionToken());const s={input:e,session_token:this.sessionToken,...t},i=await this.jsonGet(this.endpoints.autocomplete,s);return i&&i.session_token&&(this.sessionToken=i.session_token),i}placeDetails({place_id:e,session_token:t,id:s}={}){const i={},n=e||s||null;return n&&(i.place_id=n),t&&(i.session_token=t),this.jsonGet(this.endpoints.details,i)}geocode(e){return this.jsonPost(this.endpoints.geocode,{address:e})}reverseGeocode({lat:e,lng:t}){return this.jsonGet(this.endpoints.reverse,{lat:e,lng:t})}timezone({lat:e,lng:t}){return this.jsonGet(this.endpoints.timezone,{lat:e,lng:t})}resetSessionToken(){this.sessionToken=null}normalizeSuggestion(e){return{id:e?.id||e?.place_id||null,place_id:e?.place_id||e?.id||null,description:e?.description||"",main_text:e?.main_text||"",secondary_text:e?.secondary_text||"",types:e?.types||[]}}_createSessionToken(){return"undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}}function d(e,{client:t,field:s="address1",dropdownClass:i="loc-suggest",minChars:n=3,debounceMs:o=200,mapping:a={address1:"address1",city:"city",state_code:"state",postal_code:"postal_code",country_code:"country",latitude:"latitude",longitude:"longitude",formatted_address:"formatted_address",place_id:"place_id"},onSelect:r}={}){if(!e||!e.element)return console.warn("[useLocationAutocomplete] Missing formView or formView.element"),()=>{};if(!t||"function"!=typeof t.autocomplete||"function"!=typeof t.placeDetails)return console.warn("[useLocationAutocomplete] Missing or invalid client. Provide an object with autocomplete() and placeDetails()."),()=>{};const l="string"==typeof s?e.element.querySelector(`input[name="${s}"], #${s}`)||null:s instanceof HTMLElement?s:null;if(!l)return()=>{};const d=document.createElement("div");d.className=i||"loc-suggest",d.style.position="absolute",d.style.zIndex="10000",d.style.display="none",d.style.background="#fff",d.style.border="1px solid #e5e7eb",d.style.borderRadius="8px",d.style.boxShadow="0 8px 24px rgba(0,0,0,.08)",d.style.padding="4px 0",d.style.maxHeight="280px",d.style.overflowY="auto",d.style.minWidth="240px",d.setAttribute("role","listbox"),d.setAttribute("aria-label","Address suggestions");let c=!1,u=null,p=!1;function h(){if(!c)return;const e=l.getBoundingClientRect();d.style.minWidth=`${e.width}px`,d.style.left=`${e.left+window.scrollX}px`,d.style.top=`${e.bottom+window.scrollY+4}px`}function m(){c=!1,d.style.display="none",d.innerHTML="",d.parentNode&&d.parentNode.removeChild(d)}async function y(){const s=l.value.trim();if(s.length<n)m();else try{const i=await t.autocomplete(s),n=(Array.isArray(i?.data)?i.data:[]).map(e=>({id:e.id,place_id:e.place_id,description:e.description,main_text:e.main_text,secondary_text:e.secondary_text,types:e.types}));c||(c=!0,d.style.display="block",document.body.appendChild(d),h()),h(),await async function(s){if(d.innerHTML="",!s||0===s.length){const e=document.createElement("div");return e.style.padding="8px 12px",e.style.color="#6b7280",e.textContent="No results",void d.appendChild(e)}s.forEach((s,i)=>d.appendChild(function(s,i){const n=document.createElement("div");n.setAttribute("role","option"),n.setAttribute("tabindex","-1"),n.style.padding="8px 12px",n.style.cursor="pointer",n.style.display="flex",n.style.flexDirection="column",n.dataset.index=String(i);const d=document.createElement("div");d.style.fontWeight="600",d.style.color="#111827",d.textContent=s.main_text||s.description||"";const c=document.createElement("div");return c.style.fontSize="12px",c.style.color="#6b7280",c.textContent=s.secondary_text||"",n.appendChild(d),c.textContent&&n.appendChild(c),n.addEventListener("mouseenter",()=>{n.style.background="#f3f4f6"}),n.addEventListener("mouseleave",()=>{n.style.background="transparent"}),n.addEventListener("mousedown",i=>{i.preventDefault(),async function(s){try{const i=s.place_id||s.id;let n=null;if(i){const e=await t.placeDetails({place_id:i,id:i,session_token:t.sessionToken});n=e?.address||null}p=!0,clearTimeout(u),n?.formatted_address?(l.value=n.formatted_address,l.dispatchEvent(new Event("input",{bubbles:!0}))):s.description&&(l.value=s.description,l.dispatchEvent(new Event("input",{bubbles:!0})));try{l.blur()}catch{}m(),setTimeout(()=>{p=!1},o+50),n&&a&&"object"==typeof a&&Object.entries(a).forEach(([t,s])=>{if(!s)return;const i=n[t];if(null!=i){try{"function"==typeof e.setFieldValue&&e.setFieldValue(s,String(i))}catch(o){}const t=e.element.querySelector(`input[name="${s}"], #${s}, textarea[name="${s}"], select[name="${s}"]`);t&&(t.value=String(i),t.dispatchEvent(new Event("input",{bubbles:!0})),t.dispatchEvent(new Event("change",{bubbles:!0})))}}),"function"==typeof r&&r(n||null)}catch(i){console.warn("[useLocationAutocomplete] placeDetails error:",i)}finally{m()}}(s)}),n}(s,i)))}(n)}catch(i){console.warn("[useLocationAutocomplete] autocomplete error:",i),m()}}function f(){p||(clearTimeout(u),u=setTimeout(y,o))}function _(){p||l.value.trim().length>=n&&f()}function b(){setTimeout(()=>{d.contains(document.activeElement)||m()},120)}function g(){c&&h()}function v(e){d.contains(e.target)||e.target===l||m()}return l.addEventListener("input",f),l.addEventListener("focus",_),l.addEventListener("blur",b),window.addEventListener("resize",g),window.addEventListener("scroll",g,!0),document.addEventListener("click",v),function(){clearTimeout(u);try{l.removeEventListener("input",f)}catch(e){}try{l.removeEventListener("focus",_)}catch(e){}try{l.removeEventListener("blur",b)}catch(e){}try{window.removeEventListener("resize",g)}catch(e){}try{window.removeEventListener("scroll",g,!0)}catch(e){}try{document.removeEventListener("click",v)}catch(e){}try{m()}catch(e){}}}class LocationDetailsView extends n{constructor({details:e={},height:t=260,tileLayer:s="osm"}={}){super({className:"location-details-view"}),this.details=e||{},this.height=Number.isFinite(t)?t:260,this.tileLayer=s||"osm",this.formatted_address=this.details.formatted_address||"",this.place_id=this.details.place_id||"",this.latitude=this._toNumber(this.details.latitude??this.details.lat??null),this.longitude=this._toNumber(this.details.longitude??this.details.lng??null),this.hasCoords=Number.isFinite(this.latitude)&&Number.isFinite(this.longitude),this._mapView=null,this.template='\n <div class="loc-details">\n {{#formatted_address}}\n <div class="mb-2 fw-semibold" style="word-break: break-word;">\n {{formatted_address}}\n </div>\n {{/formatted_address}}\n\n {{#place_id}}\n <div class="text-muted small mb-2">Place ID: {{place_id}}</div>\n {{/place_id}}\n\n {{^formatted_address}}\n <div class="text-muted small mb-2">No formatted address provided</div>\n {{/formatted_address}}\n\n {{#hasCoords}}\n <div data-container="map"></div>\n {{/hasCoords}}\n\n {{^hasCoords}}\n <div class="text-muted small" style="border: 1px dashed #e5e7eb; border-radius: 8px; padding: 12px;">\n No coordinates available for map preview\n </div>\n {{/hasCoords}}\n </div>\n '}async onInit(){if(this.hasCoords){const t=[{lat:this.latitude,lng:this.longitude,popup:this.formatted_address||""}];this._mapView=new e({markers:t,center:[this.latitude,this.longitude],zoom:13,height:this.height,tileLayer:this.tileLayer,showLayerControl:!0,containerId:"map"}),this.addChild(this._mapView)}}_toNumber(e){if(null==e||""===e)return null;const t=Number(e);return Number.isFinite(t)?t:null}}async function c({client:e=new LocationClient({basePath:"/api"}),details:t=null,place_id:s=null,id:i=null,title:n="Location Details",height:o=260,tileLayer:a="osm"}={}){let l=t;if(!l&&(s||i))try{const t=await e.placeDetails({place_id:s,id:i,session_token:e.sessionToken});l=t?.address||null}catch(c){l={formatted_address:"Unable to load place details",error:c?.message||"Unknown error"}}const d=new LocationDetailsView({details:l||{},height:o,tileLayer:a});return r.showDialog({title:n,body:d,size:"md",buttons:[{text:"Close",class:"btn-secondary",dismiss:!0,value:"close"}]})}class LocationPickerView extends n{constructor({client:e,minChars:t=3,debounceMs:s=200,placeholder:i="Search address",height:n=240,tileLayer:o="osm"}={}){super({className:"location-picker-view",template:'<div class="location-picker-view-root"></div>'}),this.client=e,this.minChars=t,this.debounceMs=s,this.placeholder=i,this.height=n,this.tileLayer=o,this._selected=null,this._timer=null,this._suggestions=[],this._elements={},this._previewView=null}getSelected(){return this._selected}async onAfterRender(){const e=document.createElement("div");e.style.display="grid",e.style.gap="10px";const t=document.createElement("input");t.type="text",t.className="form-control",t.placeholder=this.placeholder||"Search address",t.setAttribute("aria-label","Address search"),e.appendChild(t);const s=document.createElement("div");s.style.border="1px solid #e5e7eb",s.style.borderRadius="8px",s.style.background="#fff",s.style.boxShadow="0 8px 24px rgba(0,0,0,.08)",s.style.maxHeight="260px",s.style.overflowY="auto",s.style.display="none",e.appendChild(s);const i=document.createElement("div");e.appendChild(i),this.element.appendChild(e),this._elements={root:e,input:t,dd:s,previewHost:i},t.addEventListener("input",()=>{clearTimeout(this._timer),this._timer=setTimeout(()=>this._handleInput(),this.debounceMs)}),t.addEventListener("focus",()=>{(t.value||"").trim().length>=this.minChars&&this._handleInput()}),await this._renderPreview(null)}async _handleInput(){const e=(this._elements.input.value||"").trim();if(e.length<this.minChars)return this._elements.dd.style.display="none",void(this._elements.dd.innerHTML="");try{const t=await this.client.autocomplete(e),s=Array.isArray(t?.data)?t.data:[];this._suggestions=s.map(e=>({id:e.id,place_id:e.place_id,description:e.description,main_text:e.main_text,secondary_text:e.secondary_text,types:e.types})),await this._renderSuggestions()}catch(t){this._suggestions=[],await this._renderSuggestions()}}async _renderSuggestions(){const e=this._elements.dd;if(e.innerHTML="",!this._suggestions.length){const t=document.createElement("div");return t.style.padding="8px 12px",t.style.color="#6b7280",t.textContent="No results",e.appendChild(t),void(e.style.display="block")}this._suggestions.forEach((t,s)=>{const i=document.createElement("div");i.style.padding="8px 12px",i.style.cursor="pointer",i.style.display="flex",i.style.flexDirection="column",i.addEventListener("mouseenter",()=>{i.style.background="#f3f4f6"}),i.addEventListener("mouseleave",()=>{i.style.background="transparent"});const n=document.createElement("div");n.style.fontWeight="600",n.style.color="#111827",n.textContent=t.main_text||t.description||"";const o=document.createElement("div");o.style.fontSize="12px",o.style.color="#6b7280",o.textContent=t.secondary_text||"",i.appendChild(n),o.textContent&&i.appendChild(o),i.addEventListener("mousedown",e=>{e.preventDefault(),this._selectSuggestion(t)}),e.appendChild(i)}),e.style.display="block"}async _selectSuggestion(e){try{const t=e.place_id||e.id;if(!t)return;const s=await this.client.placeDetails({place_id:t,id:t,session_token:this.client.sessionToken}),i=s?.address||null;i?.formatted_address?this._elements.input.value=i.formatted_address:e.description&&(this._elements.input.value=e.description),this._selected=i||null,this._elements.dd.style.display="none",this._elements.dd.innerHTML="",await this._renderPreview(this._selected)}catch(t){}}async _renderPreview(e){if(this._elements.previewHost.innerHTML="",!e){const e=document.createElement("div");return e.style.border="1px dashed #e5e7eb",e.style.borderRadius="8px",e.style.padding="12px",e.style.color="#6b7280",e.textContent="No location selected",void this._elements.previewHost.appendChild(e)}try{this._previewView=new LocationDetailsView({details:e,height:this.height,tileLayer:this.tileLayer}),await this._previewView.render(!0,this._elements.previewHost)}catch{const t=document.createElement("div");t.style.border="1px dashed #e5e7eb",t.style.borderRadius="8px",t.style.padding="12px",t.textContent=e?.formatted_address||"Selected location",this._elements.previewHost.appendChild(t)}}async onBeforeDestroy(){clearTimeout(this._timer),await super.onBeforeDestroy()}}async function u({client:e=new LocationClient({basePath:"/api"}),title:t="Pick a Location",minChars:s=3,debounceMs:i=200,placeholder:n="Search address",confirmText:o="Select",height:a=240,tileLayer:l="osm"}={}){const d=new LocationPickerView({client:e,minChars:s,debounceMs:i,placeholder:n,height:a,tileLayer:l});return"ok"===await r.showDialog({title:t,body:d,size:"md",buttons:[{text:"Cancel",class:"btn-secondary",dismiss:!0,value:"cancel"},{text:o,class:"btn-primary",value:"ok"}]})&&d.getSelected()||null}class LocationFormPlugin{constructor({basePath:e="/api",mapping:t,registerFieldType:s=!0,fieldTypeName:i="address",attributeSelector:n="data-location",minChars:o=3,debounceMs:a=200,suppressBrowserAutocomplete:r=!0,autocompleteValue:l="new-password"}={}){this.id="location",this.client=new LocationClient({basePath:e}),this.mapping=t||{address1:"address1",city:"city",state_code:"state",postal_code:"postal_code",country_code:"country",latitude:"latitude",longitude:"longitude",formatted_address:"formatted_address",place_id:"place_id"},this.fieldTypeName=i,this.attributeSelector=n,this.minChars=o,this.debounceMs=a,this.suppressBrowserAutocomplete=!1!==r,this.autocompleteValue=l||"new-password",s&&(this.fieldTypes={[this.fieldTypeName]:(e,t)=>this.renderAddressField(e,t)})}renderAddressField(e,t){const s=this.suppressBrowserAutocomplete?`autocomplete="${this.autocompleteValue}" autocapitalize="off" autocorrect="off" spellcheck="false" inputmode="search"`:"",i={...t,type:"text",placeholder:t.placeholder||"Start typing an address",attrs:this.mergeAttrs(t.attrs,`${this.attributeSelector}="address" ${s} aria-autocomplete="list" role="combobox"`)};if("function"==typeof e.renderTextField)return e.renderTextField(i);if("function"==typeof e.renderInputField)return e.renderInputField(i,"text");const n=e.getFieldId?.(i.name)||`field_${i.name}`;return`\n <div class="mojo-form-control">\n ${i.label?`<label for="${n}" class="${e.options?.labelClass||"form-label"}">${i.label}</label>`:""}\n <input type="text" id="${n}" name="${i.name}" class="${e.options?.inputClass||"form-control"}"\n placeholder="${i.placeholder||""}" ${this.attributeSelector}="address" ${s} />\n </div>\n `}mergeAttrs(e,t){const s=(e||"").trim(),i=(t||"").trim();return s?i?`${s} ${i}`:s:i}onFormViewInit(e){}onAfterRender(e){if(e?.element)try{const t=`input[${this.attributeSelector}="address"]`;e.element.querySelectorAll(t).forEach(t=>{if(t.dataset&&"1"===t.dataset._locationBound)return;if(this.suppressBrowserAutocomplete)try{t.setAttribute("autocomplete",this.autocompleteValue),t.setAttribute("autocapitalize","off"),t.setAttribute("autocorrect","off"),t.setAttribute("spellcheck","false"),t.setAttribute("inputmode","search")}catch(n){}let s;const i=()=>{s=d(e,{client:this.client,field:t,mapping:this.mapping,minChars:this.minChars,debounceMs:this.debounceMs,onSelect:e=>{try{t.blur()}catch(n){}try{s&&s()}catch(n){}setTimeout(()=>{i()},this.debounceMs+50)}}),t.dataset._locationBound="1",this._trackDisposer(e,s)};i()})}catch(t){}}onFieldInit(e,t,s){try{const n=s?.type===this.fieldTypeName,o="address"===t?.getAttribute?.(this.attributeSelector);if(n||o){if(t.dataset&&"1"===t.dataset._locationBound)return;if(this.suppressBrowserAutocomplete)try{t.setAttribute("autocomplete",this.autocompleteValue),t.setAttribute("autocapitalize","off"),t.setAttribute("autocorrect","off"),t.setAttribute("spellcheck","false"),t.setAttribute("inputmode","search")}catch(i){}let s;const n=()=>{s=d(e,{client:this.client,field:t,mapping:this.mapping,minChars:this.minChars,debounceMs:this.debounceMs,onSelect:e=>{try{t.blur()}catch(i){}try{s&&s()}catch(i){}setTimeout(()=>{n()},this.debounceMs+50)}}),t.dataset._locationBound="1",this._trackDisposer(e,s)};n()}}catch(n){}}onFieldChange(e,t,s){}_trackDisposer(e,t){if(!t||"function"!=typeof t)return;if(!e)return;e._locationDisposers||Object.defineProperty(e,"_locationDisposers",{configurable:!0,enumerable:!1,writable:!0,value:[]}),e._locationDisposers.push(t);const s=t=>{if("function"==typeof e.on)try{return e.on(t,()=>{try{e._locationDisposers?.forEach(e=>{try{e()}catch{}})}finally{e._locationDisposers=[]}}),!0}catch{}return!1};s("before:destroy")||s("destroy")}}function p(e={}){const t=new LocationFormPlugin(e);return l.register(t)}export{o as Collection,LocationClient,LocationDetailsView,LocationFormPlugin,t as MapLibreView,e as MapView,s as MetricsCountryMapView,a as Model,n as View,p as registerLocationPlugin,c as showLocationDetailsDialog,u as showLocationPickerDialog,d as useLocationAutocomplete};
2
2
  //# sourceMappingURL=map.es.js.map
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("./chunks/ListView-DLyS4Nfd.js"),e=require("./chunks/Collection-DwIvN0yq.js");class TimelineViewItem extends t.ListViewItem{constructor(t={}){super({className:"timeline-item",...t}),this.dateFormat=t.dateFormat||"date",this.dotStyle=t.dotStyle||"solid",this.showDate=!1!==t.showDate,this.theme=t.theme||"primary",this.template||(this.template='\n <div class="timeline-marker timeline-marker-{{markerType}}">\n {{#hasIcon}}\n <i class="bi {{model.icon}} text-{{displayColor}}"></i>\n {{/hasIcon}}\n {{^hasIcon}}\n <div class="timeline-dot bg-{{displayColor}}"></div>\n {{/hasIcon}}\n </div>\n \n <div class="timeline-content">\n {{#showDate}}\n <div class="timeline-date text-muted small">\n {{formattedDate}}\n </div>\n {{/showDate}}\n \n <div class="timeline-card">\n {{#model.title}}\n <h6 class="timeline-title mb-1">{{model.title}}</h6>\n {{/model.title}}\n \n {{#model.description}}\n <p class="timeline-description mb-0">{{model.description}}</p>\n {{/model.description}}\n \n {{#model.meta}}\n <div class="timeline-meta mt-2 text-muted small">\n {{model.meta}}\n </div>\n {{/model.meta}}\n </div>\n </div>\n ')}async onInit(){await super.onInit(),this.processItemData()}processItemData(){this.displayColor=this.model?.get?.("color")||this.model?.color||this.theme;const t=!(!this.model?.get?.("icon")&&!this.model?.icon)&&"icon"===this.dotStyle;this.hasIcon=t,this.markerType=t?"icon":this.dotStyle;const e=this.model?.get?.("date")||this.model?.date;this.formattedDate=this.formatDate(e)}formatDate(t){if(!t)return"";switch(this.dateFormat){case"datetime":return e.dataFormatter.pipe(t,"datetime");case"relative":return e.dataFormatter.pipe(t,"timeago");default:return e.dataFormatter.pipe(t,"date")}}async onActionSelect(t,e){t.stopPropagation(),this.emit("item:click",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model}),this.listView&&this.listView.emit("item:click",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model})}}class TimelineView extends t.ListView{constructor(t={}){super({className:"timeline-view",itemClass:t.itemClass||TimelineViewItem,selectionMode:"none",emptyMessage:t.emptyMessage||"No timeline events to display",template:'\n <div class="timeline-container timeline-{{position}}">\n {{#loading}}\n <div class="timeline-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 <span class="ms-2 text-muted">Loading timeline...</span>\n </div>\n {{/loading}}\n {{^loading}}\n {{#isEmpty}}\n <div class="timeline-empty text-center text-muted py-4">\n <i class="bi bi-clock-history fs-1 d-block mb-2"></i>\n <p>{{emptyMessage}}</p>\n </div>\n {{/isEmpty}}\n {{^isEmpty}}\n <div class="timeline" data-container="items"></div>\n {{/isEmpty}}\n {{/loading}}\n </div>\n ',...t}),this.position=t.position||"left",this.dateFormat=t.dateFormat||"date",this.dotStyle=t.dotStyle||"solid",this.showDate=!1!==t.showDate,this.theme=t.theme||"primary",this.groupBy=t.groupBy||"none"}_createItemView(t,e){if(this.itemViews.has(t.id))return;const i=new this.itemClass({model:t,index:e,listView:this,template:this.itemTemplate,dateFormat:this.dateFormat,dotStyle:this.dotStyle,showDate:this.showDate,theme:this.theme});return this.itemViews.set(t.id,i),i.on("item:click",this._onItemClick.bind(this)),i}_onItemClick(t){this.emit("item:click",t)}setPosition(t){return"left"!==t&&"center"!==t?(console.warn('Invalid position. Use "left" or "center"'),this):(this.position=t,this.isMounted()&&this.render(),this)}setDateFormat(t){return this.dateFormat=t,this.forEachItem(e=>{e.dateFormat=t,e.processItemData(),e.isMounted()&&e.render()}),this}setDotStyle(t){return this.dotStyle=t,this.forEachItem(e=>{e.dotStyle=t,e.processItemData(),e.isMounted()&&e.render()}),this}toggleDates(t=null){return this.showDate=null!==t?t:!this.showDate,this.forEachItem(t=>{t.showDate=this.showDate,t.isMounted()&&t.render()}),this}}exports.ListView=t.ListView,exports.ListViewItem=t.ListViewItem,exports.Collection=e.Collection,exports.Model=e.Model,exports.View=e.View,exports.TimelineView=TimelineView,exports.TimelineViewItem=TimelineViewItem;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("./chunks/ListView-Jkke6pU1.js"),e=require("./chunks/Collection-CyK0u557.js");class TimelineViewItem extends t.ListViewItem{constructor(t={}){super({className:"timeline-item",...t}),this.dateFormat=t.dateFormat||"date",this.dotStyle=t.dotStyle||"solid",this.showDate=!1!==t.showDate,this.theme=t.theme||"primary",this.template||(this.template='\n <div class="timeline-marker timeline-marker-{{markerType}}">\n {{#hasIcon}}\n <i class="bi {{model.icon}} text-{{displayColor}}"></i>\n {{/hasIcon}}\n {{^hasIcon}}\n <div class="timeline-dot bg-{{displayColor}}"></div>\n {{/hasIcon}}\n </div>\n \n <div class="timeline-content">\n {{#showDate}}\n <div class="timeline-date text-muted small">\n {{formattedDate}}\n </div>\n {{/showDate}}\n \n <div class="timeline-card">\n {{#model.title}}\n <h6 class="timeline-title mb-1">{{model.title}}</h6>\n {{/model.title}}\n \n {{#model.description}}\n <p class="timeline-description mb-0">{{model.description}}</p>\n {{/model.description}}\n \n {{#model.meta}}\n <div class="timeline-meta mt-2 text-muted small">\n {{model.meta}}\n </div>\n {{/model.meta}}\n </div>\n </div>\n ')}async onInit(){await super.onInit(),this.processItemData()}processItemData(){this.displayColor=this.model?.get?.("color")||this.model?.color||this.theme;const t=!(!this.model?.get?.("icon")&&!this.model?.icon)&&"icon"===this.dotStyle;this.hasIcon=t,this.markerType=t?"icon":this.dotStyle;const e=this.model?.get?.("date")||this.model?.date;this.formattedDate=this.formatDate(e)}formatDate(t){if(!t)return"";switch(this.dateFormat){case"datetime":return e.dataFormatter.pipe(t,"datetime");case"relative":return e.dataFormatter.pipe(t,"timeago");default:return e.dataFormatter.pipe(t,"date")}}async onActionSelect(t,e){t.stopPropagation(),this.emit("item:click",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model}),this.listView&&this.listView.emit("item:click",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model})}}class TimelineView extends t.ListView{constructor(t={}){super({className:"timeline-view",itemClass:t.itemClass||TimelineViewItem,selectionMode:"none",emptyMessage:t.emptyMessage||"No timeline events to display",template:'\n <div class="timeline-container timeline-{{position}}">\n {{#loading}}\n <div class="timeline-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 <span class="ms-2 text-muted">Loading timeline...</span>\n </div>\n {{/loading}}\n {{^loading}}\n {{#isEmpty}}\n <div class="timeline-empty text-center text-muted py-4">\n <i class="bi bi-clock-history fs-1 d-block mb-2"></i>\n <p>{{emptyMessage}}</p>\n </div>\n {{/isEmpty}}\n {{^isEmpty}}\n <div class="timeline" data-container="items"></div>\n {{/isEmpty}}\n {{/loading}}\n </div>\n ',...t}),this.position=t.position||"left",this.dateFormat=t.dateFormat||"date",this.dotStyle=t.dotStyle||"solid",this.showDate=!1!==t.showDate,this.theme=t.theme||"primary",this.groupBy=t.groupBy||"none"}_createItemView(t,e){if(this.itemViews.has(t.id))return;const i=new this.itemClass({model:t,index:e,listView:this,template:this.itemTemplate,dateFormat:this.dateFormat,dotStyle:this.dotStyle,showDate:this.showDate,theme:this.theme});return this.itemViews.set(t.id,i),i.on("item:click",this._onItemClick.bind(this)),i}_onItemClick(t){this.emit("item:click",t)}setPosition(t){return"left"!==t&&"center"!==t?(console.warn('Invalid position. Use "left" or "center"'),this):(this.position=t,this.isMounted()&&this.render(),this)}setDateFormat(t){return this.dateFormat=t,this.forEachItem(e=>{e.dateFormat=t,e.processItemData(),e.isMounted()&&e.render()}),this}setDotStyle(t){return this.dotStyle=t,this.forEachItem(e=>{e.dotStyle=t,e.processItemData(),e.isMounted()&&e.render()}),this}toggleDates(t=null){return this.showDate=null!==t?t:!this.showDate,this.forEachItem(t=>{t.showDate=this.showDate,t.isMounted()&&t.render()}),this}}exports.ListView=t.ListView,exports.ListViewItem=t.ListViewItem,exports.Collection=e.Collection,exports.Model=e.Model,exports.View=e.View,exports.TimelineView=TimelineView,exports.TimelineViewItem=TimelineViewItem;
2
2
  //# sourceMappingURL=timeline.cjs.js.map
@@ -1,2 +1,2 @@
1
- import{L as t,a as e}from"./chunks/ListView-ZsMVvvUQ.js";import{d as i}from"./chunks/Collection-DXcf6bOu.js";import{C as s,M as n,V as o}from"./chunks/Collection-DXcf6bOu.js";class TimelineViewItem extends t{constructor(t={}){super({className:"timeline-item",...t}),this.dateFormat=t.dateFormat||"date",this.dotStyle=t.dotStyle||"solid",this.showDate=!1!==t.showDate,this.theme=t.theme||"primary",this.template||(this.template='\n <div class="timeline-marker timeline-marker-{{markerType}}">\n {{#hasIcon}}\n <i class="bi {{model.icon}} text-{{displayColor}}"></i>\n {{/hasIcon}}\n {{^hasIcon}}\n <div class="timeline-dot bg-{{displayColor}}"></div>\n {{/hasIcon}}\n </div>\n \n <div class="timeline-content">\n {{#showDate}}\n <div class="timeline-date text-muted small">\n {{formattedDate}}\n </div>\n {{/showDate}}\n \n <div class="timeline-card">\n {{#model.title}}\n <h6 class="timeline-title mb-1">{{model.title}}</h6>\n {{/model.title}}\n \n {{#model.description}}\n <p class="timeline-description mb-0">{{model.description}}</p>\n {{/model.description}}\n \n {{#model.meta}}\n <div class="timeline-meta mt-2 text-muted small">\n {{model.meta}}\n </div>\n {{/model.meta}}\n </div>\n </div>\n ')}async onInit(){await super.onInit(),this.processItemData()}processItemData(){this.displayColor=this.model?.get?.("color")||this.model?.color||this.theme;const t=!(!this.model?.get?.("icon")&&!this.model?.icon)&&"icon"===this.dotStyle;this.hasIcon=t,this.markerType=t?"icon":this.dotStyle;const e=this.model?.get?.("date")||this.model?.date;this.formattedDate=this.formatDate(e)}formatDate(t){if(!t)return"";switch(this.dateFormat){case"datetime":return i.pipe(t,"datetime");case"relative":return i.pipe(t,"timeago");default:return i.pipe(t,"date")}}async onActionSelect(t,e){t.stopPropagation(),this.emit("item:click",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model}),this.listView&&this.listView.emit("item:click",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model})}}class TimelineView extends e{constructor(t={}){super({className:"timeline-view",itemClass:t.itemClass||TimelineViewItem,selectionMode:"none",emptyMessage:t.emptyMessage||"No timeline events to display",template:'\n <div class="timeline-container timeline-{{position}}">\n {{#loading}}\n <div class="timeline-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 <span class="ms-2 text-muted">Loading timeline...</span>\n </div>\n {{/loading}}\n {{^loading}}\n {{#isEmpty}}\n <div class="timeline-empty text-center text-muted py-4">\n <i class="bi bi-clock-history fs-1 d-block mb-2"></i>\n <p>{{emptyMessage}}</p>\n </div>\n {{/isEmpty}}\n {{^isEmpty}}\n <div class="timeline" data-container="items"></div>\n {{/isEmpty}}\n {{/loading}}\n </div>\n ',...t}),this.position=t.position||"left",this.dateFormat=t.dateFormat||"date",this.dotStyle=t.dotStyle||"solid",this.showDate=!1!==t.showDate,this.theme=t.theme||"primary",this.groupBy=t.groupBy||"none"}_createItemView(t,e){if(this.itemViews.has(t.id))return;const i=new this.itemClass({model:t,index:e,listView:this,template:this.itemTemplate,dateFormat:this.dateFormat,dotStyle:this.dotStyle,showDate:this.showDate,theme:this.theme});return this.itemViews.set(t.id,i),i.on("item:click",this._onItemClick.bind(this)),i}_onItemClick(t){this.emit("item:click",t)}setPosition(t){return"left"!==t&&"center"!==t?(console.warn('Invalid position. Use "left" or "center"'),this):(this.position=t,this.isMounted()&&this.render(),this)}setDateFormat(t){return this.dateFormat=t,this.forEachItem(e=>{e.dateFormat=t,e.processItemData(),e.isMounted()&&e.render()}),this}setDotStyle(t){return this.dotStyle=t,this.forEachItem(e=>{e.dotStyle=t,e.processItemData(),e.isMounted()&&e.render()}),this}toggleDates(t=null){return this.showDate=null!==t?t:!this.showDate,this.forEachItem(t=>{t.showDate=this.showDate,t.isMounted()&&t.render()}),this}}export{s as Collection,e as ListView,t as ListViewItem,n as Model,TimelineView,TimelineViewItem,o as View};
1
+ import{L as t,a as e}from"./chunks/ListView-D_hOtfWZ.js";import{d as i}from"./chunks/Collection-C39Oy2q0.js";import{C as s,M as n,V as o}from"./chunks/Collection-C39Oy2q0.js";class TimelineViewItem extends t{constructor(t={}){super({className:"timeline-item",...t}),this.dateFormat=t.dateFormat||"date",this.dotStyle=t.dotStyle||"solid",this.showDate=!1!==t.showDate,this.theme=t.theme||"primary",this.template||(this.template='\n <div class="timeline-marker timeline-marker-{{markerType}}">\n {{#hasIcon}}\n <i class="bi {{model.icon}} text-{{displayColor}}"></i>\n {{/hasIcon}}\n {{^hasIcon}}\n <div class="timeline-dot bg-{{displayColor}}"></div>\n {{/hasIcon}}\n </div>\n \n <div class="timeline-content">\n {{#showDate}}\n <div class="timeline-date text-muted small">\n {{formattedDate}}\n </div>\n {{/showDate}}\n \n <div class="timeline-card">\n {{#model.title}}\n <h6 class="timeline-title mb-1">{{model.title}}</h6>\n {{/model.title}}\n \n {{#model.description}}\n <p class="timeline-description mb-0">{{model.description}}</p>\n {{/model.description}}\n \n {{#model.meta}}\n <div class="timeline-meta mt-2 text-muted small">\n {{model.meta}}\n </div>\n {{/model.meta}}\n </div>\n </div>\n ')}async onInit(){await super.onInit(),this.processItemData()}processItemData(){this.displayColor=this.model?.get?.("color")||this.model?.color||this.theme;const t=!(!this.model?.get?.("icon")&&!this.model?.icon)&&"icon"===this.dotStyle;this.hasIcon=t,this.markerType=t?"icon":this.dotStyle;const e=this.model?.get?.("date")||this.model?.date;this.formattedDate=this.formatDate(e)}formatDate(t){if(!t)return"";switch(this.dateFormat){case"datetime":return i.pipe(t,"datetime");case"relative":return i.pipe(t,"timeago");default:return i.pipe(t,"date")}}async onActionSelect(t,e){t.stopPropagation(),this.emit("item:click",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model}),this.listView&&this.listView.emit("item:click",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model})}}class TimelineView extends e{constructor(t={}){super({className:"timeline-view",itemClass:t.itemClass||TimelineViewItem,selectionMode:"none",emptyMessage:t.emptyMessage||"No timeline events to display",template:'\n <div class="timeline-container timeline-{{position}}">\n {{#loading}}\n <div class="timeline-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 <span class="ms-2 text-muted">Loading timeline...</span>\n </div>\n {{/loading}}\n {{^loading}}\n {{#isEmpty}}\n <div class="timeline-empty text-center text-muted py-4">\n <i class="bi bi-clock-history fs-1 d-block mb-2"></i>\n <p>{{emptyMessage}}</p>\n </div>\n {{/isEmpty}}\n {{^isEmpty}}\n <div class="timeline" data-container="items"></div>\n {{/isEmpty}}\n {{/loading}}\n </div>\n ',...t}),this.position=t.position||"left",this.dateFormat=t.dateFormat||"date",this.dotStyle=t.dotStyle||"solid",this.showDate=!1!==t.showDate,this.theme=t.theme||"primary",this.groupBy=t.groupBy||"none"}_createItemView(t,e){if(this.itemViews.has(t.id))return;const i=new this.itemClass({model:t,index:e,listView:this,template:this.itemTemplate,dateFormat:this.dateFormat,dotStyle:this.dotStyle,showDate:this.showDate,theme:this.theme});return this.itemViews.set(t.id,i),i.on("item:click",this._onItemClick.bind(this)),i}_onItemClick(t){this.emit("item:click",t)}setPosition(t){return"left"!==t&&"center"!==t?(console.warn('Invalid position. Use "left" or "center"'),this):(this.position=t,this.isMounted()&&this.render(),this)}setDateFormat(t){return this.dateFormat=t,this.forEachItem(e=>{e.dateFormat=t,e.processItemData(),e.isMounted()&&e.render()}),this}setDotStyle(t){return this.dotStyle=t,this.forEachItem(e=>{e.dotStyle=t,e.processItemData(),e.isMounted()&&e.render()}),this}toggleDates(t=null){return this.showDate=null!==t?t:!this.showDate,this.forEachItem(t=>{t.showDate=this.showDate,t.isMounted()&&t.render()}),this}}export{s as Collection,e as ListView,t as ListViewItem,n as Model,TimelineView,TimelineViewItem,o as View};
2
2
  //# sourceMappingURL=timeline.es.js.map
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./chunks/UserProfileView-NpL2wU6h.js"),i=require("./chunks/index-D3v4btI8.js");exports.PasskeySetupView=e.PasskeySetupView,exports.ProfileApiKeysSection=e.ProfileApiKeysSection,exports.ProfileConnectedSection=e.ProfileConnectedSection,exports.ProfileDevicesSection=e.ProfileDevicesSection,exports.ProfileGroupsSection=e.ProfileGroupsSection,exports.ProfileNotificationsSection=e.ProfileNotificationsSection,exports.ProfileOverviewSection=e.ProfileOverviewSection,exports.ProfilePermissionsSection=e.ProfilePermissionsSection,exports.ProfilePersonalSection=e.ProfilePersonalSection,exports.ProfileSecurityEventsSection=e.ProfileSecurityEventsSection,exports.ProfileSecuritySection=e.ProfileSecuritySection,exports.ProfileSessionsSection=e.ProfileSessionsSection,exports.UserProfileView=e.UserProfileView,exports.ProfileActivitySection=i.ProfileActivitySection;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./chunks/UserProfileView-BXT8COCF.js"),i=require("./chunks/index-CSmG_P2r.js");exports.PasskeySetupView=e.PasskeySetupView,exports.ProfileApiKeysSection=e.ProfileApiKeysSection,exports.ProfileConnectedSection=e.ProfileConnectedSection,exports.ProfileDevicesSection=e.ProfileDevicesSection,exports.ProfileGroupsSection=e.ProfileGroupsSection,exports.ProfileNotificationsSection=e.ProfileNotificationsSection,exports.ProfileOverviewSection=e.ProfileOverviewSection,exports.ProfilePermissionsSection=e.ProfilePermissionsSection,exports.ProfilePersonalSection=e.ProfilePersonalSection,exports.ProfileSecurityEventsSection=e.ProfileSecurityEventsSection,exports.ProfileSecuritySection=e.ProfileSecuritySection,exports.ProfileSessionsSection=e.ProfileSessionsSection,exports.UserProfileView=e.UserProfileView,exports.ProfileActivitySection=i.ProfileActivitySection;
2
2
  //# sourceMappingURL=user-profile.cjs.js.map
@@ -1,2 +1,2 @@
1
- import{P as e,a as i,b as o,c as s,d as r,e as n,f as t,g as c,h as a,i as f,j as P,k as S,U as l}from"./chunks/UserProfileView-CxrYFgMl.js";import{ProfileActivitySection as m}from"./chunks/index-BPn9cZzq.js";export{e as PasskeySetupView,m as ProfileActivitySection,i as ProfileApiKeysSection,o as ProfileConnectedSection,s as ProfileDevicesSection,r as ProfileGroupsSection,n as ProfileNotificationsSection,t as ProfileOverviewSection,c as ProfilePermissionsSection,a as ProfilePersonalSection,f as ProfileSecurityEventsSection,P as ProfileSecuritySection,S as ProfileSessionsSection,l as UserProfileView};
1
+ import{P as e,a as i,b as o,c as s,d as r,e as n,f as t,g as c,h as a,i as f,j as P,k as l,U as S}from"./chunks/UserProfileView-Slj-9C__.js";import{ProfileActivitySection as m}from"./chunks/index-uo77m1wt.js";export{e as PasskeySetupView,m as ProfileActivitySection,i as ProfileApiKeysSection,o as ProfileConnectedSection,s as ProfileDevicesSection,r as ProfileGroupsSection,n as ProfileNotificationsSection,t as ProfileOverviewSection,c as ProfilePermissionsSection,a as ProfilePersonalSection,f as ProfileSecurityEventsSection,P as ProfileSecuritySection,l as ProfileSessionsSection,S as UserProfileView};
2
2
  //# sourceMappingURL=user-profile.es.js.map
@@ -5405,6 +5405,12 @@ var MOJO = (function(exports) {
5405
5405
  * Reset model to original state
5406
5406
  */
5407
5407
  reset() {
5408
+ for (const key of Object.keys(this.attributes)) {
5409
+ if (!(key in this.originalAttributes)) delete this[key];
5410
+ }
5411
+ for (const [key, value] of Object.entries(this.originalAttributes)) {
5412
+ this[key] = value;
5413
+ }
5408
5414
  this.attributes = { ...this.originalAttributes };
5409
5415
  this._ = this.attributes;
5410
5416
  this.errors = {};
@@ -5996,7 +6002,7 @@ var MOJO = (function(exports) {
5996
6002
  }
5997
6003
  this.models.sort(comparator);
5998
6004
  if (!options.silent) {
5999
- this.trigger("sort", { collection: this });
6005
+ this.emit("sort", { collection: this });
6000
6006
  }
6001
6007
  return this;
6002
6008
  }
@@ -6050,7 +6056,7 @@ var MOJO = (function(exports) {
6050
6056
  * @returns {Collection} New collection instance
6051
6057
  */
6052
6058
  static fromArray(ModelClass, data = [], options = {}) {
6053
- const collection = new this(ModelClass, options);
6059
+ const collection = new this({ ModelClass, ...options });
6054
6060
  collection.add(data, { silent: true });
6055
6061
  return collection;
6056
6062
  }
@@ -7514,6 +7520,107 @@ var MOJO = (function(exports) {
7514
7520
  isImage() {
7515
7521
  return this.get("category") === "image";
7516
7522
  }
7523
+ /**
7524
+ * Get the file's category. Prefers the backend-provided `category` field.
7525
+ * Falls back to inferring from `content_type` for locally constructed
7526
+ * models that haven't been saved yet. Returns 'other' if both are missing.
7527
+ * @returns {string} One of: image, video, audio, pdf, document, spreadsheet,
7528
+ * presentation, archive, other.
7529
+ */
7530
+ getCategory() {
7531
+ return this.get("category") || this._inferCategoryFromContentType();
7532
+ }
7533
+ /**
7534
+ * Infer category from content_type when the backend `category` field is
7535
+ * missing. Mirrors the backend mapping in
7536
+ * django-mojo/mojo/apps/fileman/renderer/utils.py CATEGORY_MAP.
7537
+ * @returns {string}
7538
+ * @private
7539
+ */
7540
+ _inferCategoryFromContentType() {
7541
+ const ct = (this.get("content_type") || "").toLowerCase();
7542
+ if (!ct) return "other";
7543
+ if (ct.startsWith("image/")) return "image";
7544
+ if (ct.startsWith("video/")) return "video";
7545
+ if (ct.startsWith("audio/")) return "audio";
7546
+ if (ct === "application/pdf") return "pdf";
7547
+ if (ct.startsWith("text/") || ct === "application/msword" || ct.startsWith("application/vnd.openxmlformats-officedocument.wordprocessingml") || ct === "application/vnd.oasis.opendocument.text") return "document";
7548
+ if (ct === "application/vnd.ms-excel" || ct.startsWith("application/vnd.openxmlformats-officedocument.spreadsheetml") || ct === "application/vnd.oasis.opendocument.spreadsheet") return "spreadsheet";
7549
+ if (ct === "application/vnd.ms-powerpoint" || ct.startsWith("application/vnd.openxmlformats-officedocument.presentationml") || ct === "application/vnd.oasis.opendocument.presentation") return "presentation";
7550
+ if (ct === "application/zip" || ct === "application/x-rar-compressed" || ct === "application/x-7z-compressed" || ct === "application/x-tar" || ct === "application/gzip") return "archive";
7551
+ return "other";
7552
+ }
7553
+ /**
7554
+ * True if the file has at least one rendition.
7555
+ * @returns {boolean}
7556
+ */
7557
+ hasRenditions() {
7558
+ const r = this.get("renditions");
7559
+ return !!(r && Object.keys(r).length);
7560
+ }
7561
+ /**
7562
+ * True when the file upload is complete but the backend has not yet
7563
+ * produced any renditions. Renditions are generated asynchronously on the
7564
+ * `renditions` background channel, so an empty `renditions` map paired
7565
+ * with `upload_status === 'completed'` means "still processing".
7566
+ * @returns {boolean}
7567
+ */
7568
+ isRenditionsProcessing() {
7569
+ return this.get("upload_status") === "completed" && !this.hasRenditions();
7570
+ }
7571
+ /**
7572
+ * Trigger a background rebuild of renditions.
7573
+ * See django-mojo fileman docs — POST to the file with
7574
+ * `{ action: 'regenerate_renditions' }` and optional `roles` (<=20).
7575
+ * Returns immediately; the map repopulates as the worker finishes.
7576
+ * @param {string[]} [roles] - Optional rendition roles to rebuild
7577
+ * @returns {Promise}
7578
+ */
7579
+ regenerateRenditions(roles) {
7580
+ const body = { action: "regenerate_renditions" };
7581
+ if (Array.isArray(roles) && roles.length) body.roles = roles;
7582
+ return this.save(body);
7583
+ }
7584
+ /**
7585
+ * Get all renditions as an array. Backend returns renditions as a
7586
+ * role-keyed object; this normalizes to an array for easy iteration.
7587
+ * @returns {Array<object>}
7588
+ */
7589
+ getRenditions() {
7590
+ const r = this.get("renditions");
7591
+ return r ? Object.values(r) : [];
7592
+ }
7593
+ /**
7594
+ * Pick the best image rendition by pixel area (width * height).
7595
+ * Only considers renditions with an `image/*` content_type. Returns null
7596
+ * when no image rendition is available.
7597
+ * @returns {object|null}
7598
+ */
7599
+ getBestImageRendition() {
7600
+ const images = this.getRenditions().filter(
7601
+ (r) => r && typeof r.content_type === "string" && r.content_type.startsWith("image/")
7602
+ );
7603
+ if (!images.length) return null;
7604
+ return images.reduce((best, current) => {
7605
+ const bestArea = (parseInt(best.width) || 0) * (parseInt(best.height) || 0);
7606
+ const currentArea = (parseInt(current.width) || 0) * (parseInt(current.height) || 0);
7607
+ return currentArea > bestArea ? current : best;
7608
+ });
7609
+ }
7610
+ /**
7611
+ * Get a URL suitable for a small preview/thumbnail. Prefers the explicit
7612
+ * `thumbnail` rendition, then falls back to the best available image
7613
+ * rendition. Returns null when no thumbnail-ish URL is available.
7614
+ * @returns {string|null}
7615
+ */
7616
+ getThumbnailUrl() {
7617
+ const renditions = this.get("renditions") || {};
7618
+ if (renditions.thumbnail && renditions.thumbnail.url) {
7619
+ return renditions.thumbnail.url;
7620
+ }
7621
+ const best = this.getBestImageRendition();
7622
+ return best ? best.url : null;
7623
+ }
7517
7624
  /**
7518
7625
  * Upload file with progress tracking and UI integration
7519
7626
  * Returns a FileUpload instance with promise interface and cancellation support