initial commit
This commit is contained in:
commit
67c94197e1
2
.npmignore
Normal file
2
.npmignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Nested package.json's are only needed for development.
|
||||||
|
**/package.json
|
120
README.md
Normal file
120
README.md
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
# ngx-open-map-wrapper
|
||||||
|
|
||||||
|
An Angular 16+ library that provides a **unified map component** with automatic fallback between **MapLibre GL** (WebGL vector maps) and **Leaflet** (raster maps).
|
||||||
|
|
||||||
|
It automatically chooses the best renderer for the user’s device:
|
||||||
|
- **MapLibre GL** if WebGL is supported
|
||||||
|
- **Leaflet** if WebGL is not available or raster rendering is forced
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
- 🗺️ Always shows a map — automatically picks the right engine for the device
|
||||||
|
- ⚡ Uses **MapLibre GL** for modern devices with WebGL support
|
||||||
|
- 🪶 Falls back to **Leaflet** for older or low‑end devices
|
||||||
|
- 🎯 Unified API for markers, zoom, and center — no need to learn two libraries
|
||||||
|
- 📱 Works seamlessly across desktop and mobile
|
||||||
|
- 🧩 Angular‑native: standalone components, signals, and modern syntax
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 Installation
|
||||||
|
|
||||||
|
Install the library and its peer dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn add ngx-open-open-map-wrapper leaflet maplibre-gl
|
||||||
|
```
|
||||||
|
|
||||||
|
Then import the required CSS styles in your global `styles.css` (or `styles.scss`):
|
||||||
|
|
||||||
|
```css
|
||||||
|
@import "leaflet/dist/leaflet.css";
|
||||||
|
@import "maplibre-gl/dist/maplibre-gl.css";
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Usage
|
||||||
|
|
||||||
|
### Import the component
|
||||||
|
|
||||||
|
Since the library is **standalone‑ready**, you can import the component directly:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { MapComponent, MapFacade } from 'ngx-open-open-map-wrapper';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-root',
|
||||||
|
standalone: true,
|
||||||
|
imports: [MapComponent],
|
||||||
|
template: `
|
||||||
|
<open-map
|
||||||
|
[center]="[46.3385, -72.6106]"
|
||||||
|
[zoom]="14"
|
||||||
|
(mapReady)="onMapReady($event)"
|
||||||
|
></open-map>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
export class AppComponent {
|
||||||
|
private map?: MapFacade;
|
||||||
|
|
||||||
|
onMapReady(facade: MapFacade) {
|
||||||
|
this.map = facade;
|
||||||
|
this.map.addMarker([46.3385, -72.6106], { color: 'blue' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ API
|
||||||
|
|
||||||
|
### `MapComponent` Inputs
|
||||||
|
- `center: [lat, lng]` → Initial map center
|
||||||
|
- `zoom: number` → Initial zoom level
|
||||||
|
- `forceRaster: boolean` → Force Leaflet raster mode even if WebGL is available
|
||||||
|
- `styleUrl: string` → MapLibre style URL (default: MapLibre basic style)
|
||||||
|
|
||||||
|
### `MapComponent` Outputs
|
||||||
|
- `(mapReady: MapFacade)` → Emits the `MapFacade` instance once the map is initialized
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `MapFacade` Methods
|
||||||
|
- `setCenter([lat, lng])` → Move the map center
|
||||||
|
- `setZoom(zoom: number)` → Set zoom level
|
||||||
|
- `addMarker([lat, lng], { color?: string })` → Add a marker
|
||||||
|
- `destroy()` → Clean up map instance
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧩 WebGL Detection Directive
|
||||||
|
|
||||||
|
You can also use the directive directly if you want to check WebGL support:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div webglDetection (webglSupport)="onWebGLCheck($event)">
|
||||||
|
@if (webglOk) {
|
||||||
|
<my-component-using-webgl></my-component-using-webgl>
|
||||||
|
} @else {
|
||||||
|
<p>WebGL not supported → fallback</p>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts
|
||||||
|
webglOk = false;
|
||||||
|
|
||||||
|
onWebGLCheck(supported: boolean) {
|
||||||
|
this.webglOk = supported;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📜 License
|
||||||
|
|
||||||
|
MIT
|
190
fesm2022/svrnty-ngx-open-map-wrapper.mjs
Normal file
190
fesm2022/svrnty-ngx-open-map-wrapper.mjs
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
import { Map, NavigationControl, Marker } from 'maplibre-gl';
|
||||||
|
import { Map as Map$1, TileLayer, Marker as Marker$1 } from 'leaflet';
|
||||||
|
import * as i0 from '@angular/core';
|
||||||
|
import { inject, PLATFORM_ID, output, Directive, Injector, input, runInInjectionContext, effect, ViewChild, Component } from '@angular/core';
|
||||||
|
import { isPlatformBrowser } from '@angular/common';
|
||||||
|
|
||||||
|
function getLngLat(latLng) {
|
||||||
|
return [latLng[1], latLng[0]];
|
||||||
|
}
|
||||||
|
|
||||||
|
class LibreAdapter {
|
||||||
|
map;
|
||||||
|
init(container, options) {
|
||||||
|
this.map = new Map({
|
||||||
|
container,
|
||||||
|
style: options.styleUrl,
|
||||||
|
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) {
|
||||||
|
new Marker({ color: options?.color || 'red' })
|
||||||
|
.setLngLat(getLngLat(latLng))
|
||||||
|
.addTo(this.map);
|
||||||
|
}
|
||||||
|
destroy() {
|
||||||
|
this.map.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LeafletAdapter {
|
||||||
|
map;
|
||||||
|
init(container, options) {
|
||||||
|
this.map = new Map$1(container).setView(options.center, options.zoom);
|
||||||
|
new TileLayer(options.tileUrl, {
|
||||||
|
attribution: '© OpenStreetMap contributors',
|
||||||
|
}).addTo(this.map);
|
||||||
|
}
|
||||||
|
setCenter(latLng) {
|
||||||
|
this.map.setView(latLng, this.map.getZoom());
|
||||||
|
}
|
||||||
|
setZoom(zoom) {
|
||||||
|
this.map.setZoom(zoom);
|
||||||
|
}
|
||||||
|
addMarker(latLng, options) {
|
||||||
|
const marker = new Marker$1(latLng);
|
||||||
|
marker.addTo(this.map);
|
||||||
|
}
|
||||||
|
destroy() {
|
||||||
|
this.map.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MapFacade {
|
||||||
|
adapter;
|
||||||
|
leafletZoomOffset = 1;
|
||||||
|
constructor(forceRaster, webglAvailable) {
|
||||||
|
if (forceRaster || !webglAvailable) {
|
||||||
|
this.adapter = new LeafletAdapter();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.adapter = new LibreAdapter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
destroy() {
|
||||||
|
this.adapter.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WebglDetectionDirective {
|
||||||
|
platformId = inject(PLATFORM_ID);
|
||||||
|
webglSupport = output();
|
||||||
|
ngOnInit() {
|
||||||
|
if (!isPlatformBrowser(this.platformId))
|
||||||
|
return;
|
||||||
|
const supported = this.checkWebGLSupport();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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 });
|
||||||
|
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: [{
|
||||||
|
type: Directive,
|
||||||
|
args: [{
|
||||||
|
selector: '[webglDetection]',
|
||||||
|
standalone: true
|
||||||
|
}]
|
||||||
|
}] });
|
||||||
|
|
||||||
|
class OpenMapComponent {
|
||||||
|
platformId = inject(PLATFORM_ID);
|
||||||
|
injector = inject(Injector);
|
||||||
|
webglSupported;
|
||||||
|
map;
|
||||||
|
mapContainer;
|
||||||
|
options = input({
|
||||||
|
center: [50.426606229502525, 30.56308375468811],
|
||||||
|
zoom: 6,
|
||||||
|
styleUrl: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
|
||||||
|
tileUrl: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
webglDetection(supported) {
|
||||||
|
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: [{
|
||||||
|
type: Component,
|
||||||
|
args: [{ selector: 'open-map', imports: [
|
||||||
|
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"] }]
|
||||||
|
}], propDecorators: { mapContainer: [{
|
||||||
|
type: ViewChild,
|
||||||
|
args: ['mapContainer', { static: true }]
|
||||||
|
}] } });
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Public API Surface of ngx-open-open-map-wrapper
|
||||||
|
*/
|
||||||
|
// Interfaces & types
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generated bundle index. Do not edit.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { MapFacade, OpenMapComponent, WebglDetectionDirective, getLngLat };
|
||||||
|
//# sourceMappingURL=svrnty-ngx-open-map-wrapper.mjs.map
|
1
fesm2022/svrnty-ngx-open-map-wrapper.mjs.map
Normal file
1
fesm2022/svrnty-ngx-open-map-wrapper.mjs.map
Normal file
File diff suppressed because one or more lines are too long
5
index.d.ts
vendored
Normal file
5
index.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/**
|
||||||
|
* Generated bundle index. Do not edit.
|
||||||
|
*/
|
||||||
|
/// <amd-module name="@svrnty/ngx-open-map-wrapper" />
|
||||||
|
export * from './public-api';
|
11
lib/adapters/leaflet-adapter.d.ts
vendored
Normal file
11
lib/adapters/leaflet-adapter.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { IMapAdapter, LatLng, MapOptions } from './map-adapter.interface';
|
||||||
|
export declare class LeafletAdapter implements IMapAdapter {
|
||||||
|
private map;
|
||||||
|
init(container: HTMLElement, options: MapOptions): void;
|
||||||
|
setCenter(latLng: LatLng): void;
|
||||||
|
setZoom(zoom: number): void;
|
||||||
|
addMarker(latLng: LatLng, options?: {
|
||||||
|
color?: string;
|
||||||
|
}): void;
|
||||||
|
destroy(): void;
|
||||||
|
}
|
11
lib/adapters/libre-adapter.d.ts
vendored
Normal file
11
lib/adapters/libre-adapter.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { IMapAdapter, LatLng, MapOptions } from './map-adapter.interface';
|
||||||
|
export declare class LibreAdapter implements IMapAdapter {
|
||||||
|
private map;
|
||||||
|
init(container: HTMLElement, options: MapOptions): void;
|
||||||
|
setCenter(latLng: LatLng): void;
|
||||||
|
setZoom(zoom: number): void;
|
||||||
|
addMarker(latLng: LatLng, options?: {
|
||||||
|
color?: string;
|
||||||
|
}): void;
|
||||||
|
destroy(): void;
|
||||||
|
}
|
17
lib/adapters/map-adapter.interface.d.ts
vendored
Normal file
17
lib/adapters/map-adapter.interface.d.ts
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
export interface MapOptions {
|
||||||
|
center: LatLng;
|
||||||
|
zoom: number;
|
||||||
|
styleUrl: string;
|
||||||
|
tileUrl: string;
|
||||||
|
}
|
||||||
|
export type LatLng = [number, number];
|
||||||
|
export declare function getLngLat(latLng: LatLng): [number, number];
|
||||||
|
export interface IMapAdapter {
|
||||||
|
init(container: HTMLElement, options: MapOptions): void;
|
||||||
|
setCenter(latLng: LatLng): void;
|
||||||
|
setZoom(zoom: number): void;
|
||||||
|
addMarker(latLng: LatLng, options?: {
|
||||||
|
color?: string;
|
||||||
|
}): void;
|
||||||
|
destroy(): void;
|
||||||
|
}
|
13
lib/adapters/map-facade.d.ts
vendored
Normal file
13
lib/adapters/map-facade.d.ts
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { IMapAdapter, MapOptions, LatLng } from './map-adapter.interface';
|
||||||
|
export declare class MapFacade implements IMapAdapter {
|
||||||
|
private readonly adapter;
|
||||||
|
private readonly leafletZoomOffset;
|
||||||
|
constructor(forceRaster: boolean, webglAvailable: boolean);
|
||||||
|
init(container: HTMLElement, options: MapOptions): void;
|
||||||
|
setCenter(latLng: LatLng): void;
|
||||||
|
setZoom(zoom: number): void;
|
||||||
|
addMarker(latLng: LatLng, options?: {
|
||||||
|
color?: string;
|
||||||
|
}): void;
|
||||||
|
destroy(): void;
|
||||||
|
}
|
21
lib/components/open-map/open-map.component.d.ts
vendored
Normal file
21
lib/components/open-map/open-map.component.d.ts
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { AfterViewInit, ElementRef } from '@angular/core';
|
||||||
|
import { MapOptions } from '../../adapters/map-adapter.interface';
|
||||||
|
import { MapFacade } from '../../adapters/map-facade';
|
||||||
|
import * as i0 from "@angular/core";
|
||||||
|
export interface OpenMapOptions extends MapOptions {
|
||||||
|
forceRaster: boolean;
|
||||||
|
}
|
||||||
|
export declare class OpenMapComponent implements AfterViewInit {
|
||||||
|
private readonly platformId;
|
||||||
|
private readonly injector;
|
||||||
|
webglSupported?: boolean;
|
||||||
|
map?: MapFacade;
|
||||||
|
mapContainer: ElementRef<HTMLDivElement>;
|
||||||
|
options: import("@angular/core").InputSignal<OpenMapOptions>;
|
||||||
|
mapReady: import("@angular/core").OutputEmitterRef<MapFacade>;
|
||||||
|
ngAfterViewInit(): void;
|
||||||
|
webglDetection(supported: boolean): void;
|
||||||
|
private initializeMap;
|
||||||
|
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>;
|
||||||
|
}
|
10
lib/directives/webgl-detection.directive.d.ts
vendored
Normal file
10
lib/directives/webgl-detection.directive.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { OnInit } from '@angular/core';
|
||||||
|
import * as i0 from "@angular/core";
|
||||||
|
export declare class WebglDetectionDirective implements OnInit {
|
||||||
|
private readonly platformId;
|
||||||
|
webglSupport: import("@angular/core").OutputEmitterRef<boolean>;
|
||||||
|
ngOnInit(): void;
|
||||||
|
private checkWebGLSupport;
|
||||||
|
static ɵfac: i0.ɵɵFactoryDeclaration<WebglDetectionDirective, never>;
|
||||||
|
static ɵdir: i0.ɵɵDirectiveDeclaration<WebglDetectionDirective, "[webglDetection]", never, {}, { "webglSupport": "webglSupport"; }, never, never, true, never>;
|
||||||
|
}
|
38
package.json
Normal file
38
package.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "@svrnty/ngx-open-map-wrapper",
|
||||||
|
"version": "0.2.0",
|
||||||
|
"keywords": [
|
||||||
|
"maplibre",
|
||||||
|
"leaflet",
|
||||||
|
"maps",
|
||||||
|
"open source",
|
||||||
|
"angular",
|
||||||
|
"ngx"
|
||||||
|
],
|
||||||
|
"author": "Svrnty",
|
||||||
|
"license": "MIT",
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"@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"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"sideEffects": false,
|
||||||
|
"module": "fesm2022/svrnty-ngx-open-map-wrapper.mjs",
|
||||||
|
"typings": "index.d.ts",
|
||||||
|
"exports": {
|
||||||
|
"./package.json": {
|
||||||
|
"default": "./package.json"
|
||||||
|
},
|
||||||
|
".": {
|
||||||
|
"types": "./index.d.ts",
|
||||||
|
"default": "./fesm2022/svrnty-ngx-open-map-wrapper.mjs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
public-api.d.ts
vendored
Normal file
4
public-api.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export * from './lib/adapters/map-adapter.interface';
|
||||||
|
export * from './lib/adapters/map-facade';
|
||||||
|
export * from './lib/components/open-map/open-map.component';
|
||||||
|
export * from './lib/directives/webgl-detection.directive';
|
Loading…
Reference in New Issue
Block a user