Skip to content

Commit c97965e

Browse files
committed
[Map] Create Map component
1 parent fed00e2 commit c97965e

File tree

90 files changed

+5334
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+5334
-0
lines changed

src/Map/.gitattributes

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/.gitattributes export-ignore
2+
/.gitignore export-ignore
3+
/.symfony.bundle.yaml export-ignore
4+
/phpunit.xml.dist export-ignore
5+
/assets/src export-ignore
6+
/assets/test export-ignore
7+
/tests export-ignore

src/Map/.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
vendor
2+
composer.lock
3+
.php_cs.cache
4+
.phpunit.result.cache

src/Map/.symfony.bundle.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
branches: ["2.x"]
2+
maintained_branches: ["2.x"]
3+
doc_dir: "doc"

src/Map/CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# CHANGELOG
2+
3+
## 2.20.0
4+
5+
- Component added

src/Map/LICENSE

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2023-present Fabien Potencier
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

src/Map/README.md

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Symfony UX Map
2+
3+
**EXPERIMENTAL** This component is currently experimental and is
4+
likely to change, or even change drastically.
5+
6+
Symfony UX Map integrates [Symfony Translation](https://symfony.com/doc/current/translation.html) for JavaScript.
7+
8+
**This repository is a READ-ONLY sub-tree split**. See
9+
https://github.com/symfony/ux to create issues or submit pull requests.
10+
11+
## Resources
12+
13+
- [Documentation](https://symfony.com/bundles/ux-map/current/index.html)
14+
- [Report issues](https://github.com/symfony/ux/issues) and
15+
[send Pull Requests](https://github.com/symfony/ux/pulls)
16+
in the [main Symfony UX repository](https://github.com/symfony/ux)
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
declare global {
2+
interface Window {
3+
__symfony_ux_maps?: {
4+
providers?: {
5+
google_maps?: {
6+
key: string;
7+
};
8+
leaflet?: {};
9+
};
10+
};
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/// <reference types="google.maps" />
2+
import { Controller } from '@hotwired/stimulus';
3+
type MarkerId = number;
4+
export default class extends Controller<HTMLElement> {
5+
static values: {
6+
view: ObjectConstructor;
7+
};
8+
viewValue: {
9+
mapId: string | null;
10+
center: null | {
11+
lat: number;
12+
lng: number;
13+
};
14+
zoom: number;
15+
gestureHandling: string;
16+
backgroundColor: string;
17+
disableDoubleClickZoom: boolean;
18+
zoomControl: boolean;
19+
zoomControlOptions: google.maps.ZoomControlOptions;
20+
mapTypeControl: boolean;
21+
mapTypeControlOptions: google.maps.MapTypeControlOptions;
22+
streetViewControl: boolean;
23+
streetViewControlOptions: google.maps.StreetViewControlOptions;
24+
fullscreenControl: boolean;
25+
fullscreenControlOptions: google.maps.FullscreenControlOptions;
26+
markers: Array<{
27+
_id: MarkerId;
28+
position: {
29+
lat: number;
30+
lng: number;
31+
};
32+
title: string | null;
33+
}>;
34+
infoWindows: Array<{
35+
headerContent: string | null;
36+
content: string | null;
37+
position: {
38+
lat: number;
39+
lng: number;
40+
};
41+
opened: boolean;
42+
_markerId: MarkerId | null;
43+
autoClose: boolean;
44+
}>;
45+
fitBoundsToMarkers: boolean;
46+
};
47+
private loader;
48+
private map;
49+
private markers;
50+
private infoWindows;
51+
initialize(): void;
52+
connect(): Promise<void>;
53+
private createTextOrElement;
54+
private closeInfoWindowsExcept;
55+
private dispatchEvent;
56+
}
57+
export {};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { Controller } from '@hotwired/stimulus';
2+
import { Loader } from '@googlemaps/js-api-loader';
3+
4+
class default_1 extends Controller {
5+
constructor() {
6+
super(...arguments);
7+
this.markers = new Map();
8+
this.infoWindows = [];
9+
}
10+
initialize() {
11+
var _a;
12+
const providerConfig = (_a = window.__symfony_ux_maps.providers) === null || _a === void 0 ? void 0 : _a.google_maps;
13+
if (!providerConfig) {
14+
throw new Error('Google Maps provider configuration is missing, did you forget to call `{{ ux_map_script_tags() }}`?');
15+
}
16+
const loaderOptions = {
17+
apiKey: providerConfig.key,
18+
};
19+
this.dispatchEvent('init', {
20+
loaderOptions,
21+
});
22+
this.loader = new Loader(loaderOptions);
23+
}
24+
async connect() {
25+
const { Map: GoogleMap, InfoWindow } = await this.loader.importLibrary('maps');
26+
const mapOptions = {
27+
gestureHandling: this.viewValue.gestureHandling,
28+
backgroundColor: this.viewValue.backgroundColor,
29+
disableDoubleClickZoom: this.viewValue.disableDoubleClickZoom,
30+
zoomControl: this.viewValue.zoomControl,
31+
zoomControlOptions: this.viewValue.zoomControlOptions,
32+
mapTypeControl: this.viewValue.mapTypeControl,
33+
mapTypeControlOptions: this.viewValue.mapTypeControlOptions,
34+
streetViewControl: this.viewValue.streetViewControl,
35+
streetViewControlOptions: this.viewValue.streetViewControlOptions,
36+
fullscreenControl: this.viewValue.fullscreenControl,
37+
fullscreenControlOptions: this.viewValue.fullscreenControlOptions,
38+
};
39+
if (this.viewValue.mapId) {
40+
mapOptions.mapId = this.viewValue.mapId;
41+
}
42+
if (this.viewValue.center) {
43+
mapOptions.center = this.viewValue.center;
44+
}
45+
if (this.viewValue.zoom) {
46+
mapOptions.zoom = this.viewValue.zoom;
47+
}
48+
this.dispatchEvent('pre-connect', {
49+
mapOptions,
50+
});
51+
this.map = new GoogleMap(this.element, mapOptions);
52+
if (this.viewValue.markers) {
53+
const { AdvancedMarkerElement } = await this.loader.importLibrary('marker');
54+
this.viewValue.markers.forEach((markerConfiguration) => {
55+
const marker = new AdvancedMarkerElement({
56+
position: markerConfiguration.position,
57+
title: markerConfiguration.title,
58+
map: this.map,
59+
});
60+
this.markers.set(markerConfiguration._id, marker);
61+
});
62+
if (this.viewValue.fitBoundsToMarkers) {
63+
const bounds = new google.maps.LatLngBounds();
64+
this.markers.forEach((marker) => {
65+
if (!marker.position) {
66+
return;
67+
}
68+
bounds.extend(marker.position);
69+
});
70+
this.map.fitBounds(bounds);
71+
}
72+
}
73+
this.viewValue.infoWindows.forEach((infoWindowConfiguration) => {
74+
const marker = infoWindowConfiguration._markerId
75+
? this.markers.get(infoWindowConfiguration._markerId)
76+
: undefined;
77+
const infoWindow = new InfoWindow({
78+
headerContent: this.createTextOrElement(infoWindowConfiguration.headerContent),
79+
content: this.createTextOrElement(infoWindowConfiguration.content),
80+
position: infoWindowConfiguration.position,
81+
});
82+
this.infoWindows.push(infoWindow);
83+
if (infoWindowConfiguration.opened) {
84+
infoWindow.open({
85+
map: this.map,
86+
shouldFocus: false,
87+
anchor: marker,
88+
});
89+
}
90+
if (marker) {
91+
marker.addListener('click', () => {
92+
if (infoWindowConfiguration.autoClose) {
93+
this.closeInfoWindowsExcept(infoWindow);
94+
}
95+
infoWindow.open({
96+
map: this.map,
97+
anchor: marker,
98+
});
99+
});
100+
}
101+
});
102+
this.dispatchEvent('connect', {
103+
map: this.map,
104+
markers: this.markers,
105+
infoWindows: this.infoWindows,
106+
});
107+
}
108+
createTextOrElement(content) {
109+
if (!content) {
110+
return null;
111+
}
112+
if (content.includes('<')) {
113+
const div = document.createElement('div');
114+
div.innerHTML = content;
115+
return div;
116+
}
117+
return content;
118+
}
119+
closeInfoWindowsExcept(infoWindow) {
120+
this.infoWindows.forEach((otherInfoWindow) => {
121+
if (otherInfoWindow !== infoWindow) {
122+
otherInfoWindow.close();
123+
}
124+
});
125+
}
126+
dispatchEvent(name, payload) {
127+
this.dispatch(name, { detail: payload, prefix: 'google-maps' });
128+
}
129+
}
130+
default_1.values = {
131+
view: Object,
132+
};
133+
134+
export { default_1 as default };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { Controller } from '@hotwired/stimulus';
2+
import 'leaflet/dist/leaflet.min.css';
3+
import type { MarkerOptions } from 'leaflet';
4+
type MarkerId = number;
5+
export default class extends Controller<HTMLElement> {
6+
static values: {
7+
view: ObjectConstructor;
8+
};
9+
viewValue: {
10+
center: null | {
11+
lat: number;
12+
lng: number;
13+
};
14+
zoom: number | null;
15+
tileLayer: {
16+
url: string;
17+
attribution: string;
18+
} & Record<string, unknown>;
19+
fitBoundsToMarkers: boolean;
20+
markers: Array<{
21+
_id: MarkerId;
22+
position: {
23+
lat: number;
24+
lng: number;
25+
};
26+
} & MarkerOptions>;
27+
popups: Array<{
28+
_markerId: MarkerId | null;
29+
content: string;
30+
position: {
31+
lat: number;
32+
lng: number;
33+
};
34+
opened: boolean;
35+
autoClose: boolean;
36+
}>;
37+
};
38+
private map;
39+
private markers;
40+
private popups;
41+
connect(): void;
42+
private setupTileLayer;
43+
private dispatchEvent;
44+
}
45+
export {};

0 commit comments

Comments
 (0)