Map

<!-- 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. */
  • Content:
    <?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()
                ,
            ]));
        }
    }
  • URL: /components/raw/map/Map.php
  • Filesystem Path: resources/Views/Organisms/map/Map.php
  • Size: 1 KB
  • Content:
    <?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 = '';
    }
  • URL: /components/raw/map/MapBlock.php
  • Filesystem Path: resources/Views/Organisms/map/MapBlock.php
  • Size: 310 Bytes
  • Content:
    <?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;
        }
    }
    
  • URL: /components/raw/map/MapViewModel.php
  • Filesystem Path: resources/Views/Organisms/map/MapViewModel.php
  • Size: 2.5 KB
  • Content:
    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: '&copy; <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)
        }
    }
  • URL: /components/raw/map/map.js
  • Filesystem Path: resources/Views/Organisms/map/map.js
  • Size: 2.4 KB
  • Content:
    .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;
        }
    }
    
  • URL: /components/raw/map/map.scss
  • Filesystem Path: resources/Views/Organisms/map/map.scss
  • Size: 2.6 KB

Map

{% include '@organisms/map/map.twig' with {

    } only %}

ACF

Fields areas -> select

headline -> atom