Skip to content

Commit

Permalink
[Map] Add "extra" data for markers and infowindows
Browse files Browse the repository at this point in the history
  • Loading branch information
Kocal committed Aug 9, 2024
1 parent 6b8b305 commit c118875
Show file tree
Hide file tree
Showing 14 changed files with 99 additions and 20 deletions.
2 changes: 2 additions & 0 deletions src/Map/assets/dist/abstract_map_controller.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type MarkerDefinition<MarkerOptions, InfoWindowOptions> = {
title: string | null;
infoWindow?: Omit<InfoWindowDefinition<InfoWindowOptions>, 'position'>;
rawOptions?: MarkerOptions;
extra: Record<string, unknown>;
};
export type InfoWindowDefinition<InfoWindowOptions> = {
headerContent: string | null;
Expand All @@ -23,6 +24,7 @@ export type InfoWindowDefinition<InfoWindowOptions> = {
opened: boolean;
autoClose: boolean;
rawOptions?: InfoWindowOptions;
extra: Record<string, unknown>;
};
export default abstract class<MapOptions, Map, MarkerOptions, Marker, InfoWindowOptions, InfoWindow> extends Controller<HTMLElement> {
static values: {
Expand Down
21 changes: 21 additions & 0 deletions src/Map/assets/src/abstract_map_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,17 @@ export type MarkerDefinition<MarkerOptions, InfoWindowOptions> = {
position: Point;
title: string | null;
infoWindow?: Omit<InfoWindowDefinition<InfoWindowOptions>, 'position'>;
/**
* Raw options passed to the marker constructor, specific to the map provider (e.g.: `L.marker()` for Leaflet).
*/
rawOptions?: MarkerOptions;
/**
* Extra data defined by the developer.
* They are not directly used by the Stimulus controller, but they can be used by the developer with event listeners:
* - `ux:map:marker:before-create`
* - `ux:map:marker:after-create`
*/
extra: Record<string, unknown>;
};

export type InfoWindowDefinition<InfoWindowOptions> = {
Expand All @@ -23,7 +33,18 @@ export type InfoWindowDefinition<InfoWindowOptions> = {
position: Point;
opened: boolean;
autoClose: boolean;
/**
* Raw options passed to the info window constructor,
* specific to the map provider (e.g.: `google.maps.InfoWindow()` for Google Maps).
*/
rawOptions?: InfoWindowOptions;
/**
* Extra data defined by the developer.
* They are not directly used by the Stimulus controller, but they can be used by the developer with event listeners:
* - `ux:map:info-window:before-create`
* - `ux:map:info-window:after-create`
*/
extra: Record<string, unknown>;
};

export default abstract class<
Expand Down
20 changes: 16 additions & 4 deletions src/Map/doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Configuration is done in your ``config/packages/ux_map.yaml`` file:
# config/packages/ux_map.yaml
ux_map:
renderer: '%env(UX_MAP_DSN)%'
renderer: '%env(resolve:default::UX_MAP_DSN)%'
The ``UX_MAP_DSN`` environment variable configure which renderer to use.

Expand Down Expand Up @@ -82,21 +82,33 @@ A map is created by calling ``new Map()``. You can configure the center, zoom, a
->zoom(6)
;
// 2. You can add markers, with an optional info window
// 2. You can add markers
$myMap
->addMarker(new Marker(
position: new Point(48.8566, 2.3522),
title: 'Paris'
))

// With an info window associated to the marker:
->addMarker(new Marker(
position: new Point(45.7640, 4.8357),
title: 'Lyon',
// With an info window
infoWindow: new InfoWindow(
headerContent: '<b>Lyon</b>',
content: 'The French town in the historic Rhône-Alpes region, located at the junction of the Rhône and Saône rivers.'
)
));
))

// You can also pass extra data, that you can later use in your custom Stimulus controller
// when listening to "ux:map:marker:before-create" event:
->addMarker(new Marker(
position: new Point(46.5074666, 6.633729),
title: 'Olympic Parc',
extra: [
'icon_mask_url' => 'https://maps.gstatic.com/mapfiles/place_api/icons/v2/tree_pinlet.svg',
]
)
;
// 3. And inject the map in your template to render it
return $this->render('contact/index.html.twig', [
Expand Down
4 changes: 2 additions & 2 deletions src/Map/src/Bridge/Google/assets/dist/map_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class default_1 extends AbstractMapController {
});
}
doCreateMarker(definition) {
const { position, title, infoWindow, rawOptions = {}, ...otherOptions } = definition;
const { position, title, infoWindow, extra, rawOptions = {}, ...otherOptions } = definition;
const marker = new library.AdvancedMarkerElement({
position,
title,
Expand All @@ -39,7 +39,7 @@ class default_1 extends AbstractMapController {
return marker;
}
doCreateInfoWindow({ definition, marker, }) {
const { headerContent, content, rawOptions = {}, ...otherOptions } = definition;
const { headerContent, content, extra, rawOptions = {}, ...otherOptions } = definition;
const infoWindow = new library.InfoWindow({
headerContent: this.createTextOrElement(headerContent),
content: this.createTextOrElement(content),
Expand Down
4 changes: 2 additions & 2 deletions src/Map/src/Bridge/Google/assets/src/map_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export default class extends AbstractMapController<
protected doCreateMarker(
definition: MarkerDefinition<google.maps.marker.AdvancedMarkerElementOptions, google.maps.InfoWindowOptions>
): google.maps.marker.AdvancedMarkerElement {
const { position, title, infoWindow, rawOptions = {}, ...otherOptions } = definition;
const { position, title, infoWindow, extra, rawOptions = {}, ...otherOptions } = definition;

const marker = new library.AdvancedMarkerElement({
position,
Expand All @@ -114,7 +114,7 @@ export default class extends AbstractMapController<
>['infoWindow'];
marker: google.maps.marker.AdvancedMarkerElement;
}): google.maps.InfoWindow {
const { headerContent, content, rawOptions = {}, ...otherOptions } = definition;
const { headerContent, content, extra, rawOptions = {}, ...otherOptions } = definition;

const infoWindow = new library.InfoWindow({
headerContent: this.createTextOrElement(headerContent),
Expand Down
2 changes: 1 addition & 1 deletion src/Map/src/Bridge/Google/tests/GoogleRendererTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function provideTestRenderMap(): iterable
];

yield 'with markers and infoWindows' => [
'expected_render' => '<div data-controller="symfony--ux-google-map--map" data-symfony--ux-google-map--map-provider-options-value="&#x7B;&quot;apiKey&quot;&#x3A;&quot;api_key&quot;&#x7D;" data-symfony--ux-google-map--map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;12,&quot;fitBoundsToMarkers&quot;&#x3A;false,&quot;options&quot;&#x3A;&#x7B;&quot;mapId&quot;&#x3A;null,&quot;gestureHandling&quot;&#x3A;&quot;auto&quot;,&quot;backgroundColor&quot;&#x3A;null,&quot;disableDoubleClickZoom&quot;&#x3A;false,&quot;zoomControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;mapTypeControlOptions&quot;&#x3A;&#x7B;&quot;mapTypeIds&quot;&#x3A;&#x5B;&#x5D;,&quot;position&quot;&#x3A;14,&quot;style&quot;&#x3A;0&#x7D;,&quot;streetViewControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;fullscreenControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;20&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;title&quot;&#x3A;&quot;Paris&quot;,&quot;infoWindow&quot;&#x3A;null&#x7D;,&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;title&quot;&#x3A;&quot;Lyon&quot;,&quot;infoWindow&quot;&#x3A;&#x7B;&quot;headerContent&quot;&#x3A;null,&quot;content&quot;&#x3A;&quot;Lyon&quot;,&quot;position&quot;&#x3A;null,&quot;opened&quot;&#x3A;false,&quot;autoClose&quot;&#x3A;true&#x7D;&#x7D;&#x5D;&#x7D;"></div>',
'expected_render' => '<div data-controller="symfony--ux-google-map--map" data-symfony--ux-google-map--map-provider-options-value="&#x7B;&quot;apiKey&quot;&#x3A;&quot;api_key&quot;&#x7D;" data-symfony--ux-google-map--map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;12,&quot;fitBoundsToMarkers&quot;&#x3A;false,&quot;options&quot;&#x3A;&#x7B;&quot;mapId&quot;&#x3A;null,&quot;gestureHandling&quot;&#x3A;&quot;auto&quot;,&quot;backgroundColor&quot;&#x3A;null,&quot;disableDoubleClickZoom&quot;&#x3A;false,&quot;zoomControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;mapTypeControlOptions&quot;&#x3A;&#x7B;&quot;mapTypeIds&quot;&#x3A;&#x5B;&#x5D;,&quot;position&quot;&#x3A;14,&quot;style&quot;&#x3A;0&#x7D;,&quot;streetViewControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;fullscreenControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;20&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;title&quot;&#x3A;&quot;Paris&quot;,&quot;infoWindow&quot;&#x3A;null,&quot;extra&quot;&#x3A;&#x7B;&#x7D;&#x7D;,&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;title&quot;&#x3A;&quot;Lyon&quot;,&quot;infoWindow&quot;&#x3A;&#x7B;&quot;headerContent&quot;&#x3A;null,&quot;content&quot;&#x3A;&quot;Lyon&quot;,&quot;position&quot;&#x3A;null,&quot;opened&quot;&#x3A;false,&quot;autoClose&quot;&#x3A;true,&quot;extra&quot;&#x3A;&#x7B;&#x7D;&#x7D;,&quot;extra&quot;&#x3A;&#x7B;&#x7D;&#x7D;&#x5D;&#x7D;"></div>',
'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'),
'map' => (clone $map)
->addMarker(new Marker(new Point(48.8566, 2.3522), 'Paris'))
Expand Down
4 changes: 2 additions & 2 deletions src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ class map_controller extends AbstractMapController {
return map$1;
}
doCreateMarker(definition) {
const { position, title, infoWindow, rawOptions = {}, ...otherOptions } = definition;
const { position, title, infoWindow, extra, rawOptions = {}, ...otherOptions } = definition;
const marker$1 = marker(position, { title, ...otherOptions, ...rawOptions }).addTo(this.map);
if (infoWindow) {
this.createInfoWindow({ definition: infoWindow, marker: marker$1 });
}
return marker$1;
}
doCreateInfoWindow({ definition, marker, }) {
const { headerContent, content, rawOptions = {}, ...otherOptions } = definition;
const { headerContent, content, extra, rawOptions = {}, ...otherOptions } = definition;
marker.bindPopup([headerContent, content].filter((x) => x).join('<br>'), { ...otherOptions, ...rawOptions });
if (definition.opened) {
marker.openPopup();
Expand Down
4 changes: 2 additions & 2 deletions src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export default class extends AbstractMapController<
}

protected doCreateMarker(definition: MarkerDefinition): Marker {
const { position, title, infoWindow, rawOptions = {}, ...otherOptions } = definition;
const { position, title, infoWindow, extra, rawOptions = {}, ...otherOptions } = definition;

const marker = createMarker(position, { title, ...otherOptions, ...rawOptions }).addTo(this.map);

Expand All @@ -69,7 +69,7 @@ export default class extends AbstractMapController<
definition: MarkerDefinition['infoWindow'];
marker: Marker;
}): Popup {
const { headerContent, content, rawOptions = {}, ...otherOptions } = definition;
const { headerContent, content, extra, rawOptions = {}, ...otherOptions } = definition;

marker.bindPopup([headerContent, content].filter((x) => x).join('<br>'), { ...otherOptions, ...rawOptions });
if (definition.opened) {
Expand Down
2 changes: 1 addition & 1 deletion src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public function provideTestRenderMap(): iterable
];

yield 'with markers and infoWindows' => [
'expected_render' => '<div data-controller="symfony--ux-leaflet-map--map" data-symfony--ux-leaflet-map--map-provider-options-value="&#x7B;&#x7D;" data-symfony--ux-leaflet-map--map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;12,&quot;fitBoundsToMarkers&quot;&#x3A;false,&quot;options&quot;&#x3A;&#x7B;&quot;tileLayer&quot;&#x3A;&#x7B;&quot;url&quot;&#x3A;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;tile.openstreetmap.org&#x5C;&#x2F;&#x7B;z&#x7D;&#x5C;&#x2F;&#x7B;x&#x7D;&#x5C;&#x2F;&#x7B;y&#x7D;.png&quot;,&quot;attribution&quot;&#x3A;&quot;&#x5C;u00a9&#x20;&lt;a&#x20;href&#x3D;&#x5C;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;www.openstreetmap.org&#x5C;&#x2F;copyright&#x5C;&quot;&gt;OpenStreetMap&lt;&#x5C;&#x2F;a&gt;&quot;,&quot;options&quot;&#x3A;&#x7B;&#x7D;&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;title&quot;&#x3A;&quot;Paris&quot;,&quot;infoWindow&quot;&#x3A;null&#x7D;,&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;title&quot;&#x3A;&quot;Lyon&quot;,&quot;infoWindow&quot;&#x3A;&#x7B;&quot;headerContent&quot;&#x3A;null,&quot;content&quot;&#x3A;&quot;Lyon&quot;,&quot;position&quot;&#x3A;null,&quot;opened&quot;&#x3A;false,&quot;autoClose&quot;&#x3A;true&#x7D;&#x7D;&#x5D;&#x7D;"></div>',
'expected_render' => '<div data-controller="symfony--ux-leaflet-map--map" data-symfony--ux-leaflet-map--map-provider-options-value="&#x7B;&#x7D;" data-symfony--ux-leaflet-map--map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;12,&quot;fitBoundsToMarkers&quot;&#x3A;false,&quot;options&quot;&#x3A;&#x7B;&quot;tileLayer&quot;&#x3A;&#x7B;&quot;url&quot;&#x3A;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;tile.openstreetmap.org&#x5C;&#x2F;&#x7B;z&#x7D;&#x5C;&#x2F;&#x7B;x&#x7D;&#x5C;&#x2F;&#x7B;y&#x7D;.png&quot;,&quot;attribution&quot;&#x3A;&quot;&#x5C;u00a9&#x20;&lt;a&#x20;href&#x3D;&#x5C;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;www.openstreetmap.org&#x5C;&#x2F;copyright&#x5C;&quot;&gt;OpenStreetMap&lt;&#x5C;&#x2F;a&gt;&quot;,&quot;options&quot;&#x3A;&#x7B;&#x7D;&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;title&quot;&#x3A;&quot;Paris&quot;,&quot;infoWindow&quot;&#x3A;null,&quot;extra&quot;&#x3A;&#x7B;&#x7D;&#x7D;,&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;title&quot;&#x3A;&quot;Lyon&quot;,&quot;infoWindow&quot;&#x3A;&#x7B;&quot;headerContent&quot;&#x3A;null,&quot;content&quot;&#x3A;&quot;Lyon&quot;,&quot;position&quot;&#x3A;null,&quot;opened&quot;&#x3A;false,&quot;autoClose&quot;&#x3A;true,&quot;extra&quot;&#x3A;&#x7B;&#x7D;&#x7D;,&quot;extra&quot;&#x3A;&#x7B;&#x7D;&#x7D;&#x5D;&#x7D;"></div>',
'renderer' => new LeafletRenderer(new StimulusHelper(null)),
'map' => (clone $map)
->addMarker(new Marker(new Point(48.8566, 2.3522), 'Paris'))
Expand Down
5 changes: 5 additions & 0 deletions src/Map/src/InfoWindow.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@
*/
final readonly class InfoWindow
{
/**
* @param array<string, mixed> $extra Extra data, can be used by the developer to store additional information and use them later JavaScript side
*/
public function __construct(
private ?string $headerContent = null,
private ?string $content = null,
private ?Point $position = null,
private bool $opened = false,
private bool $autoClose = true,
private array $extra = [],
) {
}

Expand All @@ -35,6 +39,7 @@ public function toArray(): array
'position' => $this->position?->toArray(),
'opened' => $this->opened,
'autoClose' => $this->autoClose,
'extra' => (object) $this->extra,
];
}
}
5 changes: 5 additions & 0 deletions src/Map/src/Marker.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,14 @@
*/
final readonly class Marker
{
/**
* @param array<string, mixed> $extra Extra data, can be used by the developer to store additional information and use them later JavaScript side
*/
public function __construct(
private Point $position,
private ?string $title = null,
private ?InfoWindow $infoWindow = null,
private array $extra = [],
) {
}

Expand All @@ -31,6 +35,7 @@ public function toArray(): array
'position' => $this->position->toArray(),
'title' => $this->title,
'infoWindow' => $this->infoWindow?->toArray(),
'extra' => (object) $this->extra,
];
}
}
5 changes: 4 additions & 1 deletion src/Map/tests/InfoWindowTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public function testToArray(): void
autoClose: false,
);

$array = $infoWindow->toArray();

self::assertSame([
'headerContent' => 'Paris',
'content' => 'Capitale de la France, est une grande ville européenne et un centre mondial de l\'art, de la mode, de la gastronomie et de la culture.',
Expand All @@ -36,6 +38,7 @@ public function testToArray(): void
],
'opened' => true,
'autoClose' => false,
], $infoWindow->toArray());
'extra' => $array['extra'],
], $array);
}
}
30 changes: 27 additions & 3 deletions src/Map/tests/MapTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,41 @@ public function toArray(): array
[
'position' => ['lat' => 48.8566, 'lng' => 2.3522],
'title' => 'Paris',
'infoWindow' => ['headerContent' => '<b>Paris</b>', 'content' => 'Paris', 'position' => ['lat' => 48.8566, 'lng' => 2.3522], 'opened' => false, 'autoClose' => true],
'infoWindow' => [
'headerContent' => '<b>Paris</b>',
'content' => 'Paris',
'position' => ['lat' => 48.8566, 'lng' => 2.3522],
'opened' => false,
'autoClose' => true,
'extra' => $array['markers'][0]['infoWindow']['extra'],
],
'extra' => $array['markers'][0]['extra'],
],
[
'position' => ['lat' => 45.764, 'lng' => 4.8357],
'title' => 'Lyon',
'infoWindow' => ['headerContent' => '<b>Lyon</b>', 'content' => 'Lyon', 'position' => ['lat' => 45.764, 'lng' => 4.8357], 'opened' => true, 'autoClose' => true],
'infoWindow' => [
'headerContent' => '<b>Lyon</b>',
'content' => 'Lyon',
'position' => ['lat' => 45.764, 'lng' => 4.8357],
'opened' => true,
'autoClose' => true,
'extra' => $array['markers'][1]['infoWindow']['extra'],
],
'extra' => $array['markers'][1]['extra'],
],
[
'position' => ['lat' => 43.2965, 'lng' => 5.3698],
'title' => 'Marseille',
'infoWindow' => ['headerContent' => '<b>Marseille</b>', 'content' => 'Marseille', 'position' => ['lat' => 43.2965, 'lng' => 5.3698], 'opened' => true, 'autoClose' => true],
'infoWindow' => [
'headerContent' => '<b>Marseille</b>',
'content' => 'Marseille',
'position' => ['lat' => 43.2965, 'lng' => 5.3698],
'opened' => true,
'autoClose' => true,
'extra' => $array['markers'][2]['infoWindow']['extra'],
],
'extra' => $array['markers'][2]['extra'],
],
],
], $array);
Expand Down
Loading

0 comments on commit c118875

Please sign in to comment.