add zone, add popup address, add popup zone, add remove marker, add close popup

This commit is contained in:
David Nguyen 2025-10-07 16:25:24 -04:00
parent db3ec6cddd
commit 849a6f07a0
Signed by: david.nguyen
GPG Key ID: D5FB5A5715829326
10 changed files with 658 additions and 297 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

99
CLAUDE.md Normal file
View File

@ -0,0 +1,99 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
`@svrnty/ngx-open-map-wrapper` is an Angular 16+ library that provides a unified map component with automatic fallback between MapLibre GL (WebGL vector maps) and Leaflet (raster maps). The library automatically detects WebGL support and chooses the optimal rendering engine.
## Architecture
### Adapter Pattern
The library uses the **Adapter pattern** to abstract away differences between MapLibre GL and Leaflet:
- **`IMapAdapter`** (map-adapter.interface.d.ts): Common interface defining map operations
- **`LibreAdapter`** (libre-adapter.d.ts): MapLibre GL implementation
- **`LeafletAdapter`** (leaflet-adapter.d.ts): Leaflet implementation
- **`MapFacade`** (map-facade.d.ts): Facade that selects the appropriate adapter based on WebGL support and user preferences
### Component Structure
- **`OpenMapComponent`** (open-map.component.d.ts): Main Angular component that:
- Uses `WebglDetectionDirective` to detect WebGL support
- Initializes `MapFacade` with the appropriate adapter
- Exposes a unified API via input signals and output events
- Emits `mapReady` event with the `MapFacade` instance
- **`WebglDetectionDirective`** (webgl-detection.directive.d.ts): Directive that detects WebGL support on the user's device and emits the result
### Key Interfaces
**MapOptions**:
- `center: LatLng` - [lat, lng] tuple
- `zoom: number`
- `styleUrl: string` - MapLibre style URL
- `tileUrl: string` - Tile server URL for Leaflet
**Zone** (for delivery/shipping zones):
- `id: string`
- `name?: string`
- `color?: string`
- `opacity?: number`
- `polygon: GeoPoint[]` - Array of {x, y} points
- `shippingFee?: number`
- `deliverySchedule?: string`
**Address** (for popup display):
- `line1, line2, postalCode, subdivision, city, country`
- `shippingFee?: number`
- `deliverySchedule?: string`
### Core APIs
Both adapters implement:
- `init(container, options)` - Initialize map
- `setCenter(latLng)` - Pan to location
- `setZoom(zoom)` - Set zoom level
- `addMarker(latLng, options?)` - Add marker with optional id/color
- `removeMarker(id?)` - Remove specific or all markers
- `addZone(zones)` - Add delivery zones as polygons
- `updateZone(zones)` - Update existing zones
- `openPopup(address)` - Show address popup
- `closePopup()` - Hide popup
- `on(type, event)` - Event listener
- `destroy()` - Cleanup
### Zoom Level Handling
The facade includes a `leafletZoomOffset` property because MapLibre GL and Leaflet use different zoom level scales. When using Leaflet, the facade adjusts zoom levels to maintain visual consistency.
## Development
This is a **compiled Angular library** (not a workspace with source code). The repository contains:
- `/lib` - TypeScript declaration files (.d.ts)
- `/fesm2022` - Compiled ES modules
- `package.json` - Library metadata and peer dependencies
**Peer Dependencies**:
- `@angular/common` and `@angular/core`: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
- `maplibre-gl`: ^5.0.0
- `leaflet`: ^1.0.0 || ^2.0.0
**Installation**:
```bash
yarn add ngx-open-open-map-wrapper leaflet maplibre-gl
```
**CSS Imports Required**:
```css
@import "leaflet/dist/leaflet.css";
@import "maplibre-gl/dist/maplibre-gl.css";
```
## Recent Changes (Current Branch: feature/zone)
Based on git status and recent commits:
- Added `addZone()` and `updateZone()` methods to `IMapAdapter` interface
- Both adapters now support zone management (delivery/shipping zones as polygons)
- Zone objects include shipping fees and delivery schedules for e-commerce use cases

View File

@ -1,4 +1,4 @@
import { Map, NavigationControl, Marker, GeoJSONSource } from 'maplibre-gl'; import {Map, NavigationControl, Marker, Popup} from 'maplibre-gl';
import { Map as Map$1, TileLayer, Marker as Marker$1 } from 'leaflet'; import { Map as Map$1, TileLayer, Marker as Marker$1 } from 'leaflet';
import * as i0 from '@angular/core'; import * as i0 from '@angular/core';
import { inject, PLATFORM_ID, output, Directive, Injector, input, runInInjectionContext, effect, ViewChild, Component } from '@angular/core'; import { inject, PLATFORM_ID, output, Directive, Injector, input, runInInjectionContext, effect, ViewChild, Component } from '@angular/core';
@ -6,263 +6,486 @@ import { isPlatformBrowser } from '@angular/common';
import {LatLng} from "leaflet/src/geo/index.js"; import {LatLng} from "leaflet/src/geo/index.js";
function getLngLat(latLng) { function getLngLat(latLng) {
return [latLng[1], latLng[0]]; return [latLng[1], latLng[0]];
} }
class LibreAdapter { class LibreAdapter {
map; map;
init(container, options) { deliveryCheckMarker;
this.map = new Map({ popup;
container, markers = {};
style: options.styleUrl, init(container, options) {
center: getLngLat(options.center), this.map = new Map({
zoom: options.zoom, container,
}); style: options.styleUrl,
this.map.addControl(new NavigationControl(), 'top-right'); center: getLngLat(options.center),
zoom: options.zoom,
});
this.map.addControl(new NavigationControl(), 'top-right');
}
setCenter(latLng) {
this.map.setCenter(getLngLat(latLng));
}
setZoom(zoom) {
this.map.setZoom(zoom);
}
addMarker(latLng, options) {
const coords = getLngLat(latLng);
if (options.id) {
if (this.markers[options.id]) {
this.markers[options.id].remove();
}
const marker = new Marker({ color: options.color || 'red' })
.setLngLat(coords)
.addTo(this.map);
this.markers[options.id] = marker;
} else {
if (this.deliveryCheckMarker) {
this.deliveryCheckMarker.remove();
}
this.deliveryCheckMarker = new Marker({ color: options?.color || 'red' })
.setLngLat(coords)
.addTo(this.map);
} }
setCenter(latLng) { }
this.map.setCenter(getLngLat(latLng)); removeMarker(id) {
if (id) {
if (this.markers[id]) {
this.markers[id].remove();
delete this.markers[id];
}
} }
setZoom(zoom) { else
this.map.setZoom(zoom); {
if (this.deliveryCheckMarker)
{
this.deliveryCheckMarker.remove();
this.deliveryCheckMarker = undefined;
}
} }
addMarker(latLng, options) { }
new Marker({ color: options?.color || 'red' }) destroy() {
.setLngLat(getLngLat(latLng)) this.map.remove();
.addTo(this.map); }
addZone(zones) {
this.updateZone(zones);
}
updateZone(zones) {
const features = zones.map((zone) => {
const coords = zone.polygon.map((pt) => [pt.y, pt.x]);
if (coords[0][0] !== coords[coords.length - 1][0] || coords[0][1] !== coords[coords.length - 1][1]) {
coords.push(coords[0]);
}
return {
type: 'Feature',
properties: {
id: zone.id,
name: zone.name,
color: zone.color ? zone.color : '#ff0000',
opacity: zone.opacity ? zone.opacity : 0.4,
},
geometry: {
type: 'Polygon',
coordinates: [coords]
},
};
});
const geojson = {
type: 'FeatureCollection',
features,
};
if (this.map.getSource("zones")) {
(this.map.getSource("zones").setData(geojson));
} }
destroy() { else {
this.map.remove(); this.map.addSource("zones", {
type: "geojson",
data: geojson,
});
this.map.addLayer({
id: "zones-fill",
type: "fill",
source: "zones",
paint: {
"fill-color": ["get", "color"],
"fill-opacity": ["get", "opacity"],
},
});
this.map.addLayer({
id: "zones-outline",
type: "line",
source: "zones",
paint: {
"line-color": ["get", "color"],
"line-width": 2,
},
});
} }
addZone(zones) { }
this.updateZone(zones); openAddressPopup(address)
} {
updateZone(zones) { const bounds = this.map.getBounds();
const features = zones.map((zone) => { const corner = bounds.getNorthWest();
const coords = zone.polygon.map((pt) => [pt.y, pt.x]); this.closePopup();
this.popup = new Popup({
closeButton: false,
closeOnClick: false,
})
.setLngLat([corner.lng, corner.lat])
.setHTML(`
<div class="delivery-address">
<p>${address.line1}</p>
${address.line2 ? `<p>${address.line2}</p>` : ''}
<p>${address.city}, ${address.postalCode}</p>
<p>${address.subdivision}, ${address.country}</p>
${address.shippingFee ? `<p>Shipping ${address.shippingFee}</p>` : ''}
${address.deliverySchedule ? `<p>Delivery time estimate ${address.deliverySchedule}</p>` : ''}
</div>`)
.addTo(this.map);
if (coords[0][0] !== coords[coords.length - 1][0] || coords[0][1] !== coords[coords.length - 1][1]) { this.map.on('move', () => {
coords.push(coords[0]); if (this.popup) {
} const newBounds = this.map.getBounds();
const newCorner = newBounds.getNorthWest();
this.popup.setLngLat([newCorner.lng, newCorner.lat]);
}
});
}
openZonePopup(zone)
{
let sumLng = 0;
let sumLat = 0;
zone.polygon.forEach(point => {
sumLng += point.y;
sumLat += point.x;
});
const centerLng = sumLng / zone.polygon.length;
const centerLat = sumLat / zone.polygon.length;
return { this.closePopup();
type: 'Feature', this.popup = new Popup({
properties: { closeButton: true,
id: zone.id, closeOnClick: false,
name: zone.name, })
color: zone.color ? `#${zone.color}` : '#ff0000', .setLngLat([centerLng, centerLat])
}, .setHTML(`
geometry: { <div class="delivery-zone">
type: 'Polygon', ${zone.name ? `<p><strong>${zone.name}</strong></p>` : ''}
coordinates: [coords] ${zone.shippingFee ? `<p>Shipping: ${zone.shippingFee}$</p>` : ''}
}, ${zone.deliverySchedule ? `<p>Delivery: ${zone.deliverySchedule}</p>` : ''}
}; </div>`)
}); .addTo(this.map);
}
const geojson = { closePopup()
type: 'FeatureCollection', {
features, if (this.popup) {
}; this.popup.remove();
this.popup = undefined;
if (this.map.getSource("delivery-zones")) {
(this.map.getSource("delivery-zones").setData(geojson));
}
else {
this.map.addSource("delivery-zones", {
type: "geojson",
data: geojson,
});
this.map.addLayer({
id: "delivery-zones-fill",
type: "fill",
source: "delivery-zones",
paint: {
"fill-color": ["get", "color"],
"fill-opacity": 0.4,
},
});
this.map.addLayer({
id: "delivery-zones-outline",
type: "line",
source: "delivery-zones",
paint: {
"line-color": ["get", "color"],
"line-width": 2,
},
});
}
} }
}
on(type, event,){
this.map.on(type, event);
}
} }
class LeafletAdapter { class LeafletAdapter {
map; map;
init(container, options) { deliveryCheckMarker;
this.map = new Map$1(container).setView(options.center, options.zoom); markers = {};
new TileLayer(options.tileUrl, { popup;
attribution: '&copy; OpenStreetMap contributors', init(container, options) {
}).addTo(this.map); this.map = new Map$1(container).setView(options.center, options.zoom);
} new TileLayer(options.tileUrl, {
setCenter(latLng) { attribution: '&copy; OpenStreetMap contributors',
this.map.setView(latLng, this.map.getZoom()); }).addTo(this.map);
TileLayer.map }
} setCenter(latLng) {
setZoom(zoom) { this.map.setView(latLng, this.map.getZoom());
this.map.setZoom(zoom); TileLayer.map
} }
addMarker(latLng, options) { setZoom(zoom) {
const marker = new Marker$1(latLng); this.map.setZoom(zoom);
}
addMarker(latLng, options) {
if (options.id) {
if (this.markers[options.id]) {
this.map.removeLayer(this.markers[options.id]);
}
const marker = new Marker$1(latLng)
marker.addTo(this.map); marker.addTo(this.map);
this.markers[options.id] = marker;
} else {
if (this.deliveryCheckMarker) {
this.map.removeLayer(this.deliveryCheckMarker);
}
this.deliveryCheckMarker = new Marker$1(latLng).addTo(this.map);
} }
destroy() { }
this.map.remove();
removeMarker(id) {
if (id) {
if (this.markers[id]) {
this.map.removeLayer(this.markers[id]);
delete this.markers[id];
}
} else {
if (this.deliveryCheckMarker) {
this.map.removeLayer(this.deliveryCheckMarker);
this.deliveryCheckMarker = undefined;
}
} }
addZone(zones) { }
this.updateZone(zones); destroy() {
this.map.remove();
}
addZone(zones) {
this.updateZone(zones);
}
updateZone(zones) {
for(let zone of zones)
{
const latlngs = zone.polygon.map((geoPoint) => {
return new LatLng(geoPoint.x, geoPoint.y);
});
let color = "#ff0000"
let opacity = 0.4;
if(zone.color)
color = zone.color;
if (zone.opacity)
opacity = zone.opacity;
const polygon = L.polygon(latlngs, { color, fillOpacity: opacity })
.addTo(this.map);
} }
updateZone(zones) { }
for(let zone of zones) openAddressPopup(address)
{ {
const polygon = zone.polygon.map((geoPoint) => { const bounds = this.map.getBounds();
return new LatLng(geoPoint.x, geoPoint.y); const corner = bounds.getNorthEast();
});
let color = "red"
if(zone.color)
color = `#${zone.color}`;
this.map.polygon.push(L.polygon(polygon, { if (this.popup) {
color this.map.removeLayer(this.popup);
})
.addTo(this.map));
}
} }
this.popup = new Marker$1(corner, {
opacity: 0
})
.addTo(this.map)
.bindTooltip(`
<div class="delivery-address">
<p>${address.line1}</p>
${address.line2 ? `<p>${address.line2}</p>` : ''}
<p>${address.city}, ${address.postalCode}</p>
<p>${address.subdivision}, ${address.country}</p>
${address.shippingFee ? `<p>Shipping ${address.shippingFee}</p>` : ''}
${address.deliverySchedule ? `<p>Delivery time estimate ${address.deliverySchedule}</p>` : ''}
</div>`,
{
permanent: true,
offset: [-38, 135]
})
.openTooltip();
this.map.on('move', () => {
if (this.popup) {
const newBounds = this.map.getBounds();
const newCorner = newBounds.getNorthEast();
this.popup.setLatLng(newCorner);
}
});
}
openZonePopup(zone)
{
let sumLat = 0;
let sumLng = 0;
zone.polygon.forEach(point => {
sumLat += point.x;
sumLng += point.y;
});
const centerLat = sumLat / zone.polygon.length;
const centerLng = sumLng / zone.polygon.length;
if (this.popup) {
this.map.removeLayer(this.popup);
}
this.popup = new Marker$1([centerLat, centerLng], {
opacity: 0
})
.addTo(this.map)
.bindTooltip(`
<div class="delivery-zone">
${zone.name ? `<p><strong>${zone.name}</strong></p>` : ''}
${zone.shippingFee ? `<p>Shipping: ${zone.shippingFee}</p>` : ''}
${zone.deliverySchedule ? `<p>Delivery: ${zone.deliverySchedule}</p>` : ''}
</div>`,
{
permanent: true,
offset: [0, 0]
})
.openTooltip();
}
closePopup()
{
if (this.popup) {
this.map.removeLayer(this.popup);
this.popup = undefined;
}
}
on(type, event) {
this.map.on(type, event);
}
} }
class MapFacade { class MapFacade {
adapter; adapter;
leafletZoomOffset = 1; leafletZoomOffset = 1;
constructor(forceRaster, webglAvailable) { constructor(forceRaster, webglAvailable) {
if (forceRaster || !webglAvailable) { if (forceRaster || !webglAvailable) {
this.adapter = new LeafletAdapter(); this.adapter = new LeafletAdapter();
}
else {
this.adapter = new LibreAdapter();
}
} }
init(container, options) { else {
if (this.adapter instanceof LeafletAdapter) this.adapter = new LibreAdapter();
options.zoom += this.leafletZoomOffset;
this.adapter.init(container, options);
}
setCenter(latLng) {
this.adapter.setCenter(latLng);
}
setZoom(zoom) {
if (this.adapter instanceof LeafletAdapter)
zoom += this.leafletZoomOffset;
this.adapter.setZoom(zoom);
}
addMarker(latLng, options) {
this.adapter.addMarker(latLng, options);
}
destroy() {
this.adapter.destroy();
}
addZone(deliveryZones) {
this.adapter.addZone(deliveryZones);
}
updateZone(deliveryZones) {
this.adapter.updateZone(deliveryZones);
} }
}
init(container, options) {
if (this.adapter instanceof LeafletAdapter)
options.zoom += this.leafletZoomOffset;
this.adapter.init(container, options);
}
setCenter(latLng) {
this.adapter.setCenter(latLng);
}
setZoom(zoom) {
if (this.adapter instanceof LeafletAdapter)
zoom += this.leafletZoomOffset;
this.adapter.setZoom(zoom);
}
addMarker(latLng, options) {
this.adapter.addMarker(latLng, options);
}
removeMarker() {
this.adapter.removeMarker();
}
destroy() {
this.adapter.destroy();
}
addZone(zones) {
this.adapter.addZone(zones);
}
updateZone(zones) {
this.adapter.updateZone(zones);
}
openAddressPopup(deliveryAddress) {
this.adapter.openAddressPopup(deliveryAddress);
}
openZonePopup(zone) {
this.adapter.openZonePopup(zone);
}
closePopup() {
this.adapter.closePopup();
}
on(type, event,){
this.adapter.on(type, event);
}
} }
class WebglDetectionDirective { class WebglDetectionDirective {
platformId = inject(PLATFORM_ID); platformId = inject(PLATFORM_ID);
webglSupport = output(); webglSupport = output();
ngOnInit() { ngOnInit() {
if (!isPlatformBrowser(this.platformId)) if (!isPlatformBrowser(this.platformId))
return; return;
const supported = this.checkWebGLSupport(); const supported = this.checkWebGLSupport();
this.webglSupport.emit(supported); this.webglSupport.emit(supported);
}
checkWebGLSupport() {
let canvas = undefined;
try {
canvas = document.createElement('canvas');
const gl = (canvas.getContext('webgl') ||
canvas.getContext('experimental-webgl'));
const supported = !!window.WebGLRenderingContext &&
!!(gl);
return supported;
} }
checkWebGLSupport() { catch {
let canvas = undefined; return false;
try {
canvas = document.createElement('canvas');
const gl = (canvas.getContext('webgl') ||
canvas.getContext('experimental-webgl'));
const supported = !!window.WebGLRenderingContext &&
!!(gl);
return supported;
}
catch {
return false;
}
finally {
canvas?.remove();
}
} }
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WebglDetectionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); finally {
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: WebglDetectionDirective, isStandalone: true, selector: "[webglDetection]", outputs: { webglSupport: "webglSupport" }, ngImport: i0 }); canvas?.remove();
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WebglDetectionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: WebglDetectionDirective, isStandalone: true, selector: "[webglDetection]", outputs: { webglSupport: "webglSupport" }, ngImport: i0 });
} }
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WebglDetectionDirective, decorators: [{ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WebglDetectionDirective, decorators: [{
type: Directive, type: Directive,
args: [{ args: [{
selector: '[webglDetection]', selector: '[webglDetection]',
standalone: true standalone: true
}] }]
}] }); }] });
class OpenMapComponent { class OpenMapComponent {
platformId = inject(PLATFORM_ID); platformId = inject(PLATFORM_ID);
injector = inject(Injector); injector = inject(Injector);
webglSupported; webglSupported;
map; map;
mapContainer; mapContainer;
options = input({ options = input({
center: [50.426606229502525, 30.56308375468811], center: [50.426606229502525, 30.56308375468811],
zoom: 6, zoom: 6,
styleUrl: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json', styleUrl: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
tileUrl: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', tileUrl: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
forceRaster: false, forceRaster: false,
});
mapReady = output();
ngAfterViewInit() {
if (false === isPlatformBrowser(this.platformId))
return;
runInInjectionContext(this.injector, () => {
effect(() => {
if (undefined === this.webglSupported)
return;
if (!this.map)
this.initializeMap();
});
}); });
mapReady = output(); }
ngAfterViewInit() { webglDetection(supported) {
if (false === isPlatformBrowser(this.platformId)) this.webglSupported = supported;
return; if (undefined === this.map)
runInInjectionContext(this.injector, () => { this.initializeMap();
effect(() => { }
if (undefined === this.webglSupported) initializeMap() {
return; const options = this.options();
if (!this.map) this.map = new MapFacade(options.forceRaster, this.webglSupported);
this.initializeMap(); this.map.init(this.mapContainer.nativeElement, options);
}); this.mapReady.emit(this.map);
}); }
} static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: OpenMapComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
webglDetection(supported) { static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.14", type: OpenMapComponent, isStandalone: true, selector: "open-map", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { mapReady: "mapReady" }, viewQueries: [{ propertyName: "mapContainer", first: true, predicate: ["mapContainer"], descendants: true, static: true }], ngImport: i0, template: "<div #mapContainer class=\"map-container\" webglDetection (webglSupport)=\"webglDetection($event)\">\n\n</div>\n", styles: [".map-container{width:100%;height:100%}\n"], dependencies: [{ kind: "directive", type: WebglDetectionDirective, selector: "[webglDetection]", outputs: ["webglSupport"] }] });
this.webglSupported = supported;
if (undefined === this.map)
this.initializeMap();
}
initializeMap() {
const options = this.options();
this.map = new MapFacade(options.forceRaster, this.webglSupported);
this.map.init(this.mapContainer.nativeElement, options);
this.mapReady.emit(this.map);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: OpenMapComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.14", type: OpenMapComponent, isStandalone: true, selector: "open-map", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { mapReady: "mapReady" }, viewQueries: [{ propertyName: "mapContainer", first: true, predicate: ["mapContainer"], descendants: true, static: true }], ngImport: i0, template: "<div #mapContainer class=\"map-container\" webglDetection (webglSupport)=\"webglDetection($event)\">\n\n</div>\n", styles: [".map-container{width:100%;height:100%}\n"], dependencies: [{ kind: "directive", type: WebglDetectionDirective, selector: "[webglDetection]", outputs: ["webglSupport"] }] });
} }
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: OpenMapComponent, decorators: [{ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: OpenMapComponent, decorators: [{
type: Component, type: Component,
args: [{ selector: 'open-map', imports: [ args: [{ selector: 'open-map', imports: [
WebglDetectionDirective WebglDetectionDirective
], standalone: true, template: "<div #mapContainer class=\"map-container\" webglDetection (webglSupport)=\"webglDetection($event)\">\n\n</div>\n", styles: [".map-container{width:100%;height:100%}\n"] }] ], standalone: true, template: "<div #mapContainer class=\"map-container\" webglDetection (webglSupport)=\"webglDetection($event)\">\n\n</div>\n", styles: [".map-container{width:100%;height:100%}\n"] }]
}], propDecorators: { mapContainer: [{ }], propDecorators: { mapContainer: [{
type: ViewChild, type: ViewChild,
args: ['mapContainer', { static: true }] args: ['mapContainer', { static: true }]
}] } }); }] } });
/* /*
* Public API Surface of ngx-open-open-map-wrapper * Public API Surface of ngx-open-open-map-wrapper

View File

@ -1,13 +1,19 @@
import {DeliveryZone, IMapAdapter, LatLng, MapOptions} from './map-adapter.interface'; import {Zone, IMapAdapter, LatLng, MapOptions, Address} from './map-adapter.interface';
export declare class LeafletAdapter implements IMapAdapter { export declare class LeafletAdapter implements IMapAdapter {
private map; private map;
init(container: HTMLElement, options: MapOptions): void; private deliveryCheckMarker?;
setCenter(latLng: LatLng): void; private markers?: {};
setZoom(zoom: number): void; private popup?;
addMarker(latLng: LatLng, options?: { init(container: HTMLElement, options: MapOptions): void;
color?: string; setCenter(latLng: LatLng): void;
}): void; setZoom(zoom: number): void;
destroy(): void; addMarker(latLng: LatLng, options?: { id?: string; color?: string }): void;
addZone(zone: DeliveryZone[]): void; removeMarker(id?: string): void;
updateZone(one: DeliveryZone[]): void; destroy(): void;
addZone(zone: Zone[]): void;
updateZone(zone: Zone[]): void;
openZonePopup(zone: Zone) : void
openAddressPopup(address: Address): void;
closePopup(): void;
on(type: string, event: (e: any) => void): void;
} }

View File

@ -1,13 +1,19 @@
import {DeliveryZone, IMapAdapter, LatLng, MapOptions} from './map-adapter.interface'; import {Address, IMapAdapter, LatLng, MapOptions, Zone} from './map-adapter.interface';
export declare class LibreAdapter implements IMapAdapter { export declare class LibreAdapter implements IMapAdapter {
private map; private map;
init(container: HTMLElement, options: MapOptions): void; private deliveryCheckMarker?;
setCenter(latLng: LatLng): void; private markers?: {};
setZoom(zoom: number): void; private popup?;
addMarker(latLng: LatLng, options?: { init(container: HTMLElement, options: MapOptions): void;
color?: string; setCenter(latLng: LatLng): void;
}): void; setZoom(zoom: number): void;
destroy(): void; addMarker(latLng: LatLng, options?: { id?: string; color?: string }): void;
addZone(zone: DeliveryZone[]): void; removeMarker(id?: string): void;
updateZone(one: DeliveryZone[]): void; destroy(): void;
addZone(zone: Zone[]): void;
updateZone(zone: Zone[]): void;
openZonePopup(zone: Zone) : void
openAddressPopup(address: Address): void;
closePopup(): void;
on(type: string, event: (e: any) => void): void;
} }

View File

@ -1,32 +1,51 @@
import * as maplibregl from 'maplibre-gl';
import * as L from 'leaflet';
export interface MapOptions { export interface MapOptions {
center: LatLng; center: LatLng;
zoom: number; zoom: number;
styleUrl: string; styleUrl: string;
tileUrl: string; tileUrl: string;
} }
export interface GeoPoint { export interface GeoPoint {
x: number; x: number;
y: number; y: number;
} }
export interface DeliveryZone { export interface Zone {
id: string; id: string;
name?: string; name?: string;
color?: string; color?: string;
polygon: GeoPoint[]; opacity?: number;
polygon: GeoPoint[];
shippingFee?: number;
deliverySchedule?: string;
}
export interface Address {
line1: string;
line2?: string;
postalCode: string;
subdivision: string;
city: string;
country: string;
shippingFee?: number;
deliverySchedule?: string;
} }
export type LatLng = [number, number]; export type LatLng = [number, number];
export declare function getLngLat(latLng: LatLng): [number, number]; export declare function getLngLat(latLng: LatLng): [number, number];
export interface IMapAdapter { export interface IMapAdapter {
init(container: HTMLElement, options: MapOptions): void; init(container: HTMLElement, options: MapOptions): void;
setCenter(latLng: LatLng): void; setCenter(latLng: LatLng): void;
setZoom(zoom: number): void; setZoom(zoom: number): void;
addMarker(latLng: LatLng, options?: { addMarker(latLng: LatLng, options?: { id?: string; color?: string }): void;
color?: string; removeMarker(id?: string): void;
}): void; destroy(): void;
destroy(): void; addZone(zone: Zone[]): void;
addZone(zone: DeliveryZone[]): void; updateZone(zone: Zone[]): void;
updateZone(one: DeliveryZone[]): void; openZonePopup(zone: Zone) : void
openAddressPopup(address: Address): void;
closePopup(): void;
on(
type: string,
event: (e: any) => void,
): void;
} }

View File

@ -1,15 +1,18 @@
import {IMapAdapter, MapOptions, LatLng, DeliveryZone} from './map-adapter.interface'; import {IMapAdapter, MapOptions, LatLng, Zone, Address} from './map-adapter.interface';
export declare class MapFacade implements IMapAdapter { export declare class MapFacade implements IMapAdapter {
private readonly adapter; private readonly adapter;
private readonly leafletZoomOffset; private readonly leafletZoomOffset;
constructor(forceRaster: boolean, webglAvailable: boolean); constructor(forceRaster: boolean, webglAvailable: boolean);
init(container: HTMLElement, options: MapOptions): void; init(container: HTMLElement, options: MapOptions): void;
setCenter(latLng: LatLng): void; setCenter(latLng: LatLng): void;
setZoom(zoom: number): void; setZoom(zoom: number): void;
addMarker(latLng: LatLng, options?: { addMarker(latLng: LatLng, options?: { id?: string; color?: string }): void;
color?: string; removeMarker(id?: string): void;
}): void; destroy(): void;
destroy(): void; addZone(zone: Zone[]): void;
addZone(zone: DeliveryZone[]): void; updateZone(zone: Zone[]): void;
updateZone(one: DeliveryZone[]): void; openZonePopup(zone: Zone) : void
openAddressPopup(address: Address): void;
closePopup(): void;
on(type: string, event: (e: any) => void): void;
} }

View File

@ -3,19 +3,19 @@ import { MapOptions } from '../../adapters/map-adapter.interface';
import { MapFacade } from '../../adapters/map-facade'; import { MapFacade } from '../../adapters/map-facade';
import * as i0 from "@angular/core"; import * as i0 from "@angular/core";
export interface OpenMapOptions extends MapOptions { export interface OpenMapOptions extends MapOptions {
forceRaster: boolean; forceRaster: boolean;
} }
export declare class OpenMapComponent implements AfterViewInit { export declare class OpenMapComponent implements AfterViewInit {
private readonly platformId; private readonly platformId;
private readonly injector; private readonly injector;
webglSupported?: boolean; webglSupported?: boolean;
map?: MapFacade; map?: MapFacade;
mapContainer: ElementRef<HTMLDivElement>; mapContainer: ElementRef<HTMLDivElement>;
options: import("@angular/core").InputSignal<OpenMapOptions>; options: import("@angular/core").InputSignal<OpenMapOptions>;
mapReady: import("@angular/core").OutputEmitterRef<MapFacade>; mapReady: import("@angular/core").OutputEmitterRef<MapFacade>;
ngAfterViewInit(): void; ngAfterViewInit(): void;
webglDetection(supported: boolean): void; webglDetection(supported: boolean): void;
private initializeMap; private initializeMap;
static ɵfac: i0.ɵɵFactoryDeclaration<OpenMapComponent, never>; static ɵfac: i0.ɵɵFactoryDeclaration<OpenMapComponent, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<OpenMapComponent, "open-map", never, { "options": { "alias": "options"; "required": false; "isSignal": true; }; }, { "mapReady": "mapReady"; }, never, never, true, never>; static ɵcmp: i0.ɵɵComponentDeclaration<OpenMapComponent, "open-map", never, { "options": { "alias": "options"; "required": false; "isSignal": true; }; }, { "mapReady": "mapReady"; }, never, never, true, never>;
} }

View File

@ -1,10 +1,10 @@
import { OnInit } from '@angular/core'; import { OnInit } from '@angular/core';
import * as i0 from "@angular/core"; import * as i0 from "@angular/core";
export declare class WebglDetectionDirective implements OnInit { export declare class WebglDetectionDirective implements OnInit {
private readonly platformId; private readonly platformId;
webglSupport: import("@angular/core").OutputEmitterRef<boolean>; webglSupport: import("@angular/core").OutputEmitterRef<boolean>;
ngOnInit(): void; ngOnInit(): void;
private checkWebGLSupport; private checkWebGLSupport;
static ɵfac: i0.ɵɵFactoryDeclaration<WebglDetectionDirective, never>; static ɵfac: i0.ɵɵFactoryDeclaration<WebglDetectionDirective, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<WebglDetectionDirective, "[webglDetection]", never, {}, { "webglSupport": "webglSupport"; }, never, never, true, never>; static ɵdir: i0.ɵɵDirectiveDeclaration<WebglDetectionDirective, "[webglDetection]", never, {}, { "webglSupport": "webglSupport"; }, never, never, true, never>;
} }