@symfony/ux-leaflet-map 2.23.0

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.
@@ -0,0 +1,42 @@
1
+ import AbstractMapController from '@symfony/ux-map';
2
+ import type { InfoWindowWithoutPositionDefinition, MarkerDefinition, Point, PolygonDefinition, PolylineDefinition } from '@symfony/ux-map';
3
+ import 'leaflet/dist/leaflet.min.css';
4
+ import * as L from 'leaflet';
5
+ import type { MapOptions as LeafletMapOptions, MarkerOptions, PolylineOptions as PolygonOptions, PolylineOptions, PopupOptions } from 'leaflet';
6
+ type MapOptions = Pick<LeafletMapOptions, 'center' | 'zoom'> & {
7
+ tileLayer: {
8
+ url: string;
9
+ attribution: string;
10
+ options: Record<string, unknown>;
11
+ };
12
+ };
13
+ export default class extends AbstractMapController<MapOptions, L.Map, MarkerOptions, L.Marker, PopupOptions, L.Popup, PolygonOptions, L.Polygon, PolylineOptions, L.Polyline> {
14
+ map: L.Map;
15
+ connect(): void;
16
+ centerValueChanged(): void;
17
+ zoomValueChanged(): void;
18
+ protected dispatchEvent(name: string, payload?: Record<string, unknown>): void;
19
+ protected doCreateMap({ center, zoom, options, }: {
20
+ center: Point | null;
21
+ zoom: number | null;
22
+ options: MapOptions;
23
+ }): L.Map;
24
+ protected doCreateMarker({ definition }: {
25
+ definition: MarkerDefinition<MarkerOptions, PopupOptions>;
26
+ }): L.Marker;
27
+ protected doRemoveMarker(marker: L.Marker): void;
28
+ protected doCreatePolygon({ definition, }: {
29
+ definition: PolygonDefinition<PolygonOptions, PopupOptions>;
30
+ }): L.Polygon;
31
+ protected doRemovePolygon(polygon: L.Polygon): void;
32
+ protected doCreatePolyline({ definition, }: {
33
+ definition: PolylineDefinition<PolylineOptions, PopupOptions>;
34
+ }): L.Polyline;
35
+ protected doRemovePolyline(polyline: L.Polyline): void;
36
+ protected doCreateInfoWindow({ definition, element, }: {
37
+ definition: InfoWindowWithoutPositionDefinition<PopupOptions>;
38
+ element: L.Marker | L.Polygon | L.Polyline;
39
+ }): L.Popup;
40
+ protected doFitBoundsToMarkers(): void;
41
+ }
42
+ export {};
@@ -0,0 +1,213 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+ import 'leaflet/dist/leaflet.min.css';
3
+ import * as L from 'leaflet';
4
+
5
+ class default_1 extends Controller {
6
+ constructor() {
7
+ super(...arguments);
8
+ this.markers = new Map();
9
+ this.polygons = new Map();
10
+ this.polylines = new Map();
11
+ this.infoWindows = [];
12
+ this.isConnected = false;
13
+ }
14
+ connect() {
15
+ const options = this.optionsValue;
16
+ this.dispatchEvent('pre-connect', { options });
17
+ this.createMarker = this.createDrawingFactory('marker', this.markers, this.doCreateMarker.bind(this));
18
+ this.createPolygon = this.createDrawingFactory('polygon', this.polygons, this.doCreatePolygon.bind(this));
19
+ this.createPolyline = this.createDrawingFactory('polyline', this.polylines, this.doCreatePolyline.bind(this));
20
+ this.map = this.doCreateMap({
21
+ center: this.hasCenterValue ? this.centerValue : null,
22
+ zoom: this.hasZoomValue ? this.zoomValue : null,
23
+ options,
24
+ });
25
+ this.markersValue.forEach((definition) => this.createMarker({ definition }));
26
+ this.polygonsValue.forEach((definition) => this.createPolygon({ definition }));
27
+ this.polylinesValue.forEach((definition) => this.createPolyline({ definition }));
28
+ if (this.fitBoundsToMarkersValue) {
29
+ this.doFitBoundsToMarkers();
30
+ }
31
+ this.dispatchEvent('connect', {
32
+ map: this.map,
33
+ markers: [...this.markers.values()],
34
+ polygons: [...this.polygons.values()],
35
+ polylines: [...this.polylines.values()],
36
+ infoWindows: this.infoWindows,
37
+ });
38
+ this.isConnected = true;
39
+ }
40
+ createInfoWindow({ definition, element, }) {
41
+ this.dispatchEvent('info-window:before-create', { definition, element });
42
+ const infoWindow = this.doCreateInfoWindow({ definition, element });
43
+ this.dispatchEvent('info-window:after-create', { infoWindow, element });
44
+ this.infoWindows.push(infoWindow);
45
+ return infoWindow;
46
+ }
47
+ markersValueChanged() {
48
+ if (!this.isConnected) {
49
+ return;
50
+ }
51
+ this.onDrawChanged(this.markers, this.markersValue, this.createMarker, this.doRemoveMarker);
52
+ if (this.fitBoundsToMarkersValue) {
53
+ this.doFitBoundsToMarkers();
54
+ }
55
+ }
56
+ polygonsValueChanged() {
57
+ if (!this.isConnected) {
58
+ return;
59
+ }
60
+ this.onDrawChanged(this.polygons, this.polygonsValue, this.createPolygon, this.doRemovePolygon);
61
+ }
62
+ polylinesValueChanged() {
63
+ if (!this.isConnected) {
64
+ return;
65
+ }
66
+ this.onDrawChanged(this.polylines, this.polylinesValue, this.createPolyline, this.doRemovePolyline);
67
+ }
68
+ createDrawingFactory(type, draws, factory) {
69
+ const eventBefore = `${type}:before-create`;
70
+ const eventAfter = `${type}:after-create`;
71
+ return ({ definition }) => {
72
+ this.dispatchEvent(eventBefore, { definition });
73
+ const drawing = factory({ definition });
74
+ this.dispatchEvent(eventAfter, { [type]: drawing });
75
+ draws.set(definition['@id'], drawing);
76
+ return drawing;
77
+ };
78
+ }
79
+ onDrawChanged(draws, newDrawDefinitions, factory, remover) {
80
+ const idsToRemove = new Set(draws.keys());
81
+ newDrawDefinitions.forEach((definition) => {
82
+ idsToRemove.delete(definition['@id']);
83
+ });
84
+ idsToRemove.forEach((id) => {
85
+ const draw = draws.get(id);
86
+ remover(draw);
87
+ draws.delete(id);
88
+ });
89
+ newDrawDefinitions.forEach((definition) => {
90
+ if (!draws.has(definition['@id'])) {
91
+ factory({ definition });
92
+ }
93
+ });
94
+ }
95
+ }
96
+ default_1.values = {
97
+ providerOptions: Object,
98
+ center: Object,
99
+ zoom: Number,
100
+ fitBoundsToMarkers: Boolean,
101
+ markers: Array,
102
+ polygons: Array,
103
+ polylines: Array,
104
+ options: Object,
105
+ };
106
+
107
+ class map_controller extends default_1 {
108
+ connect() {
109
+ L.Marker.prototype.options.icon = L.divIcon({
110
+ html: '<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" fill-rule="evenodd" stroke-linecap="round" clip-rule="evenodd" viewBox="0 0 500 820"><defs><linearGradient id="__sf_ux_map_gradient_marker_fill" x1="0" x2="1" y1="0" y2="0" gradientTransform="matrix(0 -37.57 37.57 0 416.45 541)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#126FC6"/><stop offset="1" stop-color="#4C9CD1"/></linearGradient><linearGradient id="__sf_ux_map_gradient_marker_border" x1="0" x2="1" y1="0" y2="0" gradientTransform="matrix(0 -19.05 19.05 0 414.48 522.49)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#2E6C97"/><stop offset="1" stop-color="#3883B7"/></linearGradient></defs><circle cx="252.31" cy="266.24" r="83.99" fill="#fff"/><path fill="url(#__sf_ux_map_gradient_marker_fill)" stroke="url(#__sf_ux_map_gradient_marker_border)" stroke-width="1.1" d="M416.54 503.61c-6.57 0-12.04 5.7-12.04 11.87 0 2.78 1.56 6.3 2.7 8.74l9.3 17.88 9.26-17.88c1.13-2.43 2.74-5.79 2.74-8.74 0-6.18-5.38-11.87-11.96-11.87Zm0 7.16a4.69 4.69 0 1 1-.02 9.4 4.69 4.69 0 0 1 .02-9.4Z" transform="translate(-7889.1 -9807.44) scale(19.54)"/></svg>',
111
+ iconSize: [25, 41],
112
+ iconAnchor: [12.5, 41],
113
+ popupAnchor: [0, -41],
114
+ className: '',
115
+ });
116
+ super.connect();
117
+ }
118
+ centerValueChanged() {
119
+ if (this.map && this.hasCenterValue && this.centerValue && this.hasZoomValue && this.zoomValue) {
120
+ this.map.setView(this.centerValue, this.zoomValue);
121
+ }
122
+ }
123
+ zoomValueChanged() {
124
+ if (this.map && this.hasZoomValue && this.zoomValue) {
125
+ this.map.setZoom(this.zoomValue);
126
+ }
127
+ }
128
+ dispatchEvent(name, payload = {}) {
129
+ this.dispatch(name, {
130
+ prefix: 'ux:map',
131
+ detail: {
132
+ ...payload,
133
+ L,
134
+ },
135
+ });
136
+ }
137
+ doCreateMap({ center, zoom, options, }) {
138
+ const map = L.map(this.element, {
139
+ ...options,
140
+ center: center === null ? undefined : center,
141
+ zoom: zoom === null ? undefined : zoom,
142
+ });
143
+ L.tileLayer(options.tileLayer.url, {
144
+ attribution: options.tileLayer.attribution,
145
+ ...options.tileLayer.options,
146
+ }).addTo(map);
147
+ return map;
148
+ }
149
+ doCreateMarker({ definition }) {
150
+ const { '@id': _id, position, title, infoWindow, extra, rawOptions = {}, ...otherOptions } = definition;
151
+ const marker = L.marker(position, { title: title || undefined, ...otherOptions, ...rawOptions }).addTo(this.map);
152
+ if (infoWindow) {
153
+ this.createInfoWindow({ definition: infoWindow, element: marker });
154
+ }
155
+ return marker;
156
+ }
157
+ doRemoveMarker(marker) {
158
+ marker.remove();
159
+ }
160
+ doCreatePolygon({ definition, }) {
161
+ const { '@id': _id, points, title, infoWindow, rawOptions = {} } = definition;
162
+ const polygon = L.polygon(points, { ...rawOptions }).addTo(this.map);
163
+ if (title) {
164
+ polygon.bindPopup(title);
165
+ }
166
+ if (infoWindow) {
167
+ this.createInfoWindow({ definition: infoWindow, element: polygon });
168
+ }
169
+ return polygon;
170
+ }
171
+ doRemovePolygon(polygon) {
172
+ polygon.remove();
173
+ }
174
+ doCreatePolyline({ definition, }) {
175
+ const { '@id': _id, points, title, infoWindow, rawOptions = {} } = definition;
176
+ const polyline = L.polyline(points, { ...rawOptions }).addTo(this.map);
177
+ if (title) {
178
+ polyline.bindPopup(title);
179
+ }
180
+ if (infoWindow) {
181
+ this.createInfoWindow({ definition: infoWindow, element: polyline });
182
+ }
183
+ return polyline;
184
+ }
185
+ doRemovePolyline(polyline) {
186
+ polyline.remove();
187
+ }
188
+ doCreateInfoWindow({ definition, element, }) {
189
+ const { headerContent, content, rawOptions = {}, ...otherOptions } = definition;
190
+ element.bindPopup([headerContent, content].filter((x) => x).join('<br>'), { ...otherOptions, ...rawOptions });
191
+ if (definition.opened) {
192
+ element.openPopup();
193
+ }
194
+ const popup = element.getPopup();
195
+ if (!popup) {
196
+ throw new Error('Unable to get the Popup associated with the element.');
197
+ }
198
+ return popup;
199
+ }
200
+ doFitBoundsToMarkers() {
201
+ if (this.markers.size === 0) {
202
+ return;
203
+ }
204
+ const bounds = [];
205
+ this.markers.forEach((marker) => {
206
+ const position = marker.getLatLng();
207
+ bounds.push([position.lat, position.lng]);
208
+ });
209
+ this.map.fitBounds(bounds);
210
+ }
211
+ }
212
+
213
+ export { map_controller as default };
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@symfony/ux-leaflet-map",
3
+ "description": "Leaflet bridge for Symfony UX Map, integrate interactive maps in your Symfony applications",
4
+ "license": "MIT",
5
+ "version": "2.23.0",
6
+ "keywords": [
7
+ "symfony-ux",
8
+ "leaflet",
9
+ "map"
10
+ ],
11
+ "homepage": "https://ux.symfony.com/map",
12
+ "repository": "https://github.com/symfony/ux-leaflet-map",
13
+ "type": "module",
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "main": "dist/map_controller.js",
18
+ "types": "dist/map_controller.d.ts",
19
+ "scripts": {
20
+ "build": "node ../../../../../../bin/build_package.js .",
21
+ "watch": "node ../../../../../../bin/build_package.js . --watch",
22
+ "test": "../../../../../../bin/test_package.sh .",
23
+ "check": "biome check",
24
+ "ci": "biome ci"
25
+ },
26
+ "symfony": {
27
+ "controllers": {
28
+ "map": {
29
+ "main": "dist/map_controller.js",
30
+ "webpackMode": "lazy",
31
+ "fetch": "lazy",
32
+ "enabled": true
33
+ }
34
+ },
35
+ "importmap": {
36
+ "@hotwired/stimulus": "^3.0.0",
37
+ "leaflet": "^1.9.4",
38
+ "@symfony/ux-leaflet-map": "path:%PACKAGE%/dist/map_controller.js"
39
+ }
40
+ },
41
+ "peerDependencies": {
42
+ "@hotwired/stimulus": "^3.0.0",
43
+ "leaflet": "^1.9.4"
44
+ },
45
+ "peerDependenciesMeta": {
46
+ "leaflet": {
47
+ "optional": false
48
+ }
49
+ },
50
+ "devDependencies": {
51
+ "@hotwired/stimulus": "^3.0.0",
52
+ "@symfony/ux-map": "2.23.0",
53
+ "@types/leaflet": "^1.9.12",
54
+ "leaflet": "^1.9.4"
55
+ }
56
+ }