Skip to content

[Map] Add Marker Icon customization capability #2605

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 18, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
[Map] Refactor and simplify the way to create a custom Icon
  • Loading branch information
Kocal committed Mar 17, 2025
commit 311689c2e312db4b30e322979a19cd80f8b5aac8
23 changes: 14 additions & 9 deletions src/Map/doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -137,19 +137,24 @@ Add Marker icons

.. versionadded:: 2.24

``Marker`` icon customizatisation is available since UX Map 2.24.
When returning a Map, it's quite common to want to customize ``Marker`` icon. It is possible thanks to ``icon`` parameter and ``Icon`` class::

// It can be a UX Icon (requires `symfony/ux-icons`)
$icon = Icon::fromUxIcon('fa:map-marker');
``Marker`` icon customization is available since UX Map 2.24.

A ``Marker`` can be customized with an ``Icon`` instance, which can either be an UX Icon, an URL, or a SVG content::

// It can be a UX Icon (requires `symfony/ux-icons` package)
$icon = Icon::ux('fa:map-marker');
// Or an URL pointing to an image
$icon = Icon::fromUrl('https://example.com/marker.png');
$icon = Icon::url('https://example.com/marker.png');
// Or a plain SVG content
$icon = Icon::fromInlineSVG('<svg>(...)</svg>');
new Marker(
$icon = Icon::svg('<svg>(...)</svg>');

// Configure the icon size with the `width()` and `height()` methods
$icon = Icon::ux('fa:map-marker')->width(48)->height(48);

$map->addMarker(new Marker(
// ...
icon: $icon
))
));

Remove elements from Map
~~~~~~~~~~~~~~~~~~~~~~~~
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 @@ -31,7 +31,7 @@ public function provideTestRenderMap(): iterable
->center(new Point(48.8566, 2.3522))
->zoom(12);
$marker1 = new Marker(position: new Point(48.8566, 2.3522), title: 'Paris', id: 'marker1');
$marker2 = new Marker(position: new Point(48.8566, 2.3522), title: 'Lyon', infoWindow: new InfoWindow(content: 'Lyon'), id: 'marker2', icon: Icon::fromInlineSVG(html: '<svg></svg>'));
$marker2 = new Marker(position: new Point(48.8566, 2.3522), title: 'Lyon', infoWindow: new InfoWindow(content: 'Lyon'), id: 'marker2', icon: Icon::svg(html: '<svg></svg>'));
$marker3 = new Marker(position: new Point(45.8566, 2.3522), title: 'Dijon', id: 'marker3');

yield 'simple map, with minimum options' => [
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 @@ -31,7 +31,7 @@ public function provideTestRenderMap(): iterable
->zoom(12);

$marker1 = new Marker(position: new Point(48.8566, 2.3522), title: 'Paris', id: 'marker1');
$marker2 = new Marker(position: new Point(48.8566, 2.3522), title: 'Lyon', infoWindow: new InfoWindow(content: 'Lyon'), id: 'marker2', icon: Icon::fromInlineSVG(html: '<svg></svg>'));
$marker2 = new Marker(position: new Point(48.8566, 2.3522), title: 'Lyon', infoWindow: new InfoWindow(content: 'Lyon'), id: 'marker2', icon: Icon::svg(html: '<svg></svg>'));
$marker3 = new Marker(position: new Point(45.8566, 2.3522), title: 'Dijon', id: 'marker3');

yield 'simple map' => [
Expand Down
110 changes: 63 additions & 47 deletions src/Map/src/Icon/Icon.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,77 +17,93 @@
* Represents an icon that can be displayed on a map marker.
*
* @author Sylvain Blondeau <[email protected]>
* @author Hugo Alliaume <[email protected]>
*/
class Icon
abstract class Icon
{
public const TYPE_URL = 'url';
public const TYPE_INLINE_SVG = 'inline-svg';
public const TYPE_UX_ICON = 'ux-icon';
/**
* @param non-empty-string $url
*/
public static function url(string $url): UrlIcon
{
return new UrlIcon($url);
}

private function __construct(
public string $content,
public string $type,
public int $width = 24,
public int $height = 24,
) {
/**
* @param non-empty-string $html
*/
public static function svg(string $html): SvgIcon
{
return new SvgIcon($html);
}

public function toArray(): array
/**
* @param non-empty-string $name
*/
public static function ux(string $name): UxIcon
{
return [
'content' => $this->content,
'type' => $this->type,
'width' => $this->width,
'height' => $this->height,
];
return new UxIcon($name);
}

/**
* @param positive-int $width
* @param positive-int $height
*/
protected function __construct(
protected IconType $type,
protected int $width = 24,
protected int $height = 24,
) {
}

public static function fromUrl(string $url, int $width = 24, int $height = 24): Url
public function width(int $width): static
{
return new Url(
content: $url,
type: self::TYPE_URL,
width: $width,
height: $height
);
if ($width <= 0) {
throw new InvalidArgumentException('Width must be greater than 0.');
}

$this->width = $width;

return $this;
}

public static function fromInlineSVG(string $html, int $width = 24, int $height = 24): InlineSvg
public function height(int $height): static
{
return new InlineSvg(
content: $html,
type: self::TYPE_INLINE_SVG,
width: $width,
height: $height
);
if ($height <= 0) {
throw new InvalidArgumentException('Height must be greater than 0.');
}

$this->height = $height;

return $this;
}

public static function fromUxIcon(string $name, int $width = 24, int $height = 24): UxIcon
/**
* @internal
*/
public function toArray(): array
{
return new UxIcon(
content: $name,
type: self::TYPE_UX_ICON,
width: $width,
height: $height
);
return [
'type' => $this->type->value,
'width' => $this->width,
'height' => $this->height,
];
}

/**
* @param array{
* content: string,
* type: string,
* width: int,
* height: int,
* } $data
* @param array{ type: value-of<IconType>, width: positive-int, height: positive-int }
* &(array{ url: non-empty-string }
* |array{ html: non-empty-string }
* |array{ name: non-empty-string }) $data
*
* @internal
*/
public static function fromArray(array $data): static
{
return match ($data['type']) {
'url' => self::fromUrl($data['content'], (int) $data['width'], (int) $data['height']),
'inline-svg' => self::fromInlineSvg($data['content'], (int) $data['width'], (int) $data['height']),
'ux-icon' => self::fromUxIcon($data['content'], (int) $data['width'], (int) $data['height']),
IconType::Url->value => UrlIcon::fromArray($data),
IconType::Svg->value => SvgIcon::fromArray($data),
IconType::UxIcon->value => UxIcon::fromArray($data),
default => throw new InvalidArgumentException(\sprintf('Invalid icon type %s.', $data['type'])),
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@
/**
* Represents an inline SVG icon.
*
* @author Sylvain Blondeau <[email protected]>
* @author Hugo Alliaume <[email protected]>
*/
class InlineSvg extends Icon
enum IconType: string
{
case Url = 'url';
case Svg = 'svg';
case UxIcon = 'ux-icon';
}
56 changes: 56 additions & 0 deletions src/Map/src/Icon/SvgIcon.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\UX\Map\Icon;

/**
* Represents an inline SVG icon.
*
* @author Sylvain Blondeau <[email protected]>
* @author Hugo Alliaume <[email protected]>
*
* @internal
*/
class SvgIcon extends Icon
{
/**
* @param non-empty-string $html
* @param positive-int $width
* @param positive-int $height
*/
protected function __construct(
protected string $html,
int $width = 24,
int $height = 24,
) {
parent::__construct(IconType::Svg, $width, $height);
}

/**
* @param array{ html: string, width: positive-int, height: positive-int } $data
*/
public static function fromArray(array $data): static
{
return new self(
html: $data['html'],
width: $data['width'],
height: $data['height'],
);
}

public function toArray(): array
{
return [
...parent::toArray(),
'html' => $this->html,
];
}
}
21 changes: 0 additions & 21 deletions src/Map/src/Icon/Url.php

This file was deleted.

53 changes: 53 additions & 0 deletions src/Map/src/Icon/UrlIcon.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\UX\Map\Icon;

/**
* Represents an URL icon.
*
* @author Sylvain Blondeau <[email protected]>
* @author Hugo Alliaume <[email protected]>
*
* @internal
*/
class UrlIcon extends Icon
{
/**
* @param non-empty-string $url
* @param positive-int $width
* @param positive-int $height
*/
protected function __construct(
protected string $url,
int $width = 24,
int $height = 24,
) {
parent::__construct(IconType::Url, $width, $height);
}

public static function fromArray(array $data): static
{
return new self(
url: $data['url'],
width: $data['width'],
height: $data['height'],
);
}

public function toArray(): array
{
return [
...parent::toArray(),
'url' => $this->url,
];
}
}
34 changes: 33 additions & 1 deletion src/Map/src/Icon/UxIcon.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,42 @@
namespace Symfony\UX\Map\Icon;

/**
* Represents an ux icon.
* Represents an UX icon.
*
* @author Sylvain Blondeau <[email protected]>
* @author Hugo Alliaume <[email protected]>
*
* @internal
*/
class UxIcon extends Icon
{
/**
* @param non-empty-string $name
* @param positive-int $width
* @param positive-int $height
*/
protected function __construct(
protected string $name,
int $width = 24,
int $height = 24,
) {
parent::__construct(IconType::UxIcon, $width, $height);
}

public static function fromArray(array $data): static
{
return new self(
name: $data['name'],
width: $data['width'],
height: $data['height'],
);
}

public function toArray(): array
{
return [
...parent::toArray(),
'name' => $this->name,
];
}
}
Loading
Loading