Skip to content

Commit

Permalink
[Map] Second iteration, simplify configuration a lot, make Map/Marker…
Browse files Browse the repository at this point in the history
…s/InfoWindow generic!
  • Loading branch information
Kocal committed Jul 24, 2024
1 parent a8dea93 commit 3598a9a
Show file tree
Hide file tree
Showing 120 changed files with 2,886 additions and 3,024 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ jobs:
dependency-version: 'highest'
component: ${{ fromJson(needs.tests-php-components.outputs.components )}}
exclude:
- component: Map # does not support PHP 8.1
php-version: '8.1'
- component: Swup # has no tests
- component: Turbo # has its own workflow (test-turbo.yml)
- component: Typed # has no tests
Expand Down
1 change: 1 addition & 0 deletions src/Map/.gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
/phpunit.xml.dist export-ignore
/assets/src export-ignore
/assets/test export-ignore
/assets/vitest.config.js export-ignore
/tests export-ignore
1 change: 0 additions & 1 deletion src/Map/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
vendor
composer.lock
.php_cs.cache
.phpunit.result.cache
2 changes: 1 addition & 1 deletion src/Map/LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2023-present Fabien Potencier
Copyright (c) 2024-present Fabien Potencier

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
2 changes: 1 addition & 1 deletion src/Map/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
**EXPERIMENTAL** This component is currently experimental and is
likely to change, or even change drastically.

Symfony UX Map integrates [Symfony Translation](https://symfony.com/doc/current/translation.html) for JavaScript.
Symfony UX Map integrates interactive Maps in Symfony applications, like Leaflet or GoogleMaps.

**This repository is a READ-ONLY sub-tree split**. See
https://github.com/symfony/ux to create issues or submit pull requests.
Expand Down
47 changes: 7 additions & 40 deletions src/Map/assets/dist/google_maps_controller.d.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,22 @@
/// <reference types="google.maps" />
import { Controller } from '@hotwired/stimulus';
type MarkerId = number;
import type { MapView } from './map';
type GoogleMapsOptions = Pick<google.maps.MapOptions, 'mapId' | 'gestureHandling' | 'backgroundColor' | 'disableDoubleClickZoom' | 'zoomControl' | 'zoomControlOptions' | 'mapTypeControl' | 'mapTypeControlOptions' | 'streetViewControl' | 'streetViewControlOptions' | 'fullscreenControl' | 'fullscreenControlOptions'>;
export default class extends Controller<HTMLElement> {
static values: {
view: ObjectConstructor;
};
viewValue: {
mapId: string | null;
center: null | {
lat: number;
lng: number;
};
zoom: number;
gestureHandling: string;
backgroundColor: string;
disableDoubleClickZoom: boolean;
zoomControl: boolean;
zoomControlOptions: google.maps.ZoomControlOptions;
mapTypeControl: boolean;
mapTypeControlOptions: google.maps.MapTypeControlOptions;
streetViewControl: boolean;
streetViewControlOptions: google.maps.StreetViewControlOptions;
fullscreenControl: boolean;
fullscreenControlOptions: google.maps.FullscreenControlOptions;
markers: Array<{
_id: MarkerId;
position: {
lat: number;
lng: number;
};
title: string | null;
}>;
infoWindows: Array<{
headerContent: string | null;
content: string | null;
position: {
lat: number;
lng: number;
};
opened: boolean;
_markerId: MarkerId | null;
autoClose: boolean;
}>;
fitBoundsToMarkers: boolean;
};
viewValue: MapView<GoogleMapsOptions>;
private loader;
private map;
private markers;
private infoWindows;
initialize(): void;
connect(): Promise<void>;
private createMarkers;
private createMarker;
private createInfoWindows;
private createInfoWindow;
private createTextOrElement;
private closeInfoWindowsExcept;
private dispatchEvent;
Expand Down
148 changes: 68 additions & 80 deletions src/Map/assets/dist/google_maps_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,106 +4,94 @@ import { Loader } from '@googlemaps/js-api-loader';
class default_1 extends Controller {
constructor() {
super(...arguments);
this.markers = new Map();
this.markers = [];
this.infoWindows = [];
}
initialize() {
var _a;
const providerConfig = (_a = window.__symfony_ux_maps.providers) === null || _a === void 0 ? void 0 : _a.google_maps;
var _a, _b;
const providerConfig = (_b = (_a = window.__symfony_ux_maps) === null || _a === void 0 ? void 0 : _a.providers) === null || _b === void 0 ? void 0 : _b['google-maps'];
if (!providerConfig) {
throw new Error('Google Maps provider configuration is missing, did you forget to call `{{ ux_map_script_tags() }}`?');
}
const loaderOptions = {
apiKey: providerConfig.key,
};
const loaderOptions = providerConfig;
this.dispatchEvent('init', {
loaderOptions,
});
this.loader = new Loader(loaderOptions);
}
async connect() {
const { Map: GoogleMap, InfoWindow } = await this.loader.importLibrary('maps');
const mapOptions = {
gestureHandling: this.viewValue.gestureHandling,
backgroundColor: this.viewValue.backgroundColor,
disableDoubleClickZoom: this.viewValue.disableDoubleClickZoom,
zoomControl: this.viewValue.zoomControl,
zoomControlOptions: this.viewValue.zoomControlOptions,
mapTypeControl: this.viewValue.mapTypeControl,
mapTypeControlOptions: this.viewValue.mapTypeControlOptions,
streetViewControl: this.viewValue.streetViewControl,
streetViewControlOptions: this.viewValue.streetViewControlOptions,
fullscreenControl: this.viewValue.fullscreenControl,
fullscreenControlOptions: this.viewValue.fullscreenControlOptions,
};
if (this.viewValue.mapId) {
mapOptions.mapId = this.viewValue.mapId;
}
if (this.viewValue.center) {
mapOptions.center = this.viewValue.center;
}
if (this.viewValue.zoom) {
mapOptions.zoom = this.viewValue.zoom;
}
this.dispatchEvent('pre-connect', {
mapOptions,
const { Map: GoogleMap } = await this.loader.importLibrary('maps');
const { center, zoom, fitBoundsToMarkers, options, markers, infoWindows } = this.viewValue;
this.dispatchEvent('pre-connect', { options });
this.map = new GoogleMap(this.element, Object.assign(Object.assign({}, options), { center,
zoom }));
this.createMarkers(markers, fitBoundsToMarkers);
this.createInfoWindows(infoWindows);
this.dispatchEvent('connect', {
map: this.map,
markers: this.markers,
infoWindows: this.infoWindows,
});
this.map = new GoogleMap(this.element, mapOptions);
if (this.viewValue.markers) {
const { AdvancedMarkerElement } = await this.loader.importLibrary('marker');
this.viewValue.markers.forEach((markerConfiguration) => {
const marker = new AdvancedMarkerElement({
position: markerConfiguration.position,
title: markerConfiguration.title,
map: this.map,
});
this.markers.set(markerConfiguration._id, marker);
}
createMarkers(markers, fitBoundsToMarkers) {
markers.forEach((definition) => this.createMarker(definition));
if (this.markers.length > 0 && fitBoundsToMarkers) {
const bounds = new google.maps.LatLngBounds();
this.markers.forEach((marker) => {
if (!marker.position) {
return;
}
bounds.extend(marker.position);
});
if (this.viewValue.fitBoundsToMarkers) {
const bounds = new google.maps.LatLngBounds();
this.markers.forEach((marker) => {
if (!marker.position) {
return;
}
bounds.extend(marker.position);
});
this.map.fitBounds(bounds);
}
this.map.fitBounds(bounds);
}
}
async createMarker(definition) {
const { AdvancedMarkerElement } = await this.loader.importLibrary('marker');
const options = {
position: definition.position,
title: definition.title,
};
this.dispatchEvent('marker:before-create', { options });
const marker = new AdvancedMarkerElement(Object.assign(Object.assign({}, options), { map: this.map }));
if (definition.infoWindow) {
this.createInfoWindow(definition.infoWindow, marker);
}
this.viewValue.infoWindows.forEach((infoWindowConfiguration) => {
const marker = infoWindowConfiguration._markerId
? this.markers.get(infoWindowConfiguration._markerId)
: undefined;
const infoWindow = new InfoWindow({
headerContent: this.createTextOrElement(infoWindowConfiguration.headerContent),
content: this.createTextOrElement(infoWindowConfiguration.content),
position: infoWindowConfiguration.position,
this.dispatchEvent('marker:after-create', { marker });
this.markers.push(marker);
}
createInfoWindows(infoWindows) {
infoWindows.forEach((definition) => this.createInfoWindow(definition));
}
async createInfoWindow(definition, marker) {
const { InfoWindow } = await this.loader.importLibrary('maps');
const options = {
headerContent: this.createTextOrElement(definition.headerContent),
content: this.createTextOrElement(definition.content),
position: definition.position,
};
this.dispatchEvent('info-window:before-create', { options });
const infoWindow = new InfoWindow(options);
this.infoWindows.push(infoWindow);
if (definition.opened) {
infoWindow.open({
map: this.map,
shouldFocus: false,
anchor: marker,
});
this.infoWindows.push(infoWindow);
if (infoWindowConfiguration.opened) {
}
if (marker) {
marker.addListener('click', () => {
if (definition.autoClose) {
this.closeInfoWindowsExcept(infoWindow);
}
infoWindow.open({
map: this.map,
shouldFocus: false,
anchor: marker,
});
}
if (marker) {
marker.addListener('click', () => {
if (infoWindowConfiguration.autoClose) {
this.closeInfoWindowsExcept(infoWindow);
}
infoWindow.open({
map: this.map,
anchor: marker,
});
});
}
});
this.dispatchEvent('connect', {
map: this.map,
markers: this.markers,
infoWindows: this.infoWindows,
});
});
}
this.dispatchEvent('info-window:after-create', { infoWindow });
}
createTextOrElement(content) {
if (!content) {
Expand Down
50 changes: 17 additions & 33 deletions src/Map/assets/dist/leaflet_controller.d.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,29 @@
import { Controller } from '@hotwired/stimulus';
import 'leaflet/dist/leaflet.min.css';
import type { MarkerOptions } from 'leaflet';
type MarkerId = number;
import type { MapOptions } from 'leaflet';
import type { MapView } from './map';
type LeafletOptions = Pick<MapOptions, 'center' | 'zoom'>;
type AdditionalOptions = {
tileLayer: {
url: string;
attribution: string;
options: Record<string, unknown>;
};
};
export default class extends Controller<HTMLElement> {
static values: {
view: ObjectConstructor;
};
viewValue: {
center: null | {
lat: number;
lng: number;
};
zoom: number | null;
tileLayer: {
url: string;
attribution: string;
} & Record<string, unknown>;
fitBoundsToMarkers: boolean;
markers: Array<{
_id: MarkerId;
position: {
lat: number;
lng: number;
};
} & MarkerOptions>;
popups: Array<{
_markerId: MarkerId | null;
content: string;
position: {
lat: number;
lng: number;
};
opened: boolean;
autoClose: boolean;
}>;
};
viewValue: MapView<LeafletOptions & AdditionalOptions>;
private map;
private markers;
private popups;
private infoWindows;
connect(): void;
private setupTileLayer;
private createTileLayer;
private createMarkers;
private createMarker;
private createInfoWindows;
private createInfoWindow;
private dispatchEvent;
}
export {};
Loading

0 comments on commit 3598a9a

Please sign in to comment.