<!-- Error rendering component -->
<!-- TwigException: Unable to parse '=> "data-#{k}=#{v}")|join(' ') ~ ' ' : ''' at template position71 -->
<!-- Error: TwigException: Unable to parse '=> "data-#{k}=#{v}")|join(' ') ~ ' ' : ''' at template position71
at /builds/michaelhulsman/footballrs.net/htdocs/content/themes/footballrs.net/node_modules/@frctl/twig/src/adapter.js:156:24
at new Promise (<anonymous>)
at TwigAdapter.render (/builds/michaelhulsman/footballrs.net/htdocs/content/themes/footballrs.net/node_modules/@frctl/twig/src/adapter.js:134:16)
at ComponentSource._renderVariant (/builds/michaelhulsman/footballrs.net/htdocs/content/themes/footballrs.net/node_modules/@frctl/fractal/src/api/components/source.js:212:30)
at _renderVariant.next (<anonymous>)
at onFulfilled (/builds/michaelhulsman/footballrs.net/htdocs/content/themes/footballrs.net/node_modules/co/index.js:65:19) -->
{# @var map ThemeViews\Organisms\map\MapViewModel #}
{% extends '@atoms/section/section.twig' %}
{% set className = "map" %}
{% block content %}
<div class="flex items-start justify-center">
{% include '@atoms/button/button.twig' with {
cta: map.headline.headline,
type: 'x-btn--primary small',
className: 'mb-0.75'
} only %}
</div>
<div class="map-holder" style="height: 400px;" data-area='{{ map.area }}'></div>
<h4 class="font-roboto-bold text-primary text-center my-1">stay in touchdown</h4>
{% endblock %}
/* No context defined. */
<?php
namespace Theme\Views\Organisms\map;
use ACFToolkit\FieldClasses\Select;
use ACFToolkit\Types\CustomOrganism;
use Theme\Views\Atoms\headline\Headline;
class Map extends CustomOrganism
{
public string $name = 'map';
public string $label = 'Map';
public int $needsJs = 1;
public int $needsVue = 0;
public int $needsVueInitialisation = 0;
public function __construct($name = '', $title = '', $children = [])
{
parent::__construct($name, $title);
$this->addChildren(array_merge($children, [
(Select::getInstance('area', 'Area'))
->addChoices([
'team' => 'Teams',
'member' => 'Members',
'stadium' => 'Stadiums',
]),
...(Headline::getInstance('headline', 'Headline'))
->setMessage('Available placeholders', '{totalMemberCount} / {memberCount} / {stadiumCount} / {teamCount}')
->getChildren()
,
]));
}
}
<?php
namespace Theme\Views\Organisms\map;
use ACFToolkit\Types\Block;
class MapBlock extends Block
{
protected string $organismClassName = Map::class;
protected string $blockName = 'map';
protected string $blockTitle = 'Map';
public string $category = '';
protected string $icon = '';
}
<?php
namespace Theme\Views\Organisms\map;
use ACFToolkit\ViewModels\BaseViewModel;
use App\Http\Resources\MapTeaserResource;
use App\Models\Member;
use App\Models\Stadium;
use App\Models\Team;
use Theme\Views\Atoms\headline\HeadlineViewModel;
class MapViewModel extends BaseViewModel
{
protected $locatableRecords;
public function __construct(array $data, string $prefix = '')
{
parent::__construct($data, $prefix);
$mapping = config('datatypeMapping');
$results = [];
/** @var Stadium|Team|Member $class */
foreach([] as $area) {
//foreach($this->areas() as $area) {
if(!isset($results[$area])){
$results[$area] = [];
}
$class = $mapping[$area] ?? Team::class;
$results[$area] = MapTeaserResource::collection($class::getLocatableRecords())->resolve();
}
$this->locatableRecords = $results;
}
public function markers()
{
$mapping = config('datatypeMapping');
$results = [];
/** @var Stadium|Team|Member $class */
foreach([] as $area) {
$results = array_merge($results, $this->locatableRecords[$area]);
}
return $results;
}
public function area()
{
return $this->getData('area');
}
public function headline()
{
$result = (new HeadlineViewModel($this->data, 'headline'))->toArray();
if(isset($result['headline']) && !empty($result['headline'])){
$headline = $result['headline'];
$replacements = [
'{totalMemberCount}' => function($text){
return Member::active()->count() ?? 0;
},
'{memberCount}' => function($text){
return Member::geoLocatable()->count() ?? 0;
},
'{stadiumCount}' => function($text){
return Stadium::geoLocatable()->count() ?? 0;
},
'{teamCount}' => function($text){
return Team::geoLocatable()->count() ?? 0;
}
];
foreach($replacements as $pattern => $replacement){
if(str_contains($headline, $pattern)){
$headline = str_replace($pattern, $replacement($headline), $headline);
}
}
$result['headline'] = $headline;
}
return $result;
}
}
import {Map as LeafletMap} from 'leaflet/src/map';
import {FeatureGroup, TileLayer} from 'leaflet/src/layer';
import {Icon} from 'leaflet/src/layer';
import {Marker} from 'leaflet/src/layer';
import 'leaflet/dist/leaflet.css'
import {Control} from 'leaflet/src/control';
import MapService from '../../Assets/src/js/services/MapService';
export class Map {
static CSS_SELECTOR = '.map > .map-holder'
static init = () => {
document.querySelectorAll(Map.CSS_SELECTOR).forEach(map => {
new Map(map)
})
}
constructor(map)
{
this.el = map
this.area = map.dataset.area
this.markers = {}
this.icon = new Icon({
iconUrl: '/content/themes/footballrs.net/resources/Views/Assets/dist/images/marker.png',
iconSize: [16, 16],
})
this.map = new LeafletMap(this.el, {
zoomControl: false,
center: [51.20263954725433, 10.269985871726195],
zoom: 5,
maxZoom: 19,
minZoom: 6,
scrollWheelZoom: false
})
new TileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
noWrap: true,
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(this.map);
new Control.Zoom({
position: 'bottomright'
}).addTo(this.map);
this.map.on('moveend', this.getMarkerForBounds.bind(this))
this.getMarkerForBounds()
}
async getMarkerForBounds(){
const {data} = await MapService.getMarker({
bounds: this.map.getBounds(),
ids: Object.keys(this.markers),
area: this.area
})
for(const id in data){
const marker = data[id]
if(this.markers[marker.id]){
continue
}
const mapMarker = new Marker([marker.lat, marker.lng], {
icon: this.icon
})
mapMarker
.bindPopup(`<a href="${marker.link}">${marker.title}</a>`)
.on('click', e => {
this.map.setView(e.target.getLatLng(), Math.max(5, this.map.getZoom()));
})
this.markers[marker.id] = mapMarker
}
(new FeatureGroup(Object.values(this.markers))).addTo(this.map)
}
}
.map {
.leaflet-container {
@apply bg-white;
}
.leaflet-cluster-anim .leaflet-marker-icon, .leaflet-cluster-anim .leaflet-marker-shadow {
-webkit-transition: -webkit-transform 0.3s ease-out, opacity 0.3s ease-in;
-moz-transition: -moz-transform 0.3s ease-out, opacity 0.3s ease-in;
-o-transition: -o-transform 0.3s ease-out, opacity 0.3s ease-in;
transition: transform 0.3s ease-out, opacity 0.3s ease-in;
}
.leaflet-cluster-spider-leg {
/* stroke-dashoffset (duration and function) should match with leaflet-marker-icon transform in order to track it exactly */
-webkit-transition: -webkit-stroke-dashoffset 0.3s ease-out, -webkit-stroke-opacity 0.3s ease-in;
-moz-transition: -moz-stroke-dashoffset 0.3s ease-out, -moz-stroke-opacity 0.3s ease-in;
-o-transition: -o-stroke-dashoffset 0.3s ease-out, -o-stroke-opacity 0.3s ease-in;
transition: stroke-dashoffset 0.3s ease-out, stroke-opacity 0.3s ease-in;
}
.marker-cluster-small {
background-color: rgba(181, 226, 140, 0.6);
}
.marker-cluster-small div {
background-color: rgba(110, 204, 57, 0.6);
}
.marker-cluster-medium {
background-color: rgba(241, 211, 87, 0.6);
}
.marker-cluster-medium div {
background-color: rgba(240, 194, 12, 0.6);
}
.marker-cluster-large {
background-color: rgba(253, 156, 115, 0.6);
}
.marker-cluster-large div {
background-color: rgba(241, 128, 23, 0.6);
}
/* IE 6-8 fallback colors */
.leaflet-oldie .marker-cluster-small {
background-color: rgb(181, 226, 140);
}
.leaflet-oldie .marker-cluster-small div {
background-color: rgb(110, 204, 57);
}
.leaflet-oldie .marker-cluster-medium {
background-color: rgb(241, 211, 87);
}
.leaflet-oldie .marker-cluster-medium div {
background-color: rgb(240, 194, 12);
}
.leaflet-oldie .marker-cluster-large {
background-color: rgb(253, 156, 115);
}
.leaflet-oldie .marker-cluster-large div {
background-color: rgb(241, 128, 23);
}
.marker-cluster {
background-clip: padding-box;
border-radius: 20px;
}
.marker-cluster div {
width: 30px;
height: 30px;
margin-left: 5px;
margin-top: 5px;
text-align: center;
border-radius: 15px;
font: 12px "Helvetica Neue", Arial, Helvetica, sans-serif;
}
.marker-cluster span {
line-height: 30px;
}
}
{% include '@organisms/map/map.twig' with {
} only %}Fields areas -> select
headline -> atom