{"_id":"@drecchia/maplibre-layerlibre","_rev":"2-e5b7d7aec7a052ccd2aa9471167b5e99","name":"@drecchia/maplibre-layerlibre","dist-tags":{"latest":"2.2.1"},"versions":{"2.2.0":{"name":"@drecchia/maplibre-layerlibre","version":"2.2.0","keywords":["layer","control","maplibre","vanillajs"],"author":{"name":"Danilo T Recchia"},"license":"CC-BY-NC-4.0","_id":"@drecchia/maplibre-layerlibre@2.2.0","maintainers":[{"name":"drecchia","email":"ciscomp@gmail.com"}],"homepage":"https://github.com/drecchia/maplibre-layerlibre#readme","bugs":{"url":"https://github.com/drecchia/maplibre-layerlibre/issues"},"dist":{"shasum":"dd1cc5279e9e8d653fbea453c2b6ddf617653b34","tarball":"https://registry.npmjs.org/@drecchia/maplibre-layerlibre/-/maplibre-layerlibre-2.2.0.tgz","fileCount":4,"integrity":"sha512-MdIRe+YQQs28XeIBcVv5xnDDoJP76FKJsa+TYHGIlXRmwJMxqj78oiPrbLOeitysKUpepuwaasAuSD+mTVlDEg==","signatures":[{"sig":"MEQCIH/5Z4qWwTw5fOcS+MTpPmddpc2levCEaQAnhvO15rJMAiBVBLJjmD6wc3N1ZN1VsJhbZL0C78iJ2eGRWVWetsaI4w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":54844},"main":"dist/js/all.min.js","gitHead":"a0d4cd040a9a5129dcb9b5ea4feaf4c2040da69c","scripts":{"build":"gulp default"},"_npmUser":{"name":"drecchia","email":"ciscomp@gmail.com"},"repository":{"url":"git+https://github.com/drecchia/maplibre-layerlibre.git","type":"git"},"_npmVersion":"10.2.3","description":"MapLibre LayerLibre — a compact layer-switcher control for MapLibre GL JS with deck.gl overlay support.","directories":{},"_nodeVersion":"20.10.0","_hasShrinkwrap":false,"devDependencies":{"gulp":"^4.0.2","gulp-concat":"^2.6.1","gulp-uglify":"^3.0.2","gulp-postcss":"^10.0.0","gulp-clean-css":"^4.3.0","postcss-nested":"^7.0.2","gulp-autoprefixer":"^8.0.0"},"_npmOperationalInternal":{"tmp":"tmp/maplibre-layerlibre_2.2.0_1771519348574_0.12362517853331778","host":"s3://npm-registry-packages-npm-production"}},"2.2.1":{"name":"@drecchia/maplibre-layerlibre","version":"2.2.1","author":{"name":"Danilo T Recchia"},"main":"dist/js/all.min.js","license":"CC-BY-NC-4.0","repository":{"type":"git","url":"git+https://github.com/drecchia/maplibre-layerlibre.git"},"description":"MapLibre LayerLibre — a compact layer-switcher control for MapLibre GL JS with deck.gl overlay support.","keywords":["layer","control","maplibre","vanillajs"],"scripts":{"build":"gulp default"},"devDependencies":{"gulp":"^4.0.2","gulp-autoprefixer":"^8.0.0","gulp-clean-css":"^4.3.0","gulp-concat":"^2.6.1","gulp-postcss":"^10.0.0","gulp-uglify":"^3.0.2","postcss-nested":"^7.0.2"},"_id":"@drecchia/maplibre-layerlibre@2.2.1","gitHead":"8a112a66d7eb7600180d5c5cccba3190a9b26401","bugs":{"url":"https://github.com/drecchia/maplibre-layerlibre/issues"},"homepage":"https://github.com/drecchia/maplibre-layerlibre#readme","_nodeVersion":"20.10.0","_npmVersion":"10.2.3","dist":{"integrity":"sha512-rhlseGqdW6LV/vaZewyszLCKtwQ1g/H9hnGSKbyh+qHz4PQ7kP/qlT+zHM7wydseCnrXqTEYY+UjDE2fMMyNlA==","shasum":"92a94d1d93c0ff1948ffac7d54c90c096005b43b","tarball":"https://registry.npmjs.org/@drecchia/maplibre-layerlibre/-/maplibre-layerlibre-2.2.1.tgz","fileCount":4,"unpackedSize":54947,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCOTOfT3ByX3BlhH+bL0ChfEMa75GK01zac5dSWUJMTugIgDAYj5BJ3qMOfYFU0LRKI8RBlTrYx0yQuLyA8YdXTxVg="}]},"_npmUser":{"name":"drecchia","email":"ciscomp@gmail.com"},"directories":{},"maintainers":[{"name":"drecchia","email":"ciscomp@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/maplibre-layerlibre_2.2.1_1771522460731_0.8037610126800292"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-19T16:42:28.450Z","modified":"2026-02-19T17:34:21.038Z","2.2.0":"2026-02-19T16:42:28.721Z","2.2.1":"2026-02-19T17:34:20.896Z"},"bugs":{"url":"https://github.com/drecchia/maplibre-layerlibre/issues"},"author":{"name":"Danilo T Recchia"},"license":"CC-BY-NC-4.0","homepage":"https://github.com/drecchia/maplibre-layerlibre#readme","keywords":["layer","control","maplibre","vanillajs"],"repository":{"type":"git","url":"git+https://github.com/drecchia/maplibre-layerlibre.git"},"description":"MapLibre LayerLibre — a compact layer-switcher control for MapLibre GL JS with deck.gl overlay support.","maintainers":[{"name":"drecchia","email":"ciscomp@gmail.com"}],"readme":"# maplibre-layerlibre\n\nA compact layer-switcher control for [MapLibre GL JS](https://maplibre.org/) with [deck.gl](https://deck.gl/) overlay support.\n\n![UI interaction](docs/img01.gif)\n![Dynamic loader](docs/img02.gif)\n\n---\n\n## Features\n\n- **Base map switching** — radio-button selector, `setStyle` strategy\n- **deck.gl overlays** — static `deckLayers` or lazy-loaded via `onChecked` callback\n- **Overlay groups** — group-level visibility toggle and opacity control\n- **Per-overlay opacity sliders** and status indicators (loading / error / zoom-filtered)\n- **Viewport targeting** — `fitBounds`, `center+zoom`, `bearing`, `pitch` applied on activation\n- **Forced base layer** — overlay can require a specific base style before activating\n- **State persistence** — base, overlays, opacity, viewport saved to `localStorage`\n- **Zoom filtering** — overlays hidden automatically outside `minZoomLevel`/`maxZoomLevel`\n- **Event-driven API** — all state changes emit typed events\n- **Dark theme + responsive** — CSS media queries included\n\n---\n\n## Installation\n\nLoad MapLibre GL JS, deck.gl, and the LayersControl bundle from CDN, then build with `npm run build` to produce `dist/js/all.min.js` and `dist/css/all.css`.\n\n```html\n<link href=\"https://unpkg.com/maplibre-gl@4.1.1/dist/maplibre-gl.css\" rel=\"stylesheet\">\n<link href=\"dist/css/all.css\" rel=\"stylesheet\">\n\n<script src=\"https://unpkg.com/maplibre-gl@4.1.1/dist/maplibre-gl.js\"></script>\n<script src=\"https://cdn.jsdelivr.net/npm/deck.gl@9.1.14/dist.min.js\"></script>\n<script src=\"dist/js/all.min.js\"></script>\n```\n\nBuild the bundle:\n\n```bash\nnpm install\nnpm run build   # → dist/js/all.min.js + dist/css/all.css\n```\n\n---\n\n## Quick Start\n\nAll classes (`EventEmitter`, `StateService`, `MapService`, `UIService`, `BusinessLogicService`, `LayersControl`, `BoundsHelper`) are globals exposed by the bundle.\n\n```html\n<div id=\"map\"></div>\n<script>\nconst baseStyles = [\n  {\n    id: 'osm',\n    label: 'OpenStreetMap',\n    style: 'https://demotiles.maplibre.org/style.json',\n    strategy: 'setStyle'\n  }\n];\n\nconst overlays = [\n  {\n    id: 'cities',\n    label: 'Major Cities',\n    deckLayers: [\n      {\n        id: 'cities-layer',\n        type: 'ScatterplotLayer',\n        props: {\n          data: [\n            { position: [-74.0, 40.7], name: 'New York' },\n            { position: [-87.6, 41.9], name: 'Chicago' },\n            { position: [-118.2, 34.0], name: 'Los Angeles' }\n          ],\n          getPosition: d => d.position,\n          getRadius: 20000,\n          getFillColor: [255, 100, 0],\n          pickable: true\n        }\n      }\n    ],\n    tooltip: 'name',\n    defaultVisible: true,\n    opacityControls: true\n  }\n];\n\n// ── Instantiate services ───────────────────────────────────────────────────\nconst eventEmitter         = new EventEmitter();\nconst stateService         = new StateService(eventEmitter, 'my-app-layers'); // localStorage key\nconst mapService           = new MapService(eventEmitter);\nconst uiService            = new UIService(stateService, mapService, eventEmitter);\nconst businessLogicService = new BusinessLogicService(stateService, eventEmitter);\n\nconst layersControl = new LayersControl(\n  { baseStyles, overlays, defaultBaseId: 'osm' },\n  { stateService, uiService, mapService, businessLogicService, eventEmitter }\n);\n\n// ── Create map and add control ─────────────────────────────────────────────\nconst map = new maplibregl.Map({\n  container: 'map',\n  style: baseStyles[0].style,\n  center: [-95, 40],\n  zoom: 3\n});\n\nmap.addControl(layersControl, 'top-left');\n\n// ── Subscribe to events ────────────────────────────────────────────────────\nlayersControl\n  .on('basechange',    e => console.log('base →', e.id))\n  .on('overlaychange', e => console.log('overlay →', e.id, e.visible))\n  .on('error',         e => console.error('error →', e.id, e.error));\n</script>\n```\n\n---\n\n## Dynamic Overlays (`onChecked`)\n\nUse `onChecked` to load data lazily — only when the user first activates the overlay:\n\n```js\n{\n  id: 'live-data',\n  label: 'Live Data',\n  onChecked: async (context) => {\n    if (context.getCache()) return;           // skip if already loaded\n\n    const data = await fetch('/api/data').then(r => r.json());\n\n    context.setOverlayConfig({\n      deckLayers: [{\n        id: 'live-layer',\n        type: 'ScatterplotLayer',\n        props: { data, getPosition: d => d.position, getRadius: 5000, getFillColor: [0, 180, 255], pickable: true }\n      }]\n    });\n\n    context.setCache({ loaded: true });       // prevent re-fetch\n  },\n  tooltip: 'name',\n  defaultVisible: false\n}\n```\n\nThe `loading`, `success`, and `error` events fire automatically. An in-UI retry button appears on failure.\n\n---\n\n## Viewport Targeting\n\nFit the map to specific bounds when an overlay is activated:\n\n```js\n{\n  id: 'usa-cities',\n  label: 'USA Cities',\n  viewport: {\n    fitBounds: BoundsHelper.calculateBounds(usaData.map(d => d.position))\n  },\n  deckLayers: [...]\n}\n```\n\nOr pan to a specific location:\n\n```js\nviewport: { center: [-74.0, 40.7], zoom: 10, bearing: 45, pitch: 30 }\n```\n\n---\n\n## Overlay Groups\n\n```js\nconst groups  = [{ id: 'transport', label: 'Transport' }];\nconst overlays = [\n  { id: 'roads',   label: 'Roads',   group: 'transport', deckLayers: [...] },\n  { id: 'transit', label: 'Transit', group: 'transport', deckLayers: [...] }\n];\n```\n\nThe group header shows an all-at-once visibility toggle and (optionally) a shared opacity slider.\n\n---\n\n## Runtime API\n\n```js\n// Base layers\nlayersControl.setBaseLayer('satellite');\nlayersControl.addBaseStyle({ id: 'terrain', label: 'Terrain', style: '...', strategy: 'setStyle' });\n\n// Overlays\nlayersControl.addOverlay({ id: 'new', label: 'New Layer', deckLayers: [...] });\nlayersControl.showOverlay('my-overlay');\nlayersControl.hideOverlay('my-overlay');\nlayersControl.setOverlayOpacity('my-overlay', 0.5);\nlayersControl.removeOverlay('my-overlay');\n\n// Groups\nlayersControl.showGroup('transport');\nlayersControl.setGroupOpacity('transport', 0.7);\n\n// Persistence\nlayersControl.clearPersistedData();\n\n// State\nconst state = layersControl.getCurrentState();\n```\n\n---\n\n## Configuration Options\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `baseStyles` | `Array` | required | Base map styles |\n| `overlays` | `Array` | required | Overlay definitions |\n| `groups` | `Array` | `[]` | Group metadata |\n| `defaultBaseId` | `string` | `null` | Initial active base style |\n| `showOpacity` | `boolean` | `true` | Show opacity sliders |\n| `autoClose` | `boolean` | `false` | Close panel after base selection |\n| `icon` | `string` | `'☰'` | Toggle button icon |\n| `i18n` | `object` | see docs | UI string overrides `{ baseHeader, overlaysHeader }` |\n\n**Persistence** is configured via `new StateService(eventEmitter, 'your-key')` — the second argument is the `localStorage` key.\n\n**Control position** is set via `map.addControl(layersControl, 'top-left')` (standard MapLibre API).\n\n---\n\n## Events\n\n| Event | Payload | When |\n|-------|---------|------|\n| `basechange` | `{ id }` | Active base style changed |\n| `overlaychange` | `{ id, visible, opacity }` | Overlay visibility or opacity changed |\n| `overlaygroupchange` | `{ id, visible }` | Group visibility changed |\n| `loading` | `{ id }` | `onChecked` callback started |\n| `success` | `{ id }` | `onChecked` completed |\n| `error` | `{ id, error }` | Activation failed |\n| `styleload` | `{ baseId }` | Base style finished loading |\n| `zoomfilter` | `{ id, filtered }` | Overlay shown/hidden by zoom |\n| `viewportchange` | `{ center, zoom, ... }` | Viewport saved |\n| `memorycleared` | `{}` | localStorage cleared |\n\n---\n\n## Documentation\n\n| Doc | Contents |\n|-----|----------|\n| [docs/QUICKSTART.md](docs/QUICKSTART.md) | Setup guide and minimal examples |\n| [docs/CONFIGURATION.md](docs/CONFIGURATION.md) | Full options reference |\n| [docs/API_REFERENCE.md](docs/API_REFERENCE.md) | All public methods and return values |\n| [docs/ONECHECKED.md](docs/ONECHECKED.md) | `onChecked` dynamic overlay contract |\n| [docs/EVENTS.md](docs/EVENTS.md) | Event payloads and subscription patterns |\n| [docs/CSS.md](docs/CSS.md) | BEM class reference and customization |\n| [docs/WORKFLOWS.md](docs/WORKFLOWS.md) | Runtime flows and lifecycle |\n| [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | Internal service design |\n\n---\n\n## Browser Support\n\nAny modern browser supporting ES2020+. No bundler required — the library is a concatenated, minified global script.\n\n---\n\n## License\n\n[CC-BY-NC-4.0](https://creativecommons.org/licenses/by-nc/4.0/) — non-commercial use only.\n","readmeFilename":"README.md"}