Skip to content

Commit 2cb5af4

Browse files
author
Danny van Wijk
committed
[Map] Add option to configure attribution and zoom control position
1 parent 8f02bd6 commit 2cb5af4

20 files changed

+322
-15
lines changed

src/Map/src/Bridge/Leaflet/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- Using `new LeafletOptions(tileLayer: false)` will now disable the default `TileLayer`.
66
Useful when using a custom tiles layer rendering engine not configurable with `L.tileLayer().addTo(map)` method
77
(e.g.: [Esri/esri-leaflet-vector](https://github.com/Esri/esri-leaflet-vector))
8+
- Added option to configure attribution and zoom control position.
89

910
## 2.25
1011

src/Map/src/Bridge/Leaflet/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ You can use the `LeafletOptions` class to configure your `Map`::
3333

3434
```php
3535
use Symfony\UX\Map\Bridge\Leaflet\LeafletOptions;
36+
use Symfony\UX\Map\Bridge\Leaflet\Option\AttributionControlOptions;
37+
use Symfony\UX\Map\Bridge\Leaflet\Option\ControlPosition;
3638
use Symfony\UX\Map\Bridge\Leaflet\Option\TileLayer;
39+
use Symfony\UX\Map\Bridge\Leaflet\Option\ZoomControlOptions;
3740
use Symfony\UX\Map\Point;
3841
use Symfony\UX\Map\Map;
3942

@@ -50,6 +53,10 @@ $leafletOptions = (new LeafletOptions())
5053
'maxZoom' => 10,
5154
]
5255
))
56+
->attributionControl(false)
57+
->attributionControlOptions(new AttributionControlOptions(ControlPosition::BOTTOM_LEFT))
58+
->zoomControl(false)
59+
->zoomControlOptions(new ZoomControlOptions(ControlPosition::TOP_LEFT))
5360
;
5461

5562
// Add the custom options to the map

src/Map/src/Bridge/Leaflet/assets/dist/map_controller.d.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,14 @@ import AbstractMapController from '@symfony/ux-map';
22
import type { Icon, InfoWindowWithoutPositionDefinition, MarkerDefinition, Point, PolygonDefinition, PolylineDefinition } from '@symfony/ux-map';
33
import 'leaflet/dist/leaflet.min.css';
44
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'> & {
5+
import type { ControlPosition, MapOptions as LeafletMapOptions, MarkerOptions, PolylineOptions as PolygonOptions, PolylineOptions, PopupOptions } from 'leaflet';
6+
type MapOptions = Pick<LeafletMapOptions, 'center' | 'zoom' | 'attributionControl' | 'zoomControl'> & {
7+
attributionControlOptions?: {
8+
position: ControlPosition;
9+
};
10+
zoomControlOptions?: {
11+
position: ControlPosition;
12+
};
713
tileLayer: {
814
url: string;
915
attribution: string;

src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ class map_controller extends default_1 {
140140
});
141141
}
142142
doCreateMap({ center, zoom, options, }) {
143+
options.attributionControl = typeof options.attributionControlOptions !== 'undefined';
144+
options.zoomControl = typeof options.zoomControlOptions !== 'undefined';
143145
const map = L.map(this.element, {
144146
...options,
145147
center: center === null ? undefined : center,
@@ -151,6 +153,12 @@ class map_controller extends default_1 {
151153
...options.tileLayer.options,
152154
}).addTo(map);
153155
}
156+
if (options.attributionControl && options.attributionControlOptions?.position) {
157+
map.attributionControl.setPosition(options.attributionControlOptions.position);
158+
}
159+
if (options.zoomControl && options.zoomControlOptions?.position) {
160+
map.zoomControl.setPosition(options.zoomControlOptions.position);
161+
}
154162
return map;
155163
}
156164
doCreateMarker({ definition }) {

src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
import 'leaflet/dist/leaflet.min.css';
1111
import * as L from 'leaflet';
1212
import type {
13+
ControlPosition,
1314
LatLngBoundsExpression,
1415
MapOptions as LeafletMapOptions,
1516
MarkerOptions,
@@ -18,7 +19,9 @@ import type {
1819
PopupOptions,
1920
} from 'leaflet';
2021

21-
type MapOptions = Pick<LeafletMapOptions, 'center' | 'zoom'> & {
22+
type MapOptions = Pick<LeafletMapOptions, 'center' | 'zoom' | 'attributionControl' | 'zoomControl'> & {
23+
attributionControlOptions?: { position: ControlPosition };
24+
zoomControlOptions?: { position: ControlPosition };
2225
tileLayer: { url: string; attribution: string; options: Record<string, unknown> } | false;
2326
};
2427

@@ -75,6 +78,10 @@ export default class extends AbstractMapController<
7578
zoom,
7679
options,
7780
}: { center: Point | null; zoom: number | null; options: MapOptions }): L.Map {
81+
// We assume the following control options are enabled if their options are set
82+
options.attributionControl = typeof options.attributionControlOptions !== 'undefined';
83+
options.zoomControl = typeof options.zoomControlOptions !== 'undefined';
84+
7885
const map = L.map(this.element, {
7986
...options,
8087
center: center === null ? undefined : center,
@@ -88,6 +95,14 @@ export default class extends AbstractMapController<
8895
}).addTo(map);
8996
}
9097

98+
if (options.attributionControl && options.attributionControlOptions?.position) {
99+
map.attributionControl.setPosition(options.attributionControlOptions.position);
100+
}
101+
102+
if (options.zoomControl && options.zoomControlOptions?.position) {
103+
map.zoomControl.setPosition(options.zoomControlOptions.position);
104+
}
105+
91106
return map;
92107
}
93108

src/Map/src/Bridge/Leaflet/src/LeafletOptions.php

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111

1212
namespace Symfony\UX\Map\Bridge\Leaflet;
1313

14+
use Symfony\UX\Map\Bridge\Leaflet\Option\AttributionControlOptions;
1415
use Symfony\UX\Map\Bridge\Leaflet\Option\TileLayer;
16+
use Symfony\UX\Map\Bridge\Leaflet\Option\ZoomControlOptions;
1517
use Symfony\UX\Map\MapOptionsInterface;
1618

1719
/**
@@ -24,6 +26,10 @@ public function __construct(
2426
url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
2527
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
2628
),
29+
private bool $attributionControl = true,
30+
private AttributionControlOptions $attributionControlOptions = new AttributionControlOptions(),
31+
private bool $zoomControl = true,
32+
private ZoomControlOptions $zoomControlOptions = new ZoomControlOptions(),
2733
) {
2834
}
2935

@@ -34,23 +40,77 @@ public function tileLayer(TileLayer|false $tileLayer): self
3440
return $this;
3541
}
3642

43+
public function attributionControl(bool $enable = true): self
44+
{
45+
$this->attributionControl = $enable;
46+
47+
return $this;
48+
}
49+
50+
public function attributionControlOptions(AttributionControlOptions $attributionControlOptions): self
51+
{
52+
$this->attributionControl = true;
53+
$this->attributionControlOptions = $attributionControlOptions;
54+
55+
return $this;
56+
}
57+
58+
public function zoomControl(bool $enable = true): self
59+
{
60+
$this->zoomControl = $enable;
61+
62+
return $this;
63+
}
64+
65+
public function zoomControlOptions(ZoomControlOptions $zoomControlOptions): self
66+
{
67+
$this->zoomControl = true;
68+
$this->zoomControlOptions = $zoomControlOptions;
69+
70+
return $this;
71+
}
72+
3773
/**
3874
* @internal
3975
*/
4076
public static function fromArray(array $array): MapOptionsInterface
4177
{
42-
return new self(
43-
tileLayer: $array['tileLayer'] ? TileLayer::fromArray($array['tileLayer']) : false,
44-
);
78+
$array += ['attributionControl' => false, 'zoomControl' => false, 'tileLayer' => false];
79+
80+
if ($array['tileLayer']) {
81+
$array['tileLayer'] = TileLayer::fromArray($array['tileLayer']);
82+
}
83+
84+
if (isset($array['attributionControlOptions'])) {
85+
$array['attributionControl'] = true;
86+
$array['attributionControlOptions'] = AttributionControlOptions::fromArray($array['attributionControlOptions']);
87+
}
88+
89+
if (isset($array['zoomControlOptions'])) {
90+
$array['zoomControl'] = true;
91+
$array['zoomControlOptions'] = ZoomControlOptions::fromArray($array['zoomControlOptions']);
92+
}
93+
94+
return new self(...$array);
4595
}
4696

4797
/**
4898
* @internal
4999
*/
50100
public function toArray(): array
51101
{
52-
return [
102+
$array = [
53103
'tileLayer' => $this->tileLayer ? $this->tileLayer->toArray() : false,
54104
];
105+
106+
if ($this->attributionControl) {
107+
$array['attributionControlOptions'] = $this->attributionControlOptions->toArray();
108+
}
109+
110+
if ($this->zoomControl) {
111+
$array['zoomControlOptions'] = $this->zoomControlOptions->toArray();
112+
}
113+
114+
return $array;
55115
}
56116
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\UX\Map\Bridge\Leaflet\Option;
13+
14+
/**
15+
* Options for the rendering of the attribution control.
16+
*
17+
* @see https://leafletjs.com/reference.html#control-zoom
18+
*/
19+
final class AttributionControlOptions
20+
{
21+
public function __construct(
22+
private readonly ControlPosition $position = ControlPosition::BOTTOM_RIGHT,
23+
) {
24+
}
25+
26+
/**
27+
* @internal
28+
*/
29+
public static function fromArray(array $array): self
30+
{
31+
return new self(
32+
position: ControlPosition::from($array['position']),
33+
);
34+
}
35+
36+
/**
37+
* @internal
38+
*/
39+
public function toArray(): array
40+
{
41+
return [
42+
'position' => $this->position->value,
43+
];
44+
}
45+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\UX\Map\Bridge\Leaflet\Option;
13+
14+
/**
15+
* @see https://leafletjs.com/reference.html#control-position
16+
*/
17+
enum ControlPosition: string
18+
{
19+
case TOP_LEFT = 'topleft';
20+
case TOP_RIGHT = 'topright';
21+
case BOTTOM_LEFT = 'bottomleft';
22+
case BOTTOM_RIGHT = 'bottomright';
23+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\UX\Map\Bridge\Leaflet\Option;
13+
14+
/**
15+
* Options for the rendering of the zoom control.
16+
*
17+
* @see https://leafletjs.com/reference.html#control-zoom
18+
*/
19+
final class ZoomControlOptions
20+
{
21+
public function __construct(
22+
private readonly ControlPosition $position = ControlPosition::TOP_LEFT,
23+
) {
24+
}
25+
26+
/**
27+
* @internal
28+
*/
29+
public static function fromArray(array $array): self
30+
{
31+
return new self(
32+
position: ControlPosition::from($array['position']),
33+
);
34+
}
35+
36+
/**
37+
* @internal
38+
*/
39+
public function toArray(): array
40+
{
41+
return [
42+
'position' => $this->position->value,
43+
];
44+
}
45+
}

src/Map/src/Bridge/Leaflet/tests/LeafletOptionsTest.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ public function testWithMinimalConfiguration(): void
2727
'attribution' => '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
2828
'options' => [],
2929
],
30+
'attributionControlOptions' => [
31+
'position' => 'bottomright',
32+
],
33+
'zoomControlOptions' => [
34+
'position' => 'topleft',
35+
],
3036
], $leafletOptions->toArray());
3137

3238
self::assertEquals($leafletOptions, LeafletOptions::fromArray($leafletOptions->toArray()));
@@ -58,6 +64,12 @@ public function testWithMaximumConfiguration(): void
5864
'zoomOffset' => 0,
5965
],
6066
],
67+
'attributionControlOptions' => [
68+
'position' => 'bottomright',
69+
],
70+
'zoomControlOptions' => [
71+
'position' => 'topleft',
72+
],
6173
], $leafletOptions->toArray());
6274

6375
self::assertEquals($leafletOptions, LeafletOptions::fromArray($leafletOptions->toArray()));
@@ -69,6 +81,30 @@ public function testWithTileLayerFalse(): void
6981

7082
self::assertSame([
7183
'tileLayer' => false,
84+
'attributionControlOptions' => [
85+
'position' => 'bottomright',
86+
],
87+
'zoomControlOptions' => [
88+
'position' => 'topleft',
89+
],
90+
], $leafletOptions->toArray());
91+
92+
self::assertEquals($leafletOptions, LeafletOptions::fromArray($leafletOptions->toArray()));
93+
}
94+
95+
public function testWithoutControls(): void
96+
{
97+
$leafletOptions = new LeafletOptions(
98+
attributionControl: false,
99+
zoomControl: false,
100+
);
101+
102+
self::assertSame([
103+
'tileLayer' => [
104+
'url' => 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
105+
'attribution' => '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
106+
'options' => [],
107+
],
72108
], $leafletOptions->toArray());
73109

74110
self::assertEquals($leafletOptions, LeafletOptions::fromArray($leafletOptions->toArray()));

0 commit comments

Comments
 (0)