web-mojo 2.2.102 → 2.3.1

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 +131 -0
  2. package/dist/admin-models.cjs.js +1 -1
  3. package/dist/admin-models.es.js +1 -1
  4. package/dist/admin.cjs.js +1 -1
  5. package/dist/admin.es.js +1 -1
  6. package/dist/auth.cjs.js +1 -1
  7. package/dist/auth.cjs.js.map +1 -1
  8. package/dist/auth.es.js +1 -1
  9. package/dist/auth.es.js.map +1 -1
  10. package/dist/charts.cjs.js +1 -1
  11. package/dist/charts.es.js +1 -1
  12. package/dist/chunks/{AssistantPanelView-CMRTtoqS.js → AssistantPanelView-CE6me7sJ.js} +2 -2
  13. package/dist/chunks/{AssistantPanelView-CMRTtoqS.js.map → AssistantPanelView-CE6me7sJ.js.map} +1 -1
  14. package/dist/chunks/{AssistantPanelView-CaVkWhVD.js → AssistantPanelView-DvsLAAIF.js} +2 -2
  15. package/dist/chunks/{AssistantPanelView-CaVkWhVD.js.map → AssistantPanelView-DvsLAAIF.js.map} +1 -1
  16. package/dist/chunks/{ChatView-B73uox2v.js → ChatView-CXykdAV1.js} +2 -2
  17. package/dist/chunks/{ChatView-B73uox2v.js.map → ChatView-CXykdAV1.js.map} +1 -1
  18. package/dist/chunks/{ChatView-W8daOwIo.js → ChatView-ys5MIiBf.js} +2 -2
  19. package/dist/chunks/{ChatView-W8daOwIo.js.map → ChatView-ys5MIiBf.js.map} +1 -1
  20. package/dist/chunks/{Collection-BZlmtcuL.js → Collection-BpUmNuDZ.js} +2 -2
  21. package/dist/chunks/{Collection-BZlmtcuL.js.map → Collection-BpUmNuDZ.js.map} +1 -1
  22. package/dist/chunks/{Collection-Bwoq6muu.js → Collection-CY6BblFn.js} +2 -2
  23. package/dist/chunks/{Collection-Bwoq6muu.js.map → Collection-CY6BblFn.js.map} +1 -1
  24. package/dist/chunks/{ContextMenu-q76hjQb6.js → ContextMenu-PtN50qH2.js} +2 -2
  25. package/dist/chunks/{ContextMenu-q76hjQb6.js.map → ContextMenu-PtN50qH2.js.map} +1 -1
  26. package/dist/chunks/{ContextMenu-BPPtuqKk.js → ContextMenu-XQVFU0mL.js} +2 -2
  27. package/dist/chunks/{ContextMenu-BPPtuqKk.js.map → ContextMenu-XQVFU0mL.js.map} +1 -1
  28. package/dist/chunks/{DataView-k-7wmk5_.js → DataView-BTi_BZHx.js} +2 -2
  29. package/dist/chunks/{DataView-k-7wmk5_.js.map → DataView-BTi_BZHx.js.map} +1 -1
  30. package/dist/chunks/{DataView-BbrwHMV4.js → DataView-WHRh1o6E.js} +2 -2
  31. package/dist/chunks/{DataView-BbrwHMV4.js.map → DataView-WHRh1o6E.js.map} +1 -1
  32. package/dist/chunks/{FormView-Dcy7XOtC.js → FormView-BzaGMf5_.js} +3 -3
  33. package/dist/chunks/{FormView-Dcy7XOtC.js.map → FormView-BzaGMf5_.js.map} +1 -1
  34. package/dist/chunks/{FormView-DPSuwWMq.js → FormView-DTD-Zy22.js} +3 -3
  35. package/dist/chunks/{FormView-DPSuwWMq.js.map → FormView-DTD-Zy22.js.map} +1 -1
  36. package/dist/chunks/{ListView-iGBsD4a7.js → ListView-BsnnTcmC.js} +2 -2
  37. package/dist/chunks/{ListView-iGBsD4a7.js.map → ListView-BsnnTcmC.js.map} +1 -1
  38. package/dist/chunks/{ListView-DHC-yBIw.js → ListView-Xf7kO6Me.js} +2 -2
  39. package/dist/chunks/{ListView-DHC-yBIw.js.map → ListView-Xf7kO6Me.js.map} +1 -1
  40. package/dist/chunks/{MetricsCountryMapView-CAD9wR_T.js → MetricsCountryMapView-BzGOi1d2.js} +2 -2
  41. package/dist/chunks/{MetricsCountryMapView-CAD9wR_T.js.map → MetricsCountryMapView-BzGOi1d2.js.map} +1 -1
  42. package/dist/chunks/{MetricsCountryMapView-Dzk3Yrzx.js → MetricsCountryMapView-C-S00Wiw.js} +2 -2
  43. package/dist/chunks/{MetricsCountryMapView-Dzk3Yrzx.js.map → MetricsCountryMapView-C-S00Wiw.js.map} +1 -1
  44. package/dist/chunks/{Modal-DuULCMFZ.js → Modal-GWjyfcz5.js} +3 -3
  45. package/dist/chunks/Modal-GWjyfcz5.js.map +1 -0
  46. package/dist/chunks/{Modal-DBJU16cc.js → Modal-wrfWfQhv.js} +3 -3
  47. package/dist/chunks/Modal-wrfWfQhv.js.map +1 -0
  48. package/dist/chunks/{Passkeys-CGRZ8ZMv.js → Passkeys-BviQX3_5.js} +2 -2
  49. package/dist/chunks/{Passkeys-CGRZ8ZMv.js.map → Passkeys-BviQX3_5.js.map} +1 -1
  50. package/dist/chunks/{Passkeys-Dr8-oSm9.js → Passkeys-gXR1Rc6C.js} +2 -2
  51. package/dist/chunks/{Passkeys-Dr8-oSm9.js.map → Passkeys-gXR1Rc6C.js.map} +1 -1
  52. package/dist/chunks/TokenManager-C6aXkRaI.js +2 -0
  53. package/dist/chunks/TokenManager-C6aXkRaI.js.map +1 -0
  54. package/dist/chunks/TokenManager-DgvhhTqN.js +2 -0
  55. package/dist/chunks/TokenManager-DgvhhTqN.js.map +1 -0
  56. package/dist/chunks/{User-Dg7xpYEI.js → User-B_Urf7U7.js} +2 -2
  57. package/dist/chunks/{User-Dg7xpYEI.js.map → User-B_Urf7U7.js.map} +1 -1
  58. package/dist/chunks/{User-DNQhdBtI.js → User-KTBU_5cr.js} +2 -2
  59. package/dist/chunks/{User-DNQhdBtI.js.map → User-KTBU_5cr.js.map} +1 -1
  60. package/dist/chunks/{UserProfileView-Bpz3VZmP.js → UserProfileView-BmduMJ86.js} +2 -2
  61. package/dist/chunks/{UserProfileView-Bpz3VZmP.js.map → UserProfileView-BmduMJ86.js.map} +1 -1
  62. package/dist/chunks/{UserProfileView-B5nczdfw.js → UserProfileView-COxSyPB0.js} +2 -2
  63. package/dist/chunks/{UserProfileView-B5nczdfw.js.map → UserProfileView-COxSyPB0.js.map} +1 -1
  64. package/dist/chunks/{View-Yazho7OL.js → View-C8UWvaSM.js} +2 -2
  65. package/dist/chunks/{View-Yazho7OL.js.map → View-C8UWvaSM.js.map} +1 -1
  66. package/dist/chunks/{View-C5n3sIFi.js → View-Cvs2TY7b.js} +2 -2
  67. package/dist/chunks/{View-C5n3sIFi.js.map → View-Cvs2TY7b.js.map} +1 -1
  68. package/dist/chunks/{WebApp-CeiDNV6L.js → WebApp-DuwanN2O.js} +2 -2
  69. package/dist/chunks/{WebApp-CeiDNV6L.js.map → WebApp-DuwanN2O.js.map} +1 -1
  70. package/dist/chunks/{WebApp-irKlhuFX.js → WebApp-kbRq7dM_.js} +2 -2
  71. package/dist/chunks/{WebApp-irKlhuFX.js.map → WebApp-kbRq7dM_.js.map} +1 -1
  72. package/dist/chunks/admin-CszsTA0T.js +2 -0
  73. package/dist/chunks/admin-CszsTA0T.js.map +1 -0
  74. package/dist/chunks/admin-vsKDrnpV.js +2 -0
  75. package/dist/chunks/admin-vsKDrnpV.js.map +1 -0
  76. package/dist/chunks/{exportChart-Dk8D_du5.js → exportChart-Bkxr7mCe.js} +2 -2
  77. package/dist/chunks/exportChart-Bkxr7mCe.js.map +1 -0
  78. package/dist/chunks/{exportChart-DbsHDCxw.js → exportChart-Dn2pioNl.js} +2 -2
  79. package/dist/chunks/exportChart-Dn2pioNl.js.map +1 -0
  80. package/dist/chunks/{index-BNjCQA7q.js → index-BCWkcyOy.js} +2 -2
  81. package/dist/chunks/{index-BNjCQA7q.js.map → index-BCWkcyOy.js.map} +1 -1
  82. package/dist/chunks/{index-DBsIDOAa.js → index-CJeTVskY.js} +2 -2
  83. package/dist/chunks/{index-DBsIDOAa.js.map → index-CJeTVskY.js.map} +1 -1
  84. package/dist/chunks/version-CRFysUYb.js +2 -0
  85. package/dist/chunks/version-CRFysUYb.js.map +1 -0
  86. package/dist/chunks/version-uUvnEzFR.js +2 -0
  87. package/dist/chunks/version-uUvnEzFR.js.map +1 -0
  88. package/dist/core.css +134 -109
  89. package/dist/css/web-mojo.css +1 -1
  90. package/dist/docit.cjs.js +1 -1
  91. package/dist/docit.es.js +1 -1
  92. package/dist/index.cjs.js +1 -1
  93. package/dist/index.cjs.js.map +1 -1
  94. package/dist/index.es.js +1 -1
  95. package/dist/index.es.js.map +1 -1
  96. package/dist/lightbox.cjs.js +1 -1
  97. package/dist/lightbox.es.js +1 -1
  98. package/dist/map.cjs.js +1 -1
  99. package/dist/map.es.js +1 -1
  100. package/dist/mojo-auth.es.js +94 -65
  101. package/dist/mojo-auth.umd.js +1 -1
  102. package/dist/portal.css +32 -0
  103. package/dist/timeline.cjs.js +1 -1
  104. package/dist/timeline.es.js +1 -1
  105. package/dist/user-profile.cjs.js +1 -1
  106. package/dist/user-profile.es.js +1 -1
  107. package/dist/web-mojo.lite.iife.js +33 -9
  108. package/dist/web-mojo.lite.iife.js.map +1 -1
  109. package/dist/web-mojo.lite.iife.min.js +5 -5
  110. package/dist/web-mojo.lite.iife.min.js.map +1 -1
  111. package/package.json +1 -1
  112. package/dist/chunks/Modal-DBJU16cc.js.map +0 -1
  113. package/dist/chunks/Modal-DuULCMFZ.js.map +0 -1
  114. package/dist/chunks/TokenManager-CFsr1qUV.js +0 -2
  115. package/dist/chunks/TokenManager-CFsr1qUV.js.map +0 -1
  116. package/dist/chunks/TokenManager-CHQxK_e5.js +0 -2
  117. package/dist/chunks/TokenManager-CHQxK_e5.js.map +0 -1
  118. package/dist/chunks/admin-BkxeK68u.js +0 -2
  119. package/dist/chunks/admin-BkxeK68u.js.map +0 -1
  120. package/dist/chunks/admin-vjoNbv_1.js +0 -2
  121. package/dist/chunks/admin-vjoNbv_1.js.map +0 -1
  122. package/dist/chunks/exportChart-DbsHDCxw.js.map +0 -1
  123. package/dist/chunks/exportChart-Dk8D_du5.js.map +0 -1
  124. package/dist/chunks/version-B0cBv8MN.js +0 -2
  125. package/dist/chunks/version-B0cBv8MN.js.map +0 -1
  126. package/dist/chunks/version-DtqCY0ZY.js +0 -2
  127. package/dist/chunks/version-DtqCY0ZY.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-Dzk3Yrzx.js"),t=require("./chunks/Collection-BZlmtcuL.js"),s=require("./chunks/View-Yazho7OL.js"),i=require("./chunks/Modal-DBJU16cc.js"),n=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 o(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 _(){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",b),l.addEventListener("blur",_),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",b)}catch(e){}try{l.removeEventListener("blur",_)}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 s.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 s.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(n){}let s;const i=()=>{s=o(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,a="address"===t?.getAttribute?.(this.attributeSelector);if(n||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 n=()=>{s=o(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")}}exports.MapLibreView=e.MapLibreView,exports.MapView=e.MapView,exports.MetricsCountryMapView=e.MetricsCountryMapView,exports.Collection=t.Collection,exports.Model=t.Model,exports.View=s.View,exports.LocationClient=LocationClient,exports.LocationDetailsView=LocationDetailsView,exports.LocationFormPlugin=LocationFormPlugin,exports.registerLocationPlugin=function(e={}){const t=new LocationFormPlugin(e);return n.FormPlugins.register(t)},exports.showLocationDetailsDialog=async function({client:e=new LocationClient({basePath:"/api"}),details:t=null,place_id:s=null,id:n=null,title:o="Location Details",height:a=260,tileLayer:r="osm"}={}){let l=t;if(!l&&(s||n))try{const t=await e.placeDetails({place_id:s,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 i.Modal.dialog({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:s=3,debounceMs:n=200,placeholder:o="Search address",confirmText:a="Select",height:r=240,tileLayer:l="osm"}={}){const d=new LocationPickerView({client:e,minChars:s,debounceMs:n,placeholder:o,height:r,tileLayer:l});return"ok"===await i.Modal.dialog({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=o;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./chunks/MetricsCountryMapView-C-S00Wiw.js"),t=require("./chunks/Collection-BpUmNuDZ.js"),s=require("./chunks/View-C8UWvaSM.js"),i=require("./chunks/Modal-wrfWfQhv.js"),n=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 o(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 _(){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",b),l.addEventListener("blur",_),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",b)}catch(e){}try{l.removeEventListener("blur",_)}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 s.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 s.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(n){}let s;const i=()=>{s=o(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,a="address"===t?.getAttribute?.(this.attributeSelector);if(n||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 n=()=>{s=o(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")}}exports.MapLibreView=e.MapLibreView,exports.MapView=e.MapView,exports.MetricsCountryMapView=e.MetricsCountryMapView,exports.Collection=t.Collection,exports.Model=t.Model,exports.View=s.View,exports.LocationClient=LocationClient,exports.LocationDetailsView=LocationDetailsView,exports.LocationFormPlugin=LocationFormPlugin,exports.registerLocationPlugin=function(e={}){const t=new LocationFormPlugin(e);return n.FormPlugins.register(t)},exports.showLocationDetailsDialog=async function({client:e=new LocationClient({basePath:"/api"}),details:t=null,place_id:s=null,id:n=null,title:o="Location Details",height:a=260,tileLayer:r="osm"}={}){let l=t;if(!l&&(s||n))try{const t=await e.placeDetails({place_id:s,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 i.Modal.dialog({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:s=3,debounceMs:n=200,placeholder:o="Search address",confirmText:a="Select",height:r=240,tileLayer:l="osm"}={}){const d=new LocationPickerView({client:e,minChars:s,debounceMs:n,placeholder:o,height:r,tileLayer:l});return"ok"===await i.Modal.dialog({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=o;
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-CAD9wR_T.js";import{a as t,b as s}from"./chunks/MetricsCountryMapView-CAD9wR_T.js";import{r as i}from"./chunks/Collection-Bwoq6muu.js";import{C as n,M as o}from"./chunks/Collection-Bwoq6muu.js";import{V as a}from"./chunks/View-C5n3sIFi.js";import{a as r}from"./chunks/Modal-DuULCMFZ.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 a{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.dialog({title:n,body:d,size:"md",buttons:[{text:"Close",class:"btn-secondary",dismiss:!0,value:"close"}]})}class LocationPickerView extends a{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.dialog({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{n as Collection,LocationClient,LocationDetailsView,LocationFormPlugin,t as MapLibreView,e as MapView,s as MetricsCountryMapView,o as Model,a as View,p as registerLocationPlugin,c as showLocationDetailsDialog,u as showLocationPickerDialog,d as useLocationAutocomplete};
1
+ import{M as e}from"./chunks/MetricsCountryMapView-BzGOi1d2.js";import{a as t,b as s}from"./chunks/MetricsCountryMapView-BzGOi1d2.js";import{r as i}from"./chunks/Collection-CY6BblFn.js";import{C as n,M as o}from"./chunks/Collection-CY6BblFn.js";import{V as a}from"./chunks/View-Cvs2TY7b.js";import{a as r}from"./chunks/Modal-GWjyfcz5.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 a{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.dialog({title:n,body:d,size:"md",buttons:[{text:"Close",class:"btn-secondary",dismiss:!0,value:"close"}]})}class LocationPickerView extends a{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.dialog({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{n as Collection,LocationClient,LocationDetailsView,LocationFormPlugin,t as MapLibreView,e as MapView,s as MetricsCountryMapView,o as Model,a as View,p as registerLocationPlugin,c as showLocationDetailsDialog,u as showLocationPickerDialog,d as useLocationAutocomplete};
2
2
  //# sourceMappingURL=map.es.js.map
@@ -17,20 +17,21 @@ var y = k((S, p) => {
17
17
  passkeyLoginComplete: "/api/auth/passkeys/login/complete",
18
18
  oauthBegin: "/api/auth/oauth/{provider}/begin",
19
19
  oauthComplete: "/api/auth/oauth/{provider}/complete",
20
- refreshToken: "/api/refresh_token"
20
+ refreshToken: "/api/refresh_token",
21
+ exchangeAuthCode: "/api/auth/exchange"
21
22
  };
22
- function i(e, r) {
23
- var t = u[e] || w[e] || "";
24
- return r && Object.keys(r).forEach(function(n) {
25
- t = t.replace("{" + n + "}", r[n]);
26
- }), g.replace(/\/$/, "") + t;
23
+ function i(e, t) {
24
+ var n = u[e] || w[e] || "";
25
+ return t && Object.keys(t).forEach(function(r) {
26
+ n = n.replace("{" + r + "}", t[r]);
27
+ }), g.replace(/\/$/, "") + n;
27
28
  }
28
- function s(e, r, t) {
29
- var n = Object.assign({ "Content-Type": "application/json" }, {});
29
+ function s(e, t, n) {
30
+ var r = Object.assign({ "Content-Type": "application/json" }, {});
30
31
  return fetch(e, {
31
32
  method: "POST",
32
- headers: n,
33
- body: JSON.stringify(r || {})
33
+ headers: r,
34
+ body: JSON.stringify(t || {})
34
35
  }).then(function(o) {
35
36
  return o.json().then(function(a) {
36
37
  if (!o.ok) throw a;
@@ -38,36 +39,36 @@ var y = k((S, p) => {
38
39
  });
39
40
  });
40
41
  }
41
- function m(e, r) {
42
+ function m(e, t) {
42
43
  return fetch(e, {
43
44
  method: "GET",
44
45
  headers: Object.assign({ "Content-Type": "application/json" }, {})
45
- }).then(function(t) {
46
- return t.json().then(function(n) {
47
- if (!t.ok) throw n;
48
- return n;
46
+ }).then(function(n) {
47
+ return n.json().then(function(r) {
48
+ if (!n.ok) throw r;
49
+ return r;
49
50
  });
50
51
  });
51
52
  }
52
53
  function h(e) {
53
- var r = e && e.data ? e.data : e;
54
- if (!r || !r.access_token) throw new Error("No access_token in response");
55
- return localStorage.setItem(c.access, r.access_token), r.refresh_token && localStorage.setItem(c.refresh, r.refresh_token), r;
54
+ var t = e && e.data ? e.data : e;
55
+ if (!t || !t.access_token) throw new Error("No access_token in response");
56
+ return localStorage.setItem(c.access, t.access_token), t.refresh_token && localStorage.setItem(c.refresh, t.refresh_token), t;
56
57
  }
57
58
  function v(e) {
58
59
  return e ? typeof e == "string" ? e : e.message || e.error || Array.isArray(e.errors) && e.errors[0] && e.errors[0].message || "An error occurred" : "An error occurred";
59
60
  }
60
- function d(e) {
61
- for (var r = e.replace(/-/g, "+").replace(/_/g, "/"), t = atob(r), n = new Uint8Array(t.length), o = 0; o < t.length; o++)
62
- n[o] = t.charCodeAt(o);
63
- return n.buffer;
61
+ function f(e) {
62
+ for (var t = e.replace(/-/g, "+").replace(/_/g, "/"), n = atob(t), r = new Uint8Array(n.length), o = 0; o < n.length; o++)
63
+ r[o] = n.charCodeAt(o);
64
+ return r.buffer;
64
65
  }
65
66
  function l(e) {
66
- for (var r = new Uint8Array(e), t = "", n = 0; n < r.byteLength; n++)
67
- t += String.fromCharCode(r[n]);
68
- return btoa(t).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
67
+ for (var t = new Uint8Array(e), n = "", r = 0; r < t.byteLength; r++)
68
+ n += String.fromCharCode(t[r]);
69
+ return btoa(n).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
69
70
  }
70
- var f = {
71
+ var d = {
71
72
  /**
72
73
  * Initialize the library. Must be called before any auth method.
73
74
  * @param {object} config
@@ -91,10 +92,10 @@ var y = k((S, p) => {
91
92
  * NOTE: If MFA is enabled, the response will contain { mfa_required: true, mfa_token, mfa_methods }
92
93
  * instead of tokens. Check result.mfa_required before assuming login is complete.
93
94
  */
94
- login: function(e, r) {
95
- return s(i("login"), { username: e, password: r }).then(function(t) {
96
- var n = t.data || t;
97
- return n.mfa_required ? n : h(t);
95
+ login: function(e, t) {
96
+ return s(i("login"), { username: e, password: t }).then(function(n) {
97
+ var r = n.data || n;
98
+ return r.mfa_required ? r : h(n);
98
99
  });
99
100
  },
100
101
  // -----------------------------------------------------------------------
@@ -116,11 +117,11 @@ var y = k((S, p) => {
116
117
  * @param {string} newPassword
117
118
  * @returns {Promise<object>}
118
119
  */
119
- resetWithCode: function(e, r, t) {
120
+ resetWithCode: function(e, t, n) {
120
121
  return s(i("resetWithCode"), {
121
122
  email: e,
122
- code: r,
123
- new_password: t
123
+ code: t,
124
+ new_password: n
124
125
  }).then(h);
125
126
  },
126
127
  // -----------------------------------------------------------------------
@@ -141,10 +142,10 @@ var y = k((S, p) => {
141
142
  * @param {string} newPassword
142
143
  * @returns {Promise<object>}
143
144
  */
144
- resetWithToken: function(e, r) {
145
+ resetWithToken: function(e, t) {
145
146
  return s(i("resetWithToken"), {
146
147
  token: e,
147
- new_password: r
148
+ new_password: t
148
149
  }).then(h);
149
150
  },
150
151
  // -----------------------------------------------------------------------
@@ -173,11 +174,11 @@ var y = k((S, p) => {
173
174
  * @returns {Promise<object|null>} resolves with auth data or null if no token found
174
175
  */
175
176
  handleMagicTokenFromURL: function() {
176
- var e = new URLSearchParams(window.location.search), r = e.get("token");
177
- if (!r || r.indexOf("ml:") !== 0) return Promise.resolve(null);
177
+ var e = new URLSearchParams(window.location.search), t = e.get("token");
178
+ if (!t || t.indexOf("ml:") !== 0) return Promise.resolve(null);
178
179
  e.delete("token");
179
- var t = e.toString() ? window.location.pathname + "?" + e.toString() : window.location.pathname;
180
- return window.history.replaceState({}, "", t), f.loginWithMagicToken(r);
180
+ var n = e.toString() ? window.location.pathname + "?" + e.toString() : window.location.pathname;
181
+ return window.history.replaceState({}, "", n), d.loginWithMagicToken(t);
181
182
  },
182
183
  // -----------------------------------------------------------------------
183
184
  // Passkey Login (WebAuthn)
@@ -197,14 +198,14 @@ var y = k((S, p) => {
197
198
  * @returns {Promise<object>}
198
199
  */
199
200
  loginWithPasskeyDiscoverable: function() {
200
- return f.isPasskeySupported() ? s(i("passkeyLoginBegin"), {}).then(function(e) {
201
- var r = e.data || e, t = r.challenge_id, n = r.publicKey;
202
- return n.challenge = d(n.challenge), n.allowCredentials && (n.allowCredentials = n.allowCredentials.map(function(o) {
203
- return Object.assign({}, o, { id: d(o.id) });
204
- })), navigator.credentials.get({ publicKey: n }).then(function(o) {
201
+ return d.isPasskeySupported() ? s(i("passkeyLoginBegin"), {}).then(function(e) {
202
+ var t = e.data || e, n = t.challenge_id, r = t.publicKey;
203
+ return r.challenge = f(r.challenge), r.allowCredentials && (r.allowCredentials = r.allowCredentials.map(function(o) {
204
+ return Object.assign({}, o, { id: f(o.id) });
205
+ })), navigator.credentials.get({ publicKey: r }).then(function(o) {
205
206
  if (!o) throw new Error("No credential received from authenticator");
206
207
  return s(i("passkeyLoginComplete"), {
207
- challenge_id: t,
208
+ challenge_id: n,
208
209
  credential: {
209
210
  id: o.id,
210
211
  rawId: l(o.rawId),
@@ -228,14 +229,14 @@ var y = k((S, p) => {
228
229
  * @returns {Promise<object>}
229
230
  */
230
231
  loginWithPasskey: function(e) {
231
- return f.isPasskeySupported() ? s(i("passkeyLoginBegin"), { username: e }).then(function(r) {
232
- var t = r.data || r, n = t.challenge_id, o = t.publicKey;
233
- return o.challenge = d(o.challenge), o.allowCredentials && (o.allowCredentials = o.allowCredentials.map(function(a) {
234
- return Object.assign({}, a, { id: d(a.id) });
232
+ return d.isPasskeySupported() ? s(i("passkeyLoginBegin"), { username: e }).then(function(t) {
233
+ var n = t.data || t, r = n.challenge_id, o = n.publicKey;
234
+ return o.challenge = f(o.challenge), o.allowCredentials && (o.allowCredentials = o.allowCredentials.map(function(a) {
235
+ return Object.assign({}, a, { id: f(a.id) });
235
236
  })), navigator.credentials.get({ publicKey: o }).then(function(a) {
236
237
  if (!a) throw new Error("No credential received from authenticator");
237
238
  return s(i("passkeyLoginComplete"), {
238
- challenge_id: n,
239
+ challenge_id: r,
239
240
  credential: {
240
241
  id: a.id,
241
242
  rawId: l(a.rawId),
@@ -269,17 +270,17 @@ var y = k((S, p) => {
269
270
  * allowed by the backend (ALLOWED_REDIRECT_URLS or per-group).
270
271
  * @returns {Promise<void>}
271
272
  */
272
- startOAuthLogin: function(e, r) {
273
+ startOAuthLogin: function(e, t) {
273
274
  sessionStorage.setItem("oauth_provider", e);
274
- var t = r || window.location.origin + window.location.pathname, n = i("oauthBegin", { provider: e }) + "?redirect_uri=" + encodeURIComponent(t);
275
- return m(n).then(function(o) {
275
+ var n = t || window.location.origin + window.location.pathname, r = i("oauthBegin", { provider: e }) + "?redirect_uri=" + encodeURIComponent(n);
276
+ return m(r).then(function(o) {
276
277
  var a = o.data || o;
277
278
  if (!a.auth_url) throw new Error("No auth_url in OAuth begin response");
278
279
  window.location.href = a.auth_url;
279
280
  });
280
281
  },
281
282
  startGoogleLogin: function(e) {
282
- return f.startOAuthLogin("google", e);
283
+ return d.startOAuthLogin("google", e);
283
284
  },
284
285
  /**
285
286
  * Complete OAuth login for any provider — call this on your OAuth callback page.
@@ -289,15 +290,43 @@ var y = k((S, p) => {
289
290
  * @returns {Promise<object>}
290
291
  */
291
292
  completeOAuthLogin: function(e) {
292
- var r = new URLSearchParams(window.location.search), t = r.get("code"), n = r.get("state");
293
- return t ? s(i("oauthComplete", { provider: e }), {
294
- code: t,
295
- state: n
293
+ var t = new URLSearchParams(window.location.search), n = t.get("code"), r = t.get("state");
294
+ return n ? s(i("oauthComplete", { provider: e }), {
295
+ code: n,
296
+ state: r
296
297
  }).then(h) : Promise.reject(new Error("No OAuth code in URL"));
297
298
  },
298
299
  /** @deprecated use completeOAuthLogin('google') */
299
300
  completeGoogleLogin: function() {
300
- return f.completeOAuthLogin("google");
301
+ return d.completeOAuthLogin("google");
302
+ },
303
+ // -----------------------------------------------------------------------
304
+ // Cross-Origin Auth Handoff
305
+ // -----------------------------------------------------------------------
306
+ /**
307
+ * Exchange a one-time auth handoff code for access + refresh tokens.
308
+ * The auth domain mints the code via /api/auth/handoff and appends it
309
+ * as ?auth_code= on the redirect to the consuming app. Single-use,
310
+ * 60s TTL — server-enforced.
311
+ * Stores tokens on success.
312
+ * @param {string} code - 32-hex auth handoff code from ?auth_code=
313
+ * @returns {Promise<object>} response data ({ access_token, refresh_token, user })
314
+ */
315
+ exchangeAuthCode: function(e) {
316
+ return s(i("exchangeAuthCode"), { code: e }).then(h);
317
+ },
318
+ /**
319
+ * Convenience: read ?auth_code= from current URL and exchange it for
320
+ * tokens. Cleans the param from the URL *before* the network call so
321
+ * a slow exchange can't leak the code to third-party scripts.
322
+ * @returns {Promise<object|null>} resolves with auth data or null if no code found
323
+ */
324
+ handleAuthCodeFromURL: function() {
325
+ var e = new URLSearchParams(window.location.search), t = e.get("auth_code");
326
+ if (!t) return Promise.resolve(null);
327
+ e.delete("auth_code");
328
+ var n = window.location.pathname + (e.toString() ? "?" + e.toString() : "") + (window.location.hash || "");
329
+ return window.history.replaceState({}, "", n), d.exchangeAuthCode(t);
301
330
  },
302
331
  // -----------------------------------------------------------------------
303
332
  // Session helpers
@@ -346,10 +375,10 @@ var y = k((S, p) => {
346
375
  var e = localStorage.getItem(c.access);
347
376
  if (!e) return null;
348
377
  try {
349
- var r = e.split(".");
350
- if (r.length !== 3) return null;
351
- var t = r[1].replace(/-/g, "+").replace(/_/g, "/"), n = 4 - t.length % 4;
352
- return n !== 4 && (t += "====".slice(0, n)), JSON.parse(atob(t));
378
+ var t = e.split(".");
379
+ if (t.length !== 3) return null;
380
+ var n = t[1].replace(/-/g, "+").replace(/_/g, "/"), r = 4 - n.length % 4;
381
+ return r !== 4 && (n += "====".slice(0, r)), JSON.parse(atob(n));
353
382
  } catch {
354
383
  return null;
355
384
  }
@@ -360,7 +389,7 @@ var y = k((S, p) => {
360
389
  * @returns {boolean}
361
390
  */
362
391
  isTokenExpired: function() {
363
- var e = f.getTokenPayload();
392
+ var e = d.getTokenPayload();
364
393
  return !e || !e.exp ? !0 : Math.floor(Date.now() / 1e3) >= e.exp;
365
394
  },
366
395
  /**
@@ -383,7 +412,7 @@ var y = k((S, p) => {
383
412
  */
384
413
  getError: v
385
414
  };
386
- return f;
415
+ return d;
387
416
  });
388
417
  });
389
418
  export default y();
@@ -1 +1 @@
1
- (function(f){typeof define=="function"&&define.amd?define(f):f()})((function(){"use strict";(function(f,h){typeof define=="function"&&define.amd?define([],h):typeof module<"u"&&module.exports?module.exports=h():f.MojoAuth=h()})(typeof globalThis<"u"?globalThis:typeof window<"u"?window:void 0,function(){var f="",h={},u={access:"access_token",refresh:"refresh_token"},p={login:"/api/login",forgotPassword:"/api/auth/forgot",resetWithCode:"/api/auth/password/reset/code",resetWithToken:"/api/auth/password/reset/token",magicSend:"/api/auth/magic/send",magicLogin:"/api/auth/magic/login",passkeyLoginBegin:"/api/auth/passkeys/login/begin",passkeyLoginComplete:"/api/auth/passkeys/login/complete",oauthBegin:"/api/auth/oauth/{provider}/begin",oauthComplete:"/api/auth/oauth/{provider}/complete",refreshToken:"/api/refresh_token"};function i(e,n){var t=h[e]||p[e]||"";return n&&Object.keys(n).forEach(function(r){t=t.replace("{"+r+"}",n[r])}),f.replace(/\/$/,"")+t}function s(e,n,t){var r=Object.assign({"Content-Type":"application/json"},{});return fetch(e,{method:"POST",headers:r,body:JSON.stringify(n||{})}).then(function(o){return o.json().then(function(a){if(!o.ok)throw a;return a})})}function w(e,n){return fetch(e,{method:"GET",headers:Object.assign({"Content-Type":"application/json"},{})}).then(function(t){return t.json().then(function(r){if(!t.ok)throw r;return r})})}function l(e){var n=e&&e.data?e.data:e;if(!n||!n.access_token)throw new Error("No access_token in response");return localStorage.setItem(u.access,n.access_token),n.refresh_token&&localStorage.setItem(u.refresh,n.refresh_token),n}function m(e){return e?typeof e=="string"?e:e.message||e.error||Array.isArray(e.errors)&&e.errors[0]&&e.errors[0].message||"An error occurred":"An error occurred"}function g(e){for(var n=e.replace(/-/g,"+").replace(/_/g,"/"),t=atob(n),r=new Uint8Array(t.length),o=0;o<t.length;o++)r[o]=t.charCodeAt(o);return r.buffer}function c(e){for(var n=new Uint8Array(e),t="",r=0;r<n.byteLength;r++)t+=String.fromCharCode(n[r]);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}var d={init:function(e){if(!e||!e.baseURL)throw new Error("MojoAuth.init: baseURL is required");f=e.baseURL,h=Object.assign({},e.endpoints||{})},login:function(e,n){return s(i("login"),{username:e,password:n}).then(function(t){var r=t.data||t;return r.mfa_required?r:l(t)})},forgotPasswordCode:function(e){return s(i("forgotPassword"),{email:e,method:"code"})},resetWithCode:function(e,n,t){return s(i("resetWithCode"),{email:e,code:n,new_password:t}).then(l)},forgotPasswordLink:function(e){return s(i("forgotPassword"),{email:e,method:"link"})},resetWithToken:function(e,n){return s(i("resetWithToken"),{token:e,new_password:n}).then(l)},sendMagicLink:function(e){return s(i("magicSend"),{email:e})},loginWithMagicToken:function(e){return s(i("magicLogin"),{token:e}).then(l)},handleMagicTokenFromURL:function(){var e=new URLSearchParams(window.location.search),n=e.get("token");if(!n||n.indexOf("ml:")!==0)return Promise.resolve(null);e.delete("token");var t=e.toString()?window.location.pathname+"?"+e.toString():window.location.pathname;return window.history.replaceState({},"",t),d.loginWithMagicToken(n)},isPasskeySupported:function(){return typeof window<"u"&&typeof window.PublicKeyCredential<"u"&&typeof navigator.credentials<"u"&&typeof navigator.credentials.get=="function"},loginWithPasskeyDiscoverable:function(){return d.isPasskeySupported()?s(i("passkeyLoginBegin"),{}).then(function(e){var n=e.data||e,t=n.challenge_id,r=n.publicKey;return r.challenge=g(r.challenge),r.allowCredentials&&(r.allowCredentials=r.allowCredentials.map(function(o){return Object.assign({},o,{id:g(o.id)})})),navigator.credentials.get({publicKey:r}).then(function(o){if(!o)throw new Error("No credential received from authenticator");return s(i("passkeyLoginComplete"),{challenge_id:t,credential:{id:o.id,rawId:c(o.rawId),type:o.type,response:{clientDataJSON:c(o.response.clientDataJSON),authenticatorData:c(o.response.authenticatorData),signature:c(o.response.signature),userHandle:o.response.userHandle?c(o.response.userHandle):null}}})})}).then(l):Promise.reject(new Error("Passkeys are not supported in this browser"))},loginWithPasskey:function(e){return d.isPasskeySupported()?s(i("passkeyLoginBegin"),{username:e}).then(function(n){var t=n.data||n,r=t.challenge_id,o=t.publicKey;return o.challenge=g(o.challenge),o.allowCredentials&&(o.allowCredentials=o.allowCredentials.map(function(a){return Object.assign({},a,{id:g(a.id)})})),navigator.credentials.get({publicKey:o}).then(function(a){if(!a)throw new Error("No credential received from authenticator");return s(i("passkeyLoginComplete"),{challenge_id:r,credential:{id:a.id,rawId:c(a.rawId),type:a.type,response:{clientDataJSON:c(a.response.clientDataJSON),authenticatorData:c(a.response.authenticatorData),signature:c(a.response.signature),userHandle:a.response.userHandle?c(a.response.userHandle):null}}})})}).then(l):Promise.reject(new Error("Passkeys are not supported in this browser"))},startOAuthLogin:function(e,n){sessionStorage.setItem("oauth_provider",e);var t=n||window.location.origin+window.location.pathname,r=i("oauthBegin",{provider:e})+"?redirect_uri="+encodeURIComponent(t);return w(r).then(function(o){var a=o.data||o;if(!a.auth_url)throw new Error("No auth_url in OAuth begin response");window.location.href=a.auth_url})},startGoogleLogin:function(e){return d.startOAuthLogin("google",e)},completeOAuthLogin:function(e){var n=new URLSearchParams(window.location.search),t=n.get("code"),r=n.get("state");return t?s(i("oauthComplete",{provider:e}),{code:t,state:r}).then(l):Promise.reject(new Error("No OAuth code in URL"))},completeGoogleLogin:function(){return d.completeOAuthLogin("google")},logout:function(){localStorage.removeItem(u.access),localStorage.removeItem(u.refresh)},isAuthenticated:function(){return!!localStorage.getItem(u.access)},getToken:function(){return localStorage.getItem(u.access)},getRefreshToken:function(){return localStorage.getItem(u.refresh)},getAuthHeader:function(){var e=localStorage.getItem(u.access);return e?"Bearer "+e:null},getTokenPayload:function(){var e=localStorage.getItem(u.access);if(!e)return null;try{var n=e.split(".");if(n.length!==3)return null;var t=n[1].replace(/-/g,"+").replace(/_/g,"/"),r=4-t.length%4;return r!==4&&(t+="====".slice(0,r)),JSON.parse(atob(t))}catch{return null}},isTokenExpired:function(){var e=d.getTokenPayload();return!e||!e.exp?!0:Math.floor(Date.now()/1e3)>=e.exp},refreshToken:function(){var e=localStorage.getItem(u.refresh);return e?s(i("refreshToken"),{refresh_token:e}).then(l):Promise.reject(new Error("No refresh token stored"))},getError:m};return d})}));
1
+ (function(d){typeof define=="function"&&define.amd?define(d):d()})((function(){"use strict";(function(d,f){typeof define=="function"&&define.amd?define([],f):typeof module<"u"&&module.exports?module.exports=f():d.MojoAuth=f()})(typeof globalThis<"u"?globalThis:typeof window<"u"?window:void 0,function(){var d="",f={},u={access:"access_token",refresh:"refresh_token"},p={login:"/api/login",forgotPassword:"/api/auth/forgot",resetWithCode:"/api/auth/password/reset/code",resetWithToken:"/api/auth/password/reset/token",magicSend:"/api/auth/magic/send",magicLogin:"/api/auth/magic/login",passkeyLoginBegin:"/api/auth/passkeys/login/begin",passkeyLoginComplete:"/api/auth/passkeys/login/complete",oauthBegin:"/api/auth/oauth/{provider}/begin",oauthComplete:"/api/auth/oauth/{provider}/complete",refreshToken:"/api/refresh_token",exchangeAuthCode:"/api/auth/exchange"};function i(e,n){var t=f[e]||p[e]||"";return n&&Object.keys(n).forEach(function(r){t=t.replace("{"+r+"}",n[r])}),d.replace(/\/$/,"")+t}function s(e,n,t){var r=Object.assign({"Content-Type":"application/json"},{});return fetch(e,{method:"POST",headers:r,body:JSON.stringify(n||{})}).then(function(o){return o.json().then(function(a){if(!o.ok)throw a;return a})})}function w(e,n){return fetch(e,{method:"GET",headers:Object.assign({"Content-Type":"application/json"},{})}).then(function(t){return t.json().then(function(r){if(!t.ok)throw r;return r})})}function l(e){var n=e&&e.data?e.data:e;if(!n||!n.access_token)throw new Error("No access_token in response");return localStorage.setItem(u.access,n.access_token),n.refresh_token&&localStorage.setItem(u.refresh,n.refresh_token),n}function m(e){return e?typeof e=="string"?e:e.message||e.error||Array.isArray(e.errors)&&e.errors[0]&&e.errors[0].message||"An error occurred":"An error occurred"}function g(e){for(var n=e.replace(/-/g,"+").replace(/_/g,"/"),t=atob(n),r=new Uint8Array(t.length),o=0;o<t.length;o++)r[o]=t.charCodeAt(o);return r.buffer}function c(e){for(var n=new Uint8Array(e),t="",r=0;r<n.byteLength;r++)t+=String.fromCharCode(n[r]);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}var h={init:function(e){if(!e||!e.baseURL)throw new Error("MojoAuth.init: baseURL is required");d=e.baseURL,f=Object.assign({},e.endpoints||{})},login:function(e,n){return s(i("login"),{username:e,password:n}).then(function(t){var r=t.data||t;return r.mfa_required?r:l(t)})},forgotPasswordCode:function(e){return s(i("forgotPassword"),{email:e,method:"code"})},resetWithCode:function(e,n,t){return s(i("resetWithCode"),{email:e,code:n,new_password:t}).then(l)},forgotPasswordLink:function(e){return s(i("forgotPassword"),{email:e,method:"link"})},resetWithToken:function(e,n){return s(i("resetWithToken"),{token:e,new_password:n}).then(l)},sendMagicLink:function(e){return s(i("magicSend"),{email:e})},loginWithMagicToken:function(e){return s(i("magicLogin"),{token:e}).then(l)},handleMagicTokenFromURL:function(){var e=new URLSearchParams(window.location.search),n=e.get("token");if(!n||n.indexOf("ml:")!==0)return Promise.resolve(null);e.delete("token");var t=e.toString()?window.location.pathname+"?"+e.toString():window.location.pathname;return window.history.replaceState({},"",t),h.loginWithMagicToken(n)},isPasskeySupported:function(){return typeof window<"u"&&typeof window.PublicKeyCredential<"u"&&typeof navigator.credentials<"u"&&typeof navigator.credentials.get=="function"},loginWithPasskeyDiscoverable:function(){return h.isPasskeySupported()?s(i("passkeyLoginBegin"),{}).then(function(e){var n=e.data||e,t=n.challenge_id,r=n.publicKey;return r.challenge=g(r.challenge),r.allowCredentials&&(r.allowCredentials=r.allowCredentials.map(function(o){return Object.assign({},o,{id:g(o.id)})})),navigator.credentials.get({publicKey:r}).then(function(o){if(!o)throw new Error("No credential received from authenticator");return s(i("passkeyLoginComplete"),{challenge_id:t,credential:{id:o.id,rawId:c(o.rawId),type:o.type,response:{clientDataJSON:c(o.response.clientDataJSON),authenticatorData:c(o.response.authenticatorData),signature:c(o.response.signature),userHandle:o.response.userHandle?c(o.response.userHandle):null}}})})}).then(l):Promise.reject(new Error("Passkeys are not supported in this browser"))},loginWithPasskey:function(e){return h.isPasskeySupported()?s(i("passkeyLoginBegin"),{username:e}).then(function(n){var t=n.data||n,r=t.challenge_id,o=t.publicKey;return o.challenge=g(o.challenge),o.allowCredentials&&(o.allowCredentials=o.allowCredentials.map(function(a){return Object.assign({},a,{id:g(a.id)})})),navigator.credentials.get({publicKey:o}).then(function(a){if(!a)throw new Error("No credential received from authenticator");return s(i("passkeyLoginComplete"),{challenge_id:r,credential:{id:a.id,rawId:c(a.rawId),type:a.type,response:{clientDataJSON:c(a.response.clientDataJSON),authenticatorData:c(a.response.authenticatorData),signature:c(a.response.signature),userHandle:a.response.userHandle?c(a.response.userHandle):null}}})})}).then(l):Promise.reject(new Error("Passkeys are not supported in this browser"))},startOAuthLogin:function(e,n){sessionStorage.setItem("oauth_provider",e);var t=n||window.location.origin+window.location.pathname,r=i("oauthBegin",{provider:e})+"?redirect_uri="+encodeURIComponent(t);return w(r).then(function(o){var a=o.data||o;if(!a.auth_url)throw new Error("No auth_url in OAuth begin response");window.location.href=a.auth_url})},startGoogleLogin:function(e){return h.startOAuthLogin("google",e)},completeOAuthLogin:function(e){var n=new URLSearchParams(window.location.search),t=n.get("code"),r=n.get("state");return t?s(i("oauthComplete",{provider:e}),{code:t,state:r}).then(l):Promise.reject(new Error("No OAuth code in URL"))},completeGoogleLogin:function(){return h.completeOAuthLogin("google")},exchangeAuthCode:function(e){return s(i("exchangeAuthCode"),{code:e}).then(l)},handleAuthCodeFromURL:function(){var e=new URLSearchParams(window.location.search),n=e.get("auth_code");if(!n)return Promise.resolve(null);e.delete("auth_code");var t=window.location.pathname+(e.toString()?"?"+e.toString():"")+(window.location.hash||"");return window.history.replaceState({},"",t),h.exchangeAuthCode(n)},logout:function(){localStorage.removeItem(u.access),localStorage.removeItem(u.refresh)},isAuthenticated:function(){return!!localStorage.getItem(u.access)},getToken:function(){return localStorage.getItem(u.access)},getRefreshToken:function(){return localStorage.getItem(u.refresh)},getAuthHeader:function(){var e=localStorage.getItem(u.access);return e?"Bearer "+e:null},getTokenPayload:function(){var e=localStorage.getItem(u.access);if(!e)return null;try{var n=e.split(".");if(n.length!==3)return null;var t=n[1].replace(/-/g,"+").replace(/_/g,"/"),r=4-t.length%4;return r!==4&&(t+="====".slice(0,r)),JSON.parse(atob(t))}catch{return null}},isTokenExpired:function(){var e=h.getTokenPayload();return!e||!e.exp?!0:Math.floor(Date.now()/1e3)>=e.exp},refreshToken:function(){var e=localStorage.getItem(u.refresh);return e?s(i("refreshToken"),{refresh_token:e}).then(l):Promise.reject(new Error("No refresh token stored"))},getError:m};return h})}));
package/dist/portal.css CHANGED
@@ -163,6 +163,38 @@ Light / Subtle Neutral (good if rest of portal is bright):
163
163
  border-block-end: 1px solid var(--mojo-portal-border);
164
164
  }
165
165
 
166
+ /* ========================================================================
167
+ TopNav Responsive Collapse
168
+ ------------------------------------------------------------------------
169
+ When the navbar collapses under .navbar-expand-lg (<992px), Bootstrap
170
+ stacks all .navbar-nav items vertically. Our right-side items (settings,
171
+ github, user menu, etc.) are typically a small fixed set, so a horizontal
172
+ row justified to the right reads better than a vertical stack of full-
173
+ width links. Left-side navItems (rare in this app) follow the same
174
+ layout for consistency.
175
+ ======================================================================== */
176
+ @media (max-width: 991.98px) {
177
+ nav.navbar.navbar-expand-lg .navbar-collapse .navbar-nav {
178
+ flex-direction: row;
179
+ flex-wrap: wrap;
180
+ align-items: center;
181
+ gap: 0.25rem;
182
+ }
183
+ nav.navbar.navbar-expand-lg .navbar-collapse .navbar-nav.ms-auto {
184
+ justify-content: flex-end;
185
+ margin-left: auto !important;
186
+ }
187
+ nav.navbar.navbar-expand-lg .navbar-collapse .navbar-nav .nav-link {
188
+ padding-left: 0.5rem;
189
+ padding-right: 0.5rem;
190
+ }
191
+ /* Dropdowns (e.g. user menu) — keep them anchored as floating menus
192
+ instead of expanding inline (Bootstrap's default in collapse mode). */
193
+ nav.navbar.navbar-expand-lg .navbar-collapse .dropdown-menu {
194
+ position: absolute;
195
+ }
196
+ }
197
+
166
198
  /* ========================================================================
167
199
  TopNav Themes (Applied directly to the <nav> element by TopNav.js)
168
200
  ======================================================================== */
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("./chunks/ListView-iGBsD4a7.js"),e=require("./chunks/Collection-BZlmtcuL.js"),i=require("./chunks/View-Yazho7OL.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",t.template||(this.template='\n <div class="timeline-marker timeline-marker-{{displayColor}}">\n {{#hasIcon}}\n <i class="bi {{model.icon}}"></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=i.View,exports.TimelineView=TimelineView,exports.TimelineViewItem=TimelineViewItem;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("./chunks/ListView-BsnnTcmC.js"),e=require("./chunks/Collection-BpUmNuDZ.js"),i=require("./chunks/View-C8UWvaSM.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",t.template||(this.template='\n <div class="timeline-marker timeline-marker-{{displayColor}}">\n {{#hasIcon}}\n <i class="bi {{model.icon}}"></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=i.View,exports.TimelineView=TimelineView,exports.TimelineViewItem=TimelineViewItem;
2
2
  //# sourceMappingURL=timeline.cjs.js.map