@defra/interactive-map 0.0.20-alpha → 0.0.22-alpha

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 (59) hide show
  1. package/dist/esm/im-core.js +1 -1
  2. package/dist/esm/im-shell.js +1 -1
  3. package/dist/esm/index.js +1 -1
  4. package/dist/umd/im-core.js +1 -1
  5. package/dist/umd/index.js +1 -1
  6. package/docs/api.md +41 -2
  7. package/docs/examples/index.mdx +2 -2
  8. package/docs/plugins/datasets.md +50 -12
  9. package/package.json +6 -4
  10. package/plugins/beta/datasets/dist/adapters/maplibre/esm/im-datasets-ml-adapter.js +1 -0
  11. package/plugins/beta/datasets/dist/adapters/maplibre/esm/index.js +1 -0
  12. package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -1
  13. package/plugins/beta/datasets/dist/umd/maplibre/im-datasets-maplibre-adapter.js +2 -0
  14. package/plugins/beta/datasets/dist/umd/maplibre/im-datasets-plugin.js +2 -0
  15. package/plugins/beta/datasets/dist/umd/maplibre/index.js +2 -0
  16. package/plugins/beta/datasets/dist/umd/maplibre/index.js.LICENSE.txt +1 -0
  17. package/plugins/beta/datasets/src/adapters/maplibre/index.js +1 -1
  18. package/plugins/beta/datasets/src/adapters/maplibre/layerBuilders.js +4 -4
  19. package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.js +26 -7
  20. package/plugins/beta/datasets/src/defaults.js +1 -1
  21. package/plugins/beta/datasets/src/index.maplibre.umd.js +23 -0
  22. package/plugins/beta/datasets/src/manifest.js +1 -1
  23. package/plugins/beta/datasets/src/panels/Layers.jsx +4 -4
  24. package/plugins/beta/datasets/src/utils/mergeSublayer.js +1 -1
  25. package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
  26. package/providers/maplibre/dist/esm/index.js +1 -1
  27. package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
  28. package/providers/maplibre/dist/umd/index.js +1 -1
  29. package/providers/maplibre/src/index.js +7 -1
  30. package/providers/maplibre/src/index.test.js +18 -0
  31. package/providers/maplibre/src/maplibreProvider.js +2 -1
  32. package/providers/maplibre/src/maplibreProvider.test.js +22 -2
  33. package/providers/maplibre/src/utils/patternImages.js +16 -10
  34. package/providers/maplibre/src/utils/patternImages.test.js +50 -19
  35. package/rollup.esm.mjs +9 -0
  36. package/src/App/components/Viewport/MapController.jsx +1 -1
  37. package/src/App/hooks/useMapURLSync.js +3 -3
  38. package/src/App/hooks/useMapURLSync.test.js +20 -3
  39. package/src/InteractiveMap/InteractiveMap.js +1 -1
  40. package/src/InteractiveMap/InteractiveMap.test.js +4 -4
  41. package/src/InteractiveMap/behaviourController.js +1 -2
  42. package/src/InteractiveMap/buttonManager.js +1 -3
  43. package/src/InteractiveMap/buttonManager.test.js +16 -8
  44. package/src/InteractiveMap/domStateManager.js +2 -3
  45. package/src/InteractiveMap/historyManager.js +4 -3
  46. package/src/config/defaults.js +2 -1
  47. package/src/config/mergeConfig.js +8 -2
  48. package/src/config/mergeConfig.test.js +19 -0
  49. package/src/types.js +22 -2
  50. package/src/utils/mapStateSync.js +2 -2
  51. package/src/utils/mapStateSync.test.js +18 -0
  52. package/src/utils/patternUtils.js +8 -3
  53. package/src/utils/patternUtils.test.js +17 -0
  54. package/webpack.dev.mjs +4 -0
  55. package/webpack.umd.mjs +9 -5
  56. package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +0 -2
  57. package/plugins/beta/datasets/dist/umd/index.js +0 -2
  58. /package/plugins/beta/datasets/dist/umd/{im-datasets-plugin.js.LICENSE.txt → maplibre/im-datasets-maplibre-adapter.js.LICENSE.txt} +0 -0
  59. /package/plugins/beta/datasets/dist/umd/{index.js.LICENSE.txt → maplibre/im-datasets-plugin.js.LICENSE.txt} +0 -0
@@ -1,2 +1,2 @@
1
1
  /*! For license information please see index.js.LICENSE.txt */
2
- !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("defra",[],t):"object"==typeof exports?exports.defra=t():(e.defra=e.defra||{},e.defra.maplibreProvider=t())}(this,()=>(()=>{"use strict";var e,t,r,n,o={738(e){e.exports=preactCompat},287(e){e.exports=preactJsxRuntime}},i={};function a(e){var t=i[e];if(void 0!==t)return t.exports;var r=i[e]={exports:{}};return o[e].call(r.exports,r,r.exports,a),r.exports}a.m=o,t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,a.t=function(r,n){if(1&n&&(r=this(r)),8&n)return r;if("object"==typeof r&&r){if(4&n&&r.__esModule)return r;if(16&n&&"function"==typeof r.then)return r}var o=Object.create(null);a.r(o);var i={};e=e||[null,t({}),t([]),t(t)];for(var u=2&n&&r;("object"==typeof u||"function"==typeof u)&&!~e.indexOf(u);u=t(u))Object.getOwnPropertyNames(u).forEach(e=>i[e]=()=>r[e]);return i.default=()=>r,a.d(o,i),o},a.d=(e,t)=>{for(var r in t)a.o(t,r)&&!a.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},a.f={},a.e=e=>Promise.all(Object.keys(a.f).reduce((t,r)=>(a.f[r](e,t),t),[])),a.u=e=>({649:"im-maplibre-framework",772:"im-maplibre-provider"}[e]+".js"),a.miniCssF=e=>{},a.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r={},n="defra.maplibreProvider:",a.l=(e,t,o,i)=>{if(r[e])r[e].push(t);else{var u,c;if(void 0!==o)for(var f=document.getElementsByTagName("script"),l=0;l<f.length;l++){var p=f[l];if(p.getAttribute("src")==e||p.getAttribute("data-webpack")==n+o){u=p;break}}u||(c=!0,(u=document.createElement("script")).charset="utf-8",a.nc&&u.setAttribute("nonce",a.nc),u.setAttribute("data-webpack",n+o),u.src=e),r[e]=[t];var s=(t,n)=>{u.onerror=u.onload=null,clearTimeout(d);var o=r[e];if(delete r[e],u.parentNode&&u.parentNode.removeChild(u),o&&o.forEach(e=>e(n)),t)return t(n)},d=setTimeout(s.bind(null,void 0,{type:"timeout",target:u}),12e4);u.onerror=s.bind(null,u.onerror),u.onload=s.bind(null,u.onload),c&&document.head.appendChild(u)}},a.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var e;a.g.importScripts&&(e=a.g.location+"");var t=a.g.document;if(!e&&t&&(t.currentScript&&"SCRIPT"===t.currentScript.tagName.toUpperCase()&&(e=t.currentScript.src),!e)){var r=t.getElementsByTagName("script");if(r.length)for(var n=r.length-1;n>-1&&(!e||!/^http(s?):/.test(e));)e=r[n--].src}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),a.p=e})(),(()=>{var e={57:0};a.f.j=(t,r)=>{var n=a.o(e,t)?e[t]:void 0;if(0!==n)if(n)r.push(n[2]);else{var o=new Promise((r,o)=>n=e[t]=[r,o]);r.push(n[2]=o);var i=a.p+a.u(t),u=new Error;a.l(i,r=>{if(a.o(e,t)&&(0!==(n=e[t])&&(e[t]=void 0),n)){var o=r&&("load"===r.type?"missing":r.type),i=r&&r.target&&r.target.src;u.message="Loading chunk "+t+" failed.\n("+o+": "+i+")",u.name="ChunkLoadError",u.type=o,u.request=i,n[1](u)}},"chunk-"+t,t)}};var t=(t,r)=>{var n,o,[i,u,c]=r,f=0;if(i.some(t=>0!==e[t])){for(n in u)a.o(u,n)&&(a.m[n]=u[n]);c&&c(a)}for(t&&t(r);f<i.length;f++)o=i[f],a.o(e,o)&&e[o]&&e[o][0](),e[o]=0},r=this.webpackChunkdefra_DefraMap=this.webpackChunkdefra_DefraMap||[];r.forEach(t.bind(null,0)),r.push=t.bind(null,r.push.bind(r))})();var u={};function c(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r<t;r++)n[r]=e[r];return n}a.d(u,{default:()=>m});var f=function(e){if(!window.WebGLRenderingContext)return{isEnabled:!1,error:"WebGL is not supported"};var t,r=document.createElement("canvas"),n=!1,o=function(e,t){var r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!r){if(Array.isArray(e)||(r=function(e,t){if(e){if("string"==typeof e)return c(e,t);var r={}.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?c(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0,o=function(){};return{s:o,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,u=!1;return{s:function(){r=r.call(e)},n:function(){var e=r.next();return a=e.done,e},e:function(e){u=!0,i=e},f:function(){try{a||null==r.return||r.return()}finally{if(u)throw i}}}}(e);try{for(o.s();!(t=o.n()).done;){var i=t.value;try{if((n=r.getContext(i))&&"function"==typeof n.getParameter)return{isEnabled:!0}}catch(e){}}}catch(e){o.e(e)}finally{o.f()}return{isEnabled:!1,error:"WebGL is supported, but disabled"}};function l(e){return l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},l(e)}function p(){var e,t,r="function"==typeof Symbol?Symbol:{},n=r.iterator||"@@iterator",o=r.toStringTag||"@@toStringTag";function i(r,n,o,i){var c=n&&n.prototype instanceof u?n:u,f=Object.create(c.prototype);return s(f,"_invoke",function(r,n,o){var i,u,c,f=0,l=o||[],p=!1,s={p:0,n:0,v:e,a:d,f:d.bind(e,4),d:function(t,r){return i=t,u=0,c=e,s.n=r,a}};function d(r,n){for(u=r,c=n,t=0;!p&&f&&!o&&t<l.length;t++){var o,i=l[t],d=s.p,b=i[2];r>3?(o=b===n)&&(c=i[(u=i[4])?5:(u=3,3)],i[4]=i[5]=e):i[0]<=d&&((o=r<2&&d<i[1])?(u=0,s.v=n,s.n=i[1]):d<b&&(o=r<3||i[0]>n||n>b)&&(i[4]=r,i[5]=n,s.n=b,u=0))}if(o||r>1)return a;throw p=!0,n}return function(o,l,b){if(f>1)throw TypeError("Generator is already running");for(p&&1===l&&d(l,b),u=l,c=b;(t=u<2?e:c)||!p;){i||(u?u<3?(u>1&&(s.n=-1),d(u,c)):s.n=c:s.v=c);try{if(f=2,i){if(u||(o="next"),t=i[o]){if(!(t=t.call(i,c)))throw TypeError("iterator result is not an object");if(!t.done)return t;c=t.value,u<2&&(u=0)}else 1===u&&(t=i.return)&&t.call(i),u<2&&(c=TypeError("The iterator does not provide a '"+o+"' method"),u=1);i=e}else if((t=(p=s.n<0)?c:r.call(n,s))!==a)break}catch(t){i=e,u=1,c=t}finally{f=1}}return{value:t,done:p}}}(r,o,i),!0),f}var a={};function u(){}function c(){}function f(){}t=Object.getPrototypeOf;var l=[][n]?t(t([][n]())):(s(t={},n,function(){return this}),t),d=f.prototype=u.prototype=Object.create(l);function b(e){return Object.setPrototypeOf?Object.setPrototypeOf(e,f):(e.__proto__=f,s(e,o,"GeneratorFunction")),e.prototype=Object.create(d),e}return c.prototype=f,s(d,"constructor",f),s(f,"constructor",c),c.displayName="GeneratorFunction",s(f,o,"GeneratorFunction"),s(d),s(d,o,"Generator"),s(d,n,function(){return this}),s(d,"toString",function(){return"[object Generator]"}),(p=function(){return{w:i,m:b}})()}function s(e,t,r,n){var o=Object.defineProperty;try{o({},"",{})}catch(e){o=0}s=function(e,t,r,n){function i(t,r){s(e,t,function(e){return this._invoke(t,r,e)})}t?o?o(e,t,{value:r,enumerable:!n,configurable:!n,writable:!n}):e[t]=r:(i("next",0),i("throw",1),i("return",2))},s(e,t,r,n)}function d(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function b(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?d(Object(r),!0).forEach(function(t){y(e,t,r[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):d(Object(r)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))})}return e}function y(e,t,r){return(t=function(e){var t=function(e){if("object"!=l(e)||!e)return e;var t=e[Symbol.toPrimitive];if(void 0!==t){var r=t.call(e,"string");if("object"!=l(r))return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e);return"symbol"==l(t)?t:t+""}(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function v(e,t,r,n,o,i,a){try{var u=e[i](a),c=u.value}catch(e){return void r(e)}u.done?t(c):Promise.resolve(c).then(n,o)}function m(){var e,t,r=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{checkDeviceCapabilities:function(){var e=f(["webgl2","webgl1"]),t=document.documentMode;return{isSupported:e.isEnabled&&"function"==typeof"".replaceAll,error:t?"Internet Explorer is not supported":e.error}},load:(e=p().m(function e(){var t,n,o;return p().w(function(e){for(;;)switch(e.n){case 0:return e.n=1,a.e(649).then(a.t.bind(a,443,19));case 1:return t=e.v,e.n=2,a.e(772).then(a.bind(a,335));case 2:return n=e.v.default,o=b(b({},r),{},{crs:"EPSG:4326"}),e.a(2,{MapProvider:n,mapProviderConfig:o,mapFramework:t})}},e)}),t=function(){var t=this,r=arguments;return new Promise(function(n,o){var i=e.apply(t,r);function a(e){v(i,n,o,a,u,"next",e)}function u(e){v(i,n,o,a,u,"throw",e)}a(void 0)})},function(){return t.apply(this,arguments)})}}return u.default})());
2
+ !function(e,r){"object"==typeof exports&&"object"==typeof module?module.exports=r():"function"==typeof define&&define.amd?define("defra",[],r):"object"==typeof exports?exports.defra=r():(e.defra=e.defra||{},e.defra.maplibreProvider=r())}(this,()=>(()=>{"use strict";var e,r,t,n,o={738(e){e.exports=preactCompat},287(e){e.exports=preactJsxRuntime}},i={};function a(e){var r=i[e];if(void 0!==r)return r.exports;var t=i[e]={exports:{}};return o[e].call(t.exports,t,t.exports,a),t.exports}a.m=o,r=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,a.t=function(t,n){if(1&n&&(t=this(t)),8&n)return t;if("object"==typeof t&&t){if(4&n&&t.__esModule)return t;if(16&n&&"function"==typeof t.then)return t}var o=Object.create(null);a.r(o);var i={};e=e||[null,r({}),r([]),r(r)];for(var u=2&n&&t;("object"==typeof u||"function"==typeof u)&&!~e.indexOf(u);u=r(u))Object.getOwnPropertyNames(u).forEach(e=>i[e]=()=>t[e]);return i.default=()=>t,a.d(o,i),o},a.d=(e,r)=>{for(var t in r)a.o(r,t)&&!a.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},a.f={},a.e=e=>Promise.all(Object.keys(a.f).reduce((r,t)=>(a.f[t](e,r),r),[])),a.u=e=>({649:"im-maplibre-framework",772:"im-maplibre-provider"}[e]+".js"),a.miniCssF=e=>{},a.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),a.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),t={},n="defra.maplibreProvider:",a.l=(e,r,o,i)=>{if(t[e])t[e].push(r);else{var u,c;if(void 0!==o)for(var f=document.getElementsByTagName("script"),l=0;l<f.length;l++){var p=f[l];if(p.getAttribute("src")==e||p.getAttribute("data-webpack")==n+o){u=p;break}}u||(c=!0,(u=document.createElement("script")).charset="utf-8",a.nc&&u.setAttribute("nonce",a.nc),u.setAttribute("data-webpack",n+o),u.src=e),t[e]=[r];var s=(r,n)=>{u.onerror=u.onload=null,clearTimeout(d);var o=t[e];if(delete t[e],u.parentNode&&u.parentNode.removeChild(u),o&&o.forEach(e=>e(n)),r)return r(n)},d=setTimeout(s.bind(null,void 0,{type:"timeout",target:u}),12e4);u.onerror=s.bind(null,u.onerror),u.onload=s.bind(null,u.onload),c&&document.head.appendChild(u)}},a.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var e;a.g.importScripts&&(e=a.g.location+"");var r=a.g.document;if(!e&&r&&(r.currentScript&&"SCRIPT"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName("script");if(t.length)for(var n=t.length-1;n>-1&&(!e||!/^http(s?):/.test(e));)e=t[n--].src}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),a.p=e})(),(()=>{var e={57:0};a.f.j=(r,t)=>{var n=a.o(e,r)?e[r]:void 0;if(0!==n)if(n)t.push(n[2]);else{var o=new Promise((t,o)=>n=e[r]=[t,o]);t.push(n[2]=o);var i=a.p+a.u(r),u=new Error;a.l(i,t=>{if(a.o(e,r)&&(0!==(n=e[r])&&(e[r]=void 0),n)){var o=t&&("load"===t.type?"missing":t.type),i=t&&t.target&&t.target.src;u.message="Loading chunk "+r+" failed.\n("+o+": "+i+")",u.name="ChunkLoadError",u.type=o,u.request=i,n[1](u)}},"chunk-"+r,r)}};var r=(r,t)=>{var n,o,[i,u,c]=t,f=0;if(i.some(r=>0!==e[r])){for(n in u)a.o(u,n)&&(a.m[n]=u[n]);c&&c(a)}for(r&&r(t);f<i.length;f++)o=i[f],a.o(e,o)&&e[o]&&e[o][0](),e[o]=0},t=this.webpackChunkdefra_DefraMap=this.webpackChunkdefra_DefraMap||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})();var u={};function c(e,r){(null==r||r>e.length)&&(r=e.length);for(var t=0,n=Array(r);t<r;t++)n[t]=e[t];return n}a.d(u,{default:()=>m});var f=function(e){if(!window.WebGLRenderingContext)return{isEnabled:!1,error:"WebGL is not supported"};var r,t=document.createElement("canvas"),n=!1,o=function(e,r){var t="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!t){if(Array.isArray(e)||(t=function(e,r){if(e){if("string"==typeof e)return c(e,r);var t={}.toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?c(e,r):void 0}}(e))||r&&e&&"number"==typeof e.length){t&&(e=t);var n=0,o=function(){};return{s:o,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,u=!1;return{s:function(){t=t.call(e)},n:function(){var e=t.next();return a=e.done,e},e:function(e){u=!0,i=e},f:function(){try{a||null==t.return||t.return()}finally{if(u)throw i}}}}(e);try{for(o.s();!(r=o.n()).done;){var i=r.value;try{if((n=t.getContext(i))&&"function"==typeof n.getParameter)return{isEnabled:!0}}catch(e){}}}catch(e){o.e(e)}finally{o.f()}return{isEnabled:!1,error:"WebGL is supported, but disabled"}};function l(e){return l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},l(e)}function p(){var e,r,t="function"==typeof Symbol?Symbol:{},n=t.iterator||"@@iterator",o=t.toStringTag||"@@toStringTag";function i(t,n,o,i){var c=n&&n.prototype instanceof u?n:u,f=Object.create(c.prototype);return s(f,"_invoke",function(t,n,o){var i,u,c,f=0,l=o||[],p=!1,s={p:0,n:0,v:e,a:d,f:d.bind(e,4),d:function(r,t){return i=r,u=0,c=e,s.n=t,a}};function d(t,n){for(u=t,c=n,r=0;!p&&f&&!o&&r<l.length;r++){var o,i=l[r],d=s.p,b=i[2];t>3?(o=b===n)&&(c=i[(u=i[4])?5:(u=3,3)],i[4]=i[5]=e):i[0]<=d&&((o=t<2&&d<i[1])?(u=0,s.v=n,s.n=i[1]):d<b&&(o=t<3||i[0]>n||n>b)&&(i[4]=t,i[5]=n,s.n=b,u=0))}if(o||t>1)return a;throw p=!0,n}return function(o,l,b){if(f>1)throw TypeError("Generator is already running");for(p&&1===l&&d(l,b),u=l,c=b;(r=u<2?e:c)||!p;){i||(u?u<3?(u>1&&(s.n=-1),d(u,c)):s.n=c:s.v=c);try{if(f=2,i){if(u||(o="next"),r=i[o]){if(!(r=r.call(i,c)))throw TypeError("iterator result is not an object");if(!r.done)return r;c=r.value,u<2&&(u=0)}else 1===u&&(r=i.return)&&r.call(i),u<2&&(c=TypeError("The iterator does not provide a '"+o+"' method"),u=1);i=e}else if((r=(p=s.n<0)?c:t.call(n,s))!==a)break}catch(r){i=e,u=1,c=r}finally{f=1}}return{value:r,done:p}}}(t,o,i),!0),f}var a={};function u(){}function c(){}function f(){}r=Object.getPrototypeOf;var l=[][n]?r(r([][n]())):(s(r={},n,function(){return this}),r),d=f.prototype=u.prototype=Object.create(l);function b(e){return Object.setPrototypeOf?Object.setPrototypeOf(e,f):(e.__proto__=f,s(e,o,"GeneratorFunction")),e.prototype=Object.create(d),e}return c.prototype=f,s(d,"constructor",f),s(f,"constructor",c),c.displayName="GeneratorFunction",s(f,o,"GeneratorFunction"),s(d),s(d,o,"Generator"),s(d,n,function(){return this}),s(d,"toString",function(){return"[object Generator]"}),(p=function(){return{w:i,m:b}})()}function s(e,r,t,n){var o=Object.defineProperty;try{o({},"",{})}catch(e){o=0}s=function(e,r,t,n){function i(r,t){s(e,r,function(e){return this._invoke(r,t,e)})}r?o?o(e,r,{value:t,enumerable:!n,configurable:!n,writable:!n}):e[r]=t:(i("next",0),i("throw",1),i("return",2))},s(e,r,t,n)}function d(e,r){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);r&&(n=n.filter(function(r){return Object.getOwnPropertyDescriptor(e,r).enumerable})),t.push.apply(t,n)}return t}function b(e){for(var r=1;r<arguments.length;r++){var t=null!=arguments[r]?arguments[r]:{};r%2?d(Object(t),!0).forEach(function(r){y(e,r,t[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):d(Object(t)).forEach(function(r){Object.defineProperty(e,r,Object.getOwnPropertyDescriptor(t,r))})}return e}function y(e,r,t){return(r=function(e){var r=function(e){if("object"!=l(e)||!e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var t=r.call(e,"string");if("object"!=l(t))return t;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e);return"symbol"==l(r)?r:r+""}(r))in e?Object.defineProperty(e,r,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[r]=t,e}function v(e,r,t,n,o,i,a){try{var u=e[i](a),c=u.value}catch(e){return void t(e)}u.done?r(c):Promise.resolve(c).then(n,o)}function m(){var e,r,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{checkDeviceCapabilities:function(){var e=f(["webgl2","webgl1"]),r=document.documentMode;return{isSupported:e.isEnabled&&"function"==typeof"".replaceAll,error:r?"Internet Explorer is not supported":e.error}},load:(e=p().m(function e(){var r,n,o;return p().w(function(e){for(;;)switch(e.n){case 0:return e.n=1,a.e(649).then(a.t.bind(a,443,19));case 1:return r=e.v,t.workerUrl&&(r.workerUrl=t.workerUrl),e.n=2,a.e(772).then(a.bind(a,335));case 2:return n=e.v.default,o=b(b({},t),{},{crs:"EPSG:4326"}),e.a(2,{MapProvider:n,mapProviderConfig:o,mapFramework:r})}},e)}),r=function(){var r=this,t=arguments;return new Promise(function(n,o){var i=e.apply(r,t);function a(e){v(i,n,o,a,u,"next",e)}function u(e){v(i,n,o,a,u,"throw",e)}a(void 0)})},function(){return r.apply(this,arguments)})}}return u.default})());
@@ -2,6 +2,7 @@
2
2
  * @typedef {import('../../../src/types.js').MapProviderDescriptor} MapProviderDescriptor
3
3
  * @typedef {import('../../../src/types.js').MapProviderLoadResult} MapProviderLoadResult
4
4
  * @typedef {import('../../../src/types.js').MapProviderConfig} MapProviderConfig
5
+ * @typedef {import('../../../src/types.js').MaplibreProviderConfig} MaplibreProviderConfig
5
6
  */
6
7
 
7
8
  import { getWebGL } from './utils/detectWebgl.js'
@@ -21,7 +22,7 @@ function supportsModernMaplibre () {
21
22
  /**
22
23
  * Creates a MapLibre provider descriptor for lazy-loading the map provider.
23
24
  *
24
- * @param {Partial<MapProviderConfig>} [config={}] - Optional provider configuration overrides.
25
+ * @param {MaplibreProviderConfig} [config={}] - Optional provider configuration overrides.
25
26
  * @returns {MapProviderDescriptor} The map provider descriptor.
26
27
  */
27
28
  export default function createMapLibreProvider (config = {}) {
@@ -37,6 +38,11 @@ export default function createMapLibreProvider (config = {}) {
37
38
  /** @returns {Promise<MapProviderLoadResult>} */
38
39
  load: async () => {
39
40
  const mapFramework = await import(/* webpackChunkName: "im-maplibre-framework" */ 'maplibre-gl')
41
+
42
+ if (config.workerUrl) {
43
+ mapFramework.workerUrl = config.workerUrl
44
+ }
45
+
40
46
  const MapProvider = (await import(/* webpackChunkName: "im-maplibre-provider" */ './maplibreProvider.js')).default
41
47
 
42
48
  /** @type {MapProviderConfig} */
@@ -74,4 +74,22 @@ describe('createMapLibreProvider', () => {
74
74
 
75
75
  expect(mapProviderConfig).toEqual({ crs: 'EPSG:4326' })
76
76
  })
77
+
78
+ test('load sets workerUrl on mapFramework when provided', async () => {
79
+ await jest.isolateModulesAsync(async () => {
80
+ const { default: createProvider } = await import('./index.js')
81
+ const { mapFramework } = await createProvider({ workerUrl: '/assets/maplibre-gl-csp-worker.js' }).load()
82
+
83
+ expect(mapFramework.workerUrl).toBe('/assets/maplibre-gl-csp-worker.js')
84
+ })
85
+ })
86
+
87
+ test('load does not set workerUrl on mapFramework when not provided', async () => {
88
+ await jest.isolateModulesAsync(async () => {
89
+ const { default: createProvider } = await import('./index.js')
90
+ const { mapFramework } = await createProvider().load()
91
+
92
+ expect(mapFramework.workerUrl).toBeUndefined()
93
+ })
94
+ })
77
95
  })
@@ -333,7 +333,8 @@ export default class MapLibreProvider {
333
333
  * @returns {Promise<void>}
334
334
  */
335
335
  async registerPatterns (patternConfigs, mapStyleId, patternRegistry) {
336
- return registerPatterns(this.map, patternConfigs, mapStyleId, patternRegistry)
336
+ const pixelRatio = (this.map.getPixelRatio() || 1) * (scaleFactor[this.mapSize] || 1)
337
+ return registerPatterns(this.map, patternConfigs, mapStyleId, patternRegistry, pixelRatio)
337
338
  }
338
339
 
339
340
  // ==========================
@@ -280,13 +280,33 @@ describe('MapLibreProvider', () => {
280
280
  expect(registerSymbols).toHaveBeenCalledWith(map, [], { id: 'test' }, registry, 1) // (0 || 1) * 1
281
281
  })
282
282
 
283
- test('registerPatterns delegates to utility with map instance', async () => {
283
+ test('registerPatterns delegates to utility with map instance and pixelRatio', async () => {
284
284
  const p = makeProvider()
285
285
  await doInitMap(p)
286
286
  const configs = [{ fillPattern: 'dot' }]
287
287
  const registry = {}
288
288
  await p.registerPatterns(configs, 'test', registry)
289
- expect(registerPatterns).toHaveBeenCalledWith(map, configs, 'test', registry)
289
+ expect(registerPatterns).toHaveBeenCalledWith(map, configs, 'test', registry, 1) // getPixelRatio()=1, mapSize unset → 1*1
290
+ })
291
+
292
+ test('registerPatterns computes pixelRatio from getPixelRatio and mapSize scale factor', async () => {
293
+ const p = makeProvider()
294
+ await doInitMap(p)
295
+ map.getPixelRatio.mockReturnValue(2)
296
+ p.mapSize = 'medium' // scaleFactor['medium'] = 1.5
297
+ const registry = {}
298
+ await p.registerPatterns([], 'test', registry)
299
+ expect(registerPatterns).toHaveBeenCalledWith(map, [], 'test', registry, 3) // 2 * 1.5
300
+ })
301
+
302
+ test('registerPatterns falls back to pixelRatio 1 when getPixelRatio returns 0', async () => {
303
+ const p = makeProvider()
304
+ await doInitMap(p)
305
+ map.getPixelRatio.mockReturnValue(0)
306
+ p.mapSize = 'small' // scaleFactor['small'] = 1
307
+ const registry = {}
308
+ await p.registerPatterns([], 'test', registry)
309
+ expect(registerPatterns).toHaveBeenCalledWith(map, [], 'test', registry, 1) // (0 || 1) * 1
290
310
  })
291
311
 
292
312
  describe('setHoverCursor', () => {
@@ -1,4 +1,4 @@
1
- import { getPatternInnerContent, getPatternImageId, injectColors } from '../../../../src/utils/patternUtils.js'
1
+ import { getPatternInnerContent, getPatternImageId, injectColors, PATTERN_MIN_PIXEL_RATIO } from '../../../../src/utils/patternUtils.js'
2
2
  import { getValueForStyle } from '../../../../src/utils/getValueForStyle.js'
3
3
  import { rasteriseToImageData } from './rasteriseToImageData.js'
4
4
 
@@ -12,15 +12,16 @@ const imageDataCache = new Map()
12
12
  * @param {Object} dataset
13
13
  * @param {string} mapStyleId
14
14
  * @param {Object} patternRegistry
15
+ * @param {number} pixelRatio
15
16
  * @returns {Promise<{ imageId: string, imageData: ImageData }|null>}
16
17
  */
17
- const rasterisePattern = async (dataset, mapStyleId, patternRegistry) => {
18
+ const rasterisePattern = async (dataset, mapStyleId, patternRegistry, pixelRatio) => {
18
19
  const innerContent = getPatternInnerContent(dataset, patternRegistry)
19
20
  if (!innerContent) {
20
21
  return null
21
22
  }
22
23
 
23
- const imageId = getPatternImageId(dataset, mapStyleId, patternRegistry)
24
+ const imageId = getPatternImageId(dataset, mapStyleId, patternRegistry, pixelRatio)
24
25
  if (!imageId) {
25
26
  return null
26
27
  }
@@ -31,9 +32,12 @@ const rasterisePattern = async (dataset, mapStyleId, patternRegistry) => {
31
32
  const bg = getValueForStyle(dataset.fillPatternBackgroundColor, mapStyleId) || 'transparent'
32
33
  const colored = injectColors(innerContent, fg, bg)
33
34
  const bgRect = `<rect width="16" height="16" fill="${bg}"/>`
34
- // pixelRatio: 2 means the map treats this as an 8×8 logical tile — crisp on retina screens.
35
- const svgString = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">${bgRect}${colored}</svg>`
36
- imageData = await rasteriseToImageData(svgString, 16, 16)
35
+ // effectiveRatio floored at PATTERN_MIN_PIXEL_RATIO keeps the canvas at ≥16px physical so
36
+ // SVG detail renders crisply. Logical tile size = physicalSize / effectiveRatio = 8px always.
37
+ const effectiveRatio = Math.max(PATTERN_MIN_PIXEL_RATIO, pixelRatio)
38
+ const physicalSize = Math.round(8 * effectiveRatio)
39
+ const svgString = `<svg xmlns="http://www.w3.org/2000/svg" width="${physicalSize}" height="${physicalSize}" viewBox="0 0 16 16">${bgRect}${colored}</svg>`
40
+ imageData = await rasteriseToImageData(svgString, physicalSize, physicalSize)
37
41
  imageDataCache.set(imageId, imageData)
38
42
  }
39
43
 
@@ -50,21 +54,23 @@ const rasterisePattern = async (dataset, mapStyleId, patternRegistry) => {
50
54
  * @param {Object[]} patternConfigs - Flat list of datasets/merged-sublayers with a pattern config
51
55
  * @param {string} mapStyleId
52
56
  * @param {Object} patternRegistry
57
+ * @param {number} pixelRatio
53
58
  * @returns {Promise<void>}
54
59
  */
55
- export const registerPatterns = async (map, patternConfigs, mapStyleId, patternRegistry) => {
60
+ export const registerPatterns = async (map, patternConfigs, mapStyleId, patternRegistry, pixelRatio) => {
56
61
  if (!patternConfigs.length) {
57
62
  return
58
63
  }
59
64
 
65
+ const effectiveRatio = Math.max(PATTERN_MIN_PIXEL_RATIO, pixelRatio)
60
66
  await Promise.all(patternConfigs.map(async (config) => {
61
- const imageId = getPatternImageId(config, mapStyleId, patternRegistry)
67
+ const imageId = getPatternImageId(config, mapStyleId, patternRegistry, pixelRatio)
62
68
  if (!imageId || map.hasImage(imageId)) {
63
69
  return
64
70
  }
65
- const result = await rasterisePattern(config, mapStyleId, patternRegistry)
71
+ const result = await rasterisePattern(config, mapStyleId, patternRegistry, pixelRatio)
66
72
  if (result) {
67
- map.addImage(result.imageId, result.imageData, { pixelRatio: 2 })
73
+ map.addImage(result.imageId, result.imageData, { pixelRatio: effectiveRatio })
68
74
  }
69
75
  }))
70
76
  }
@@ -45,19 +45,6 @@ describe('registerPatterns — registration', () => {
45
45
  expect(map.addImage).not.toHaveBeenCalled()
46
46
  })
47
47
 
48
- it('calls addImage with pixelRatio 2 for a named pattern', async () => {
49
- const map = makeMap()
50
- const registry = makePatternRegistry()
51
- const config = { fillPattern: 'stripes' }
52
- await registerPatterns(map, [config], OUTDOOR, registry)
53
- expect(map.addImage).toHaveBeenCalledTimes(1)
54
- expect(map.addImage).toHaveBeenCalledWith(
55
- expect.stringMatching(/^pattern-[a-z0-9]+$/),
56
- expect.any(Object),
57
- { pixelRatio: 2 }
58
- )
59
- })
60
-
61
48
  it('calls addImage for an inline fillPatternSvgContent config', async () => {
62
49
  const map = makeMap()
63
50
  const registry = makePatternRegistry()
@@ -69,10 +56,11 @@ describe('registerPatterns — registration', () => {
69
56
  it('skips addImage when image is already registered', async () => {
70
57
  const registry = makePatternRegistry()
71
58
  const config = { fillPattern: 'stripes' }
59
+ const pixelRatio = 2
72
60
  const { getPatternImageId } = await import('../../../../src/utils/patternUtils.js')
73
- const existingId = getPatternImageId(config, OUTDOOR, registry)
61
+ const existingId = getPatternImageId(config, OUTDOOR, registry, pixelRatio)
74
62
  const map = makeMap([existingId])
75
- await registerPatterns(map, [config], OUTDOOR, registry)
63
+ await registerPatterns(map, [config], OUTDOOR, registry, pixelRatio)
76
64
  expect(map.addImage).not.toHaveBeenCalled()
77
65
  })
78
66
 
@@ -104,6 +92,48 @@ describe('registerPatterns — registration', () => {
104
92
  })
105
93
  })
106
94
 
95
+ describe('registerPatterns — pixel ratio', () => {
96
+ it('encodes effectiveRatio in the image ID and passes it to addImage', async () => {
97
+ const map = makeMap()
98
+ const registry = makePatternRegistry()
99
+ const config = { fillPattern: 'stripes' }
100
+ await registerPatterns(map, [config], OUTDOOR, registry, 2)
101
+ expect(map.addImage).toHaveBeenCalledTimes(1)
102
+ expect(map.addImage).toHaveBeenCalledWith(
103
+ expect.stringMatching(/^pattern-[a-z0-9]+-2x$/),
104
+ expect.any(Object),
105
+ { pixelRatio: 2 }
106
+ )
107
+ })
108
+
109
+ it('floors effectiveRatio at 2 so low-DPI patterns stay crisp', async () => {
110
+ const map = makeMap()
111
+ const registry = makePatternRegistry()
112
+ const config = { fillPattern: 'stripes' }
113
+ await registerPatterns(map, [config], OUTDOOR, registry, 1)
114
+ expect(map.addImage).toHaveBeenCalledWith(
115
+ expect.stringMatching(/-2x$/),
116
+ expect.any(Object),
117
+ { pixelRatio: 2 }
118
+ )
119
+ })
120
+
121
+ it('produces different image IDs for ratios above the floor', async () => {
122
+ const map1 = makeMap()
123
+ const map2 = makeMap()
124
+ const registry = makePatternRegistry()
125
+ const config = { fillPattern: 'stripes' }
126
+ const hiDpi = 3
127
+ await registerPatterns(map1, [config], OUTDOOR, registry, 2)
128
+ await registerPatterns(map2, [config], OUTDOOR, registry, hiDpi)
129
+ const [id2x] = map1.addImage.mock.calls[0]
130
+ const [id3x] = map2.addImage.mock.calls[0]
131
+ expect(id2x).not.toBe(id3x)
132
+ expect(id2x).toMatch(/-2x$/)
133
+ expect(id3x).toMatch(/-3x$/)
134
+ })
135
+ })
136
+
107
137
  describe('registerPatterns — color resolution and caching', () => {
108
138
  it('applies foreground and background colors when resolving the SVG', async () => {
109
139
  const map = makeMap()
@@ -137,12 +167,13 @@ describe('registerPatterns — color resolution and caching', () => {
137
167
  it('uses cached ImageData on second call with identical config', async () => {
138
168
  const map = makeMap()
139
169
  const registry = makePatternRegistry()
140
- const config = { fillPattern: 'stripes', fillPatternForegroundColor: '#unique1' }
141
- await registerPatterns(map, [config], OUTDOOR, registry)
170
+ const config = { fillPattern: 'stripes', fillPatternForegroundColor: '#unique2' }
171
+ const pixelRatio = 2
172
+ await registerPatterns(map, [config], OUTDOOR, registry, pixelRatio)
142
173
  const { getPatternImageId } = await import('../../../../src/utils/patternUtils.js')
143
- const imageId = getPatternImageId(config, OUTDOOR, registry)
174
+ const imageId = getPatternImageId(config, OUTDOOR, registry, pixelRatio)
144
175
  const map2 = makeMap([imageId])
145
- await registerPatterns(map2, [config], OUTDOOR, registry)
176
+ await registerPatterns(map2, [config], OUTDOOR, registry, pixelRatio)
146
177
  expect(map2.addImage).not.toHaveBeenCalled()
147
178
  })
148
179
  })
package/rollup.esm.mjs CHANGED
@@ -255,6 +255,15 @@ const ALL_BUILDS = [
255
255
  outDir: 'plugins/beta/datasets/dist/esm',
256
256
  manualChunks: (id) => { if (id.includes('/manifest')) return 'im-datasets-plugin' }
257
257
  },
258
+ {
259
+ entryPath: './plugins/beta/datasets/src/adapters/maplibre/index.js',
260
+ outDir: 'plugins/beta/datasets/dist/adapters/maplibre/esm',
261
+ manualChunks: (id) => {
262
+ if (id.includes('maplibreLayerAdapter')) {
263
+ return 'im-datasets-ml-adapter'
264
+ }
265
+ }
266
+ },
258
267
  {
259
268
  entryPath: './plugins/beta/map-styles/src/index.js',
260
269
  outDir: 'plugins/beta/map-styles/dist/esm',
@@ -22,7 +22,7 @@ export const MapController = ({ mapContainerRef }) => {
22
22
  const isMapInitialized = useRef(false)
23
23
 
24
24
  // Determine initial map state based on URL, bounds, or center/zoom
25
- const initialState = getInitialMapState({ id, center, zoom, bounds })
25
+ const initialState = getInitialMapState({ id, center, zoom, bounds, urlPosition: config.urlPosition })
26
26
 
27
27
  // Initialize map provider when props are available
28
28
  useEffect(() => {
@@ -10,11 +10,11 @@ import { EVENTS as events } from '../../config/events.js'
10
10
  * shared via a link.
11
11
  */
12
12
  export function useMapURLSync () {
13
- const { id } = useConfig()
13
+ const { id, urlPosition } = useConfig()
14
14
  const { eventBus } = useService()
15
15
 
16
16
  useEffect(() => {
17
- if (!id) {
17
+ if (!id || urlPosition !== 'sync') {
18
18
  return undefined
19
19
  }
20
20
 
@@ -27,5 +27,5 @@ export function useMapURLSync () {
27
27
 
28
28
  eventBus.on(events.MAP_STATE_UPDATED, handleStateUpdate)
29
29
  return () => eventBus.off(events.MAP_STATE_UPDATED, handleStateUpdate)
30
- }, [id])
30
+ }, [id, urlPosition])
31
31
  }
@@ -25,7 +25,7 @@ describe('useMapURLSync', () => {
25
25
  })
26
26
 
27
27
  it('registers map:stateupdated listener and cleans up', () => {
28
- useConfig.mockReturnValue({ id: 'map123' })
28
+ useConfig.mockReturnValue({ id: 'map123', urlPosition: 'sync' })
29
29
 
30
30
  const { unmount } = renderHook(() => useMapURLSync())
31
31
  expect(mockEventBus.on).toHaveBeenCalledWith('map:stateupdated', expect.any(Function))
@@ -35,7 +35,7 @@ describe('useMapURLSync', () => {
35
35
  })
36
36
 
37
37
  it('calls setMapStateInURL on map:stateupdated', () => {
38
- useConfig.mockReturnValue({ id: 'map123' })
38
+ useConfig.mockReturnValue({ id: 'map123', urlPosition: 'sync' })
39
39
 
40
40
  renderHook(() => useMapURLSync())
41
41
 
@@ -50,7 +50,7 @@ describe('useMapURLSync', () => {
50
50
  })
51
51
 
52
52
  it('does nothing if id is falsy', () => {
53
- useConfig.mockReturnValue({ id: null })
53
+ useConfig.mockReturnValue({ id: null, urlPosition: 'sync' })
54
54
 
55
55
  const { unmount } = renderHook(() => useMapURLSync())
56
56
  expect(mockEventBus.on).not.toHaveBeenCalled()
@@ -58,4 +58,21 @@ describe('useMapURLSync', () => {
58
58
 
59
59
  unmount() // cleanup should not throw
60
60
  })
61
+
62
+ it('does not register listener when urlPosition is none', () => {
63
+ useConfig.mockReturnValue({ id: 'map123', urlPosition: 'none' })
64
+
65
+ const { unmount } = renderHook(() => useMapURLSync())
66
+ expect(mockEventBus.on).not.toHaveBeenCalled()
67
+
68
+ unmount()
69
+ expect(mockEventBus.off).not.toHaveBeenCalled()
70
+ })
71
+
72
+ it('does not register listener when urlPosition is readOnly', () => {
73
+ useConfig.mockReturnValue({ id: 'map123', urlPosition: 'readOnly' })
74
+
75
+ renderHook(() => useMapURLSync())
76
+ expect(mockEventBus.on).not.toHaveBeenCalled()
77
+ })
61
78
  })
@@ -144,7 +144,7 @@ export default class InteractiveMap {
144
144
  if (history.state?.isBack) {
145
145
  history.back()
146
146
  } else {
147
- const key = this.config.mapViewParamKey
147
+ const key = this.config.mapViewQueryParam
148
148
  const newUrl = this._removeMapParamFromUrl(location.href, key)
149
149
  history.replaceState(history.state, '', newUrl)
150
150
  }
@@ -274,7 +274,7 @@ describe('InteractiveMap Core Functionality', () => {
274
274
  const map = new InteractiveMap('map', {
275
275
  behaviour: 'buttonFirst',
276
276
  mapProvider: mapProviderMock,
277
- mapViewParamKey: 'mv',
277
+ mapViewQueryParam: 'mv',
278
278
  manageHistoryState: true,
279
279
  preserveStateOnClose: false
280
280
  })
@@ -303,7 +303,7 @@ describe('InteractiveMap Core Functionality', () => {
303
303
  const map = new InteractiveMap('map', {
304
304
  behaviour: 'buttonFirst',
305
305
  mapProvider: mapProviderMock,
306
- mapViewParamKey: 'mv',
306
+ mapViewQueryParam: 'mv',
307
307
  manageHistoryState: true,
308
308
  preserveStateOnClose: true
309
309
  })
@@ -360,7 +360,7 @@ describe('InteractiveMap Core Functionality', () => {
360
360
  const map = new InteractiveMap('map', {
361
361
  behaviour: 'buttonFirst',
362
362
  mapProvider: mapProviderMock,
363
- mapViewParamKey: 'mv',
363
+ mapViewQueryParam: 'mv',
364
364
  manageHistoryState: true,
365
365
  preserveStateOnClose: false
366
366
  })
@@ -380,7 +380,7 @@ describe('InteractiveMap Core Functionality', () => {
380
380
  const map = new InteractiveMap('map', {
381
381
  behaviour: 'buttonFirst',
382
382
  mapProvider: mapProviderMock,
383
- mapViewParamKey: 'mv',
383
+ mapViewQueryParam: 'mv',
384
384
  manageHistoryState: false,
385
385
  preserveStateOnClose: false
386
386
  })
@@ -1,7 +1,6 @@
1
1
  import { getQueryParam } from '../utils/queryString.js'
2
2
  import { isHybridFullscreen } from '../utils/getIsFullscreen.js'
3
3
  import { updateDOMState } from './domStateManager.js'
4
- import defaults from '../config/defaults.js'
5
4
 
6
5
  // -----------------------------------------------------------------------------
7
6
  // Public API
@@ -18,7 +17,7 @@ import defaults from '../config/defaults.js'
18
17
  */
19
18
  function shouldLoadComponent (config) {
20
19
  const { id, behaviour } = config
21
- const hasViewParam = getQueryParam(defaults.mapViewParamKey) === id
20
+ const hasViewParam = getQueryParam(config.mapViewQueryParam) === id
22
21
 
23
22
  return ['mapOnly', 'inline'].includes(behaviour) ||
24
23
  (behaviour === 'hybrid' && !isHybridFullscreen(config)) ||
@@ -12,8 +12,6 @@
12
12
  * @returns {HTMLAnchorElement} The newly created button element.
13
13
  */
14
14
 
15
- import defaults from '../config/defaults.js'
16
-
17
15
  export function createButton (config, rootEl, handleClick) {
18
16
  const { id, buttonText, buttonClass } = config
19
17
 
@@ -33,7 +31,7 @@ export function createButton (config, rootEl, handleClick) {
33
31
 
34
32
  // Build URL keeping existing params
35
33
  const url = new URL(window.location.href)
36
- url.searchParams.set(defaults.mapViewParamKey, id)
34
+ url.searchParams.set(config.mapViewQueryParam, id)
37
35
  button.setAttribute('href', url.toString())
38
36
 
39
37
  button.addEventListener('click', (e) => {
@@ -1,5 +1,4 @@
1
1
  import { createButton } from './buttonManager.js'
2
- import defaults from '../config/defaults.js'
3
2
 
4
3
  describe('createButton', () => {
5
4
  let rootEl, handleClick, button
@@ -19,11 +18,11 @@ describe('createButton', () => {
19
18
  })
20
19
 
21
20
  it('creates button with correct attributes and inserts before rootEl', () => {
22
- const config = { id: 'map', buttonText: 'View Map', buttonClass: 'custom-btn' }
21
+ const config = { id: 'map', buttonText: 'View Map', buttonClass: 'custom-btn', mapViewQueryParam: 'mv' }
23
22
  button = createButton(config, rootEl, handleClick)
24
23
 
25
24
  const expectedHref = new URL(window.location.href)
26
- expectedHref.searchParams.set(defaults.mapViewParamKey, 'map')
25
+ expectedHref.searchParams.set('mv', 'map')
27
26
 
28
27
  expect(button).toBeInstanceOf(HTMLAnchorElement)
29
28
  expect(button.getAttribute('href')).toBe(expectedHref.toString())
@@ -34,7 +33,7 @@ describe('createButton', () => {
34
33
  })
35
34
 
36
35
  it('prevents default and calls handleClick on click', () => {
37
- const config = { id: 'map', buttonText: 'View Map', buttonClass: 'custom-btn' }
36
+ const config = { id: 'map', buttonText: 'View Map', buttonClass: 'custom-btn', mapViewQueryParam: 'mv' }
38
37
  button = createButton(config, rootEl, handleClick)
39
38
 
40
39
  const clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true })
@@ -47,21 +46,30 @@ describe('createButton', () => {
47
46
  })
48
47
 
49
48
  it('builds href with map param and preserves existing query params', () => {
50
- const config = { id: 'map', buttonText: 'View Map', buttonClass: 'custom-btn' }
49
+ const config = { id: 'map', buttonText: 'View Map', buttonClass: 'custom-btn', mapViewQueryParam: 'mv' }
51
50
  button = createButton(config, rootEl, handleClick)
52
51
 
53
52
  const url = new URL(button.getAttribute('href'))
54
53
  expect(url.searchParams.get('foo')).toBe('bar') // existing param preserved
55
- expect(url.searchParams.get(defaults.mapViewParamKey)).toBe('map') // new param added
54
+ expect(url.searchParams.get('mv')).toBe('map') // new param added
56
55
  })
57
56
 
58
57
  it('sets correct href even when no existing query params', () => {
59
58
  window.history.replaceState({}, '', '/test') // no query
60
- const config = { id: 'map', buttonText: 'View Map', buttonClass: 'custom-btn' }
59
+ const config = { id: 'map', buttonText: 'View Map', buttonClass: 'custom-btn', mapViewQueryParam: 'mv' }
61
60
  button = createButton(config, rootEl, handleClick)
62
61
 
63
62
  const url = new URL(button.getAttribute('href'))
64
63
  expect(url.pathname).toBe('/test')
65
- expect(url.searchParams.get(defaults.mapViewParamKey)).toBe('map')
64
+ expect(url.searchParams.get('mv')).toBe('map')
65
+ })
66
+
67
+ it('uses a custom mapViewQueryParam in the button href', () => {
68
+ const config = { id: 'map', buttonText: 'View Map', buttonClass: 'custom-btn', mapViewQueryParam: 'view' }
69
+ button = createButton(config, rootEl, handleClick)
70
+
71
+ const url = new URL(button.getAttribute('href'))
72
+ expect(url.searchParams.get('view')).toBe('map')
73
+ expect(url.searchParams.has('mv')).toBe(false)
66
74
  })
67
75
  })
@@ -1,7 +1,6 @@
1
1
  import { getQueryParam } from '../utils/queryString.js'
2
2
  import { toggleInertElements } from '../utils/toggleInertElements.js'
3
3
  import { isHybridFullscreen } from '../utils/getIsFullscreen.js'
4
- import defaults from '../config/defaults.js'
5
4
 
6
5
  // -----------------------------------------------------------------------------
7
6
  // Internal (not exported)
@@ -25,10 +24,10 @@ function getIsFullscreen (config) {
25
24
  if (manageHistoryState === false) {
26
25
  return true
27
26
  }
28
- return getQueryParam(defaults.mapViewParamKey) === id
27
+ return getQueryParam(config.mapViewQueryParam) === id
29
28
  }
30
29
 
31
- return isHybridFullscreen(config) && getQueryParam(defaults.mapViewParamKey) === id
30
+ return isHybridFullscreen(config) && getQueryParam(config.mapViewQueryParam) === id
32
31
  }
33
32
 
34
33
  // -----------------------------------------------------------------------------
@@ -1,6 +1,5 @@
1
1
  import { getQueryParam } from '../utils/queryString.js'
2
2
  import { isHybridFullscreen } from '../utils/getIsFullscreen.js'
3
- import defaults from '../config/defaults.js'
4
3
 
5
4
  // -----------------------------------------------------------------------------
6
5
  // Internal helpers
@@ -81,8 +80,10 @@ function syncMapInstance (mapInstance, viewId) {
81
80
  * @returns {void}
82
81
  */
83
82
  function handlePopstate () {
84
- const viewId = getQueryParam(defaults.mapViewParamKey)
85
- components.forEach(mapInstance => syncMapInstance(mapInstance, viewId))
83
+ components.forEach(mapInstance => {
84
+ const viewId = getQueryParam(mapInstance.config.mapViewQueryParam)
85
+ syncMapInstance(mapInstance, viewId)
86
+ })
86
87
  }
87
88
 
88
89
  // -----------------------------------------------------------------------------
@@ -25,7 +25,8 @@ const defaults = {
25
25
  mapProvider: null,
26
26
  mapSize: 'small',
27
27
  manageHistoryState: true,
28
- mapViewParamKey: 'mv',
28
+ mapViewQueryParam: 'mv',
29
+ urlPosition: 'sync',
29
30
  maxMobileWidth: 640,
30
31
  minDesktopWidth: 835,
31
32
  nudgePanDelta: 5,