Skip to content

Commit 12cb4b2

Browse files
Kocaljaviereguiluz
authored andcommitted
[Map] Create Map component
0 parents  commit 12cb4b2

33 files changed

+1610
-0
lines changed

.gitattributes

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/Tests export-ignore
2+
/phpunit.xml.dist export-ignore
3+
/.gitattributes export-ignore
4+
/.gitignore export-ignore

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
vendor/
2+
composer.lock
3+
phpunit.xml

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# CHANGELOG
2+
3+
## Unreleased
4+
5+
- Bridge added

LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2024-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.

README.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Symfony UX Map: Google Maps
2+
3+
[Google Maps](https://developers.google.com/maps/documentation/javascript/overview) integration for Symfony UX Map.
4+
5+
## DSN example
6+
7+
```dotenv
8+
UX_MAP_DSN=google://GOOGLE_MAPS_API_KEY@default
9+
10+
# With options
11+
UX_MAP_DSN=google://GOOGLE_MAPS_API_KEY@default?version=weekly
12+
UX_MAP_DSN=google://GOOGLE_MAPS_API_KEY@default?language=fr&region=FR
13+
```
14+
15+
Available options:
16+
17+
| Option | Description | Default |
18+
|------------|------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------|
19+
| `id` | The id of the script tag | `__googleMapsScriptId` |
20+
| `language` | Force language, see [list of supported languages](https://developers.google.com/maps/faq#languagesupport) specified in the browser | The user's preferred language |
21+
| `region` | Unicode region subtag identifiers compatible with [ISO 3166-1](https://en.wikipedia.org/wiki/ISO_3166-1) | |
22+
| `nonce` | Use a cryptographic nonce attribute | |
23+
| `retries` | The number of script load retries | 3 |
24+
| `url` | Custom url to load the Google Maps API script | `https://maps.googleapis.com/maps/api/js` |
25+
| `version` | The release channels or version numbers | `weekly` |
26+
27+
## Map options
28+
29+
You can use the `GoogleOptions` class to configure your `Map`::
30+
31+
```php
32+
use Symfony\UX\Map\Bridge\Google\GoogleOptions;
33+
use Symfony\UX\Map\Bridge\Google\Option\ControlPosition;
34+
use Symfony\UX\Map\Bridge\Google\Option\FullscreenControlOptions;
35+
use Symfony\UX\Map\Bridge\Google\Option\GestureHandling;
36+
use Symfony\UX\Map\Bridge\Google\Option\MapTypeControlOptions;
37+
use Symfony\UX\Map\Bridge\Google\Option\MapTypeControlStyle;
38+
use Symfony\UX\Map\Bridge\Google\Option\StreetViewControlOptions;
39+
use Symfony\UX\Map\Bridge\Google\Option\ZoomControlOptions;
40+
use Symfony\UX\Map\Point;
41+
use Symfony\UX\Map\Map;
42+
43+
$map = (new Map())
44+
->center(new Point(48.8566, 2.3522))
45+
->zoom(6);
46+
47+
// To configure controls options, and some other options:
48+
$googleOptions = (new GoogleOptions())
49+
->mapId('YOUR_MAP_ID')
50+
->gestureHandling(GestureHandling::GREEDY)
51+
->backgroundColor('#f00')
52+
->doubleClickZoom(true)
53+
->zoomControlOptions(new ZoomControlOptions(
54+
position: ControlPosition::BLOCK_START_INLINE_END,
55+
))
56+
->mapTypeControlOptions(new MapTypeControlOptions(
57+
mapTypeIds: ['roadmap'],
58+
position: ControlPosition::INLINE_END_BLOCK_START,
59+
style: MapTypeControlStyle::DROPDOWN_MENU,
60+
))
61+
->streetViewControlOptions(new StreetViewControlOptions(
62+
position: ControlPosition::BLOCK_END_INLINE_START,
63+
))
64+
->fullscreenControlOptions(new FullscreenControlOptions(
65+
position: ControlPosition::INLINE_START_BLOCK_END,
66+
))
67+
;
68+
69+
// To disable controls:
70+
$googleOptions = (new GoogleOptions())
71+
->mapId('YOUR_MAP_ID')
72+
->zoomControl(false)
73+
->mapTypeControl(false)
74+
->streetViewControl(false)
75+
->fullscreenControl(false)
76+
;
77+
78+
// Add the custom options to the map
79+
$map->options($googleOptions);
80+
```
81+
82+
## Resources
83+
84+
- [Documentation](https://symfony.com/bundles/ux-map/current/index.html)
85+
- [Report issues](https://github.com/symfony/ux/issues) and
86+
[send Pull Requests](https://github.com/symfony/ux/pulls)
87+
in the [main Symfony UX repository](https://github.com/symfony/ux)

assets/dist/map_controller.d.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/// <reference types="google.maps" />
2+
import AbstractMapController from '@symfony/ux-map/abstract-map-controller';
3+
import type { Point, MarkerDefinition } from '@symfony/ux-map/abstract-map-controller';
4+
import type { LoaderOptions } from '@googlemaps/js-api-loader';
5+
type MapOptions = Pick<google.maps.MapOptions, 'mapId' | 'gestureHandling' | 'backgroundColor' | 'disableDoubleClickZoom' | 'zoomControl' | 'zoomControlOptions' | 'mapTypeControl' | 'mapTypeControlOptions' | 'streetViewControl' | 'streetViewControlOptions' | 'fullscreenControl' | 'fullscreenControlOptions'>;
6+
export default class extends AbstractMapController<MapOptions, google.maps.Map, google.maps.marker.AdvancedMarkerElement, google.maps.InfoWindow> {
7+
static values: {
8+
providerOptions: ObjectConstructor;
9+
};
10+
providerOptionsValue: Pick<LoaderOptions, 'apiKey' | 'id' | 'language' | 'region' | 'nonce' | 'retries' | 'url' | 'version'>;
11+
connect(): Promise<void>;
12+
protected doCreateMap({ center, zoom, options, }: {
13+
center: Point;
14+
zoom: number;
15+
options: MapOptions;
16+
}): google.maps.Map;
17+
protected doCreateMarker(definition: MarkerDefinition<google.maps.marker.AdvancedMarkerElementOptions, google.maps.InfoWindowOptions>): google.maps.marker.AdvancedMarkerElement;
18+
protected doCreateInfoWindow({ definition, marker, }: {
19+
definition: MarkerDefinition<google.maps.marker.AdvancedMarkerElementOptions, google.maps.InfoWindowOptions>['infoWindow'];
20+
marker: google.maps.marker.AdvancedMarkerElement;
21+
}): google.maps.InfoWindow;
22+
private createTextOrElement;
23+
private closeInfoWindowsExcept;
24+
protected doFitBoundsToMarkers(): void;
25+
}
26+
export {};

assets/dist/map_controller.js

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import AbstractMapController from '@symfony/ux-map/abstract-map-controller';
2+
import { Loader } from '@googlemaps/js-api-loader';
3+
4+
let loader;
5+
let library;
6+
class default_1 extends AbstractMapController {
7+
async connect() {
8+
if (!loader) {
9+
loader = new Loader(this.providerOptionsValue);
10+
}
11+
const { Map: _Map, InfoWindow } = await loader.importLibrary('maps');
12+
const { AdvancedMarkerElement } = await loader.importLibrary('marker');
13+
library = { _Map, AdvancedMarkerElement, InfoWindow };
14+
super.connect();
15+
}
16+
doCreateMap({ center, zoom, options, }) {
17+
options.zoomControl = typeof options.zoomControlOptions !== 'undefined';
18+
options.mapTypeControl = typeof options.mapTypeControlOptions !== 'undefined';
19+
options.streetViewControl = typeof options.streetViewControlOptions !== 'undefined';
20+
options.fullscreenControl = typeof options.fullscreenControlOptions !== 'undefined';
21+
return new library._Map(this.element, {
22+
...options,
23+
center,
24+
zoom,
25+
});
26+
}
27+
doCreateMarker(definition) {
28+
const { position, title, infoWindow, rawOptions = {}, ...otherOptions } = definition;
29+
const marker = new library.AdvancedMarkerElement({
30+
position,
31+
title,
32+
...otherOptions,
33+
...rawOptions,
34+
map: this.map,
35+
});
36+
if (infoWindow) {
37+
this.createInfoWindow({ definition: infoWindow, marker });
38+
}
39+
return marker;
40+
}
41+
doCreateInfoWindow({ definition, marker, }) {
42+
const { headerContent, content, rawOptions = {}, ...otherOptions } = definition;
43+
const infoWindow = new library.InfoWindow({
44+
headerContent: this.createTextOrElement(headerContent),
45+
content: this.createTextOrElement(content),
46+
...otherOptions,
47+
...rawOptions,
48+
});
49+
if (definition.opened) {
50+
infoWindow.open({
51+
map: this.map,
52+
shouldFocus: false,
53+
anchor: marker,
54+
});
55+
}
56+
marker.addListener('click', () => {
57+
if (definition.autoClose) {
58+
this.closeInfoWindowsExcept(infoWindow);
59+
}
60+
infoWindow.open({
61+
map: this.map,
62+
anchor: marker,
63+
});
64+
});
65+
return infoWindow;
66+
}
67+
createTextOrElement(content) {
68+
if (!content) {
69+
return null;
70+
}
71+
if (content.includes('<')) {
72+
const div = document.createElement('div');
73+
div.innerHTML = content;
74+
return div;
75+
}
76+
return content;
77+
}
78+
closeInfoWindowsExcept(infoWindow) {
79+
this.infoWindows.forEach((otherInfoWindow) => {
80+
if (otherInfoWindow !== infoWindow) {
81+
otherInfoWindow.close();
82+
}
83+
});
84+
}
85+
doFitBoundsToMarkers() {
86+
if (this.markers.length === 0) {
87+
return;
88+
}
89+
const bounds = new google.maps.LatLngBounds();
90+
this.markers.forEach((marker) => {
91+
if (!marker.position) {
92+
return;
93+
}
94+
bounds.extend(marker.position);
95+
});
96+
this.map.fitBounds(bounds);
97+
}
98+
}
99+
default_1.values = {
100+
providerOptions: Object,
101+
};
102+
103+
export { default_1 as default };

assets/package.json

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"name": "@symfony/ux-map-google",
3+
"description": "GoogleMaps bridge for Symfony UX Map, integrate interactive maps in your Symfony applications",
4+
"license": "MIT",
5+
"version": "1.0.0",
6+
"type": "module",
7+
"main": "dist/map_controller.js",
8+
"types": "dist/map_controller.d.ts",
9+
"symfony": {
10+
"controllers": {
11+
"map": {
12+
"main": "dist/map_controller.js",
13+
"webpackMode": "lazy",
14+
"fetch": "lazy",
15+
"enabled": true
16+
}
17+
},
18+
"importmap": {
19+
"@hotwired/stimulus": "^3.0.0",
20+
"@googlemaps/js-api-loader": "^1.16.6",
21+
"@symfony/ux-map-google/map-controller": "path:%PACKAGE%/dist/map_controller.js"
22+
}
23+
},
24+
"peerDependencies": {
25+
"@googlemaps/js-api-loader": "^1.16.6",
26+
"@hotwired/stimulus": "^3.0.0"
27+
},
28+
"peerDependenciesMeta": {
29+
"@googlemaps/js-api-loader": {
30+
"optional": false
31+
}
32+
},
33+
"devDependencies": {
34+
"@googlemaps/js-api-loader": "^1.16.6",
35+
"@hotwired/stimulus": "^3.0.0",
36+
"@types/google.maps": "^3.55.9",
37+
"happy-dom": "^14.12.3"
38+
}
39+
}

0 commit comments

Comments
 (0)