Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fb58c50f18 | |||
| 621ce8dc8b |
@@ -9,7 +9,7 @@ use Contao\StringUtil;
|
|||||||
$GLOBALS['TL_DCA']['tl_module']['palettes']['member_organizations'] = '{title_legend},name,headline,type;{eventmanager_legend},editPage;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID';
|
$GLOBALS['TL_DCA']['tl_module']['palettes']['member_organizations'] = '{title_legend},name,headline,type;{eventmanager_legend},editPage;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID';
|
||||||
$GLOBALS['TL_DCA']['tl_module']['palettes']['member_events'] = '{title_legend},name,headline,type;{eventmanager_legend},editPage;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID';
|
$GLOBALS['TL_DCA']['tl_module']['palettes']['member_events'] = '{title_legend},name,headline,type;{eventmanager_legend},editPage;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID';
|
||||||
$GLOBALS['TL_DCA']['tl_module']['palettes']['event_filter'] = '{title_legend},name,headline,type;{eventmanager_legend},cal_calendar,eventListDomId;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID';
|
$GLOBALS['TL_DCA']['tl_module']['palettes']['event_filter'] = '{title_legend},name,headline,type;{eventmanager_legend},cal_calendar,eventListDomId;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID';
|
||||||
$GLOBALS['TL_DCA']['tl_module']['palettes']['eventmanager_map'] = '{title_legend},name,headline,type;{eventmanager_legend},mapShowOrganizations,mapShowExternalOrganizations,mapShowEvents,mapEventColor,mapOrganizationColorScheme,mapCenterMode;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID';
|
$GLOBALS['TL_DCA']['tl_module']['palettes']['eventmanager_map'] = '{title_legend},name,headline,type;{eventmanager_legend},mapShowOrganizations,organizationTypeTags,mapShowExternalOrganizations,mapShowEvents,mapEventColor,mapOrganizationColor,mapCenterMode;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID';
|
||||||
$GLOBALS['TL_DCA']['tl_module']['palettes']['organization_edit'] = '{title_legend},name,headline,type;{eventmanager_legend},listPage,logoFolder,organizationTypeTags;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID';
|
$GLOBALS['TL_DCA']['tl_module']['palettes']['organization_edit'] = '{title_legend},name,headline,type;{eventmanager_legend},listPage,logoFolder,organizationTypeTags;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID';
|
||||||
$GLOBALS['TL_DCA']['tl_module']['palettes']['event_edit'] = '{title_legend},name,headline,type;{eventmanager_legend},listPage,eventFolder,termsPage,frontendAuthorId,frontendArchiveId,eventTypeTags;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID';
|
$GLOBALS['TL_DCA']['tl_module']['palettes']['event_edit'] = '{title_legend},name,headline,type;{eventmanager_legend},listPage,eventFolder,termsPage,frontendAuthorId,frontendArchiveId,eventTypeTags;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID';
|
||||||
|
|
||||||
@@ -195,12 +195,12 @@ $GLOBALS['TL_DCA']['tl_module']['fields']['mapEventColor'] = [
|
|||||||
'sql' => ['type' => 'string', 'length' => 7, 'default' => '#BC5067'],
|
'sql' => ['type' => 'string', 'length' => 7, 'default' => '#BC5067'],
|
||||||
];
|
];
|
||||||
|
|
||||||
$GLOBALS['TL_DCA']['tl_module']['fields']['mapOrganizationColorScheme'] = [
|
$GLOBALS['TL_DCA']['tl_module']['fields']['mapOrganizationColor'] = [
|
||||||
'label' => &$GLOBALS['TL_LANG']['tl_module']['mapOrganizationColorScheme'],
|
'label' => &$GLOBALS['TL_LANG']['tl_module']['mapOrganizationColor'],
|
||||||
'exclude' => true,
|
'exclude' => true,
|
||||||
'inputType' => 'text',
|
'inputType' => 'text',
|
||||||
'eval' => ['maxlength' => 1024, 'tl_class' => 'clr long'],
|
'eval' => ['maxlength' => 7, 'rgxp' => 'hexcolor', 'colorpicker' => true, 'tl_class' => 'w50'],
|
||||||
'sql' => ['type' => 'string', 'length' => 1024, 'default' => ''],
|
'sql' => ['type' => 'string', 'length' => 7, 'default' => '#BC5067'],
|
||||||
];
|
];
|
||||||
|
|
||||||
$GLOBALS['TL_DCA']['tl_module']['fields']['mapCenterMode'] = [
|
$GLOBALS['TL_DCA']['tl_module']['fields']['mapCenterMode'] = [
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ $GLOBALS['TL_LANG']['tl_module']['mapShowOrganizations'] = ['Organisationen anze
|
|||||||
$GLOBALS['TL_LANG']['tl_module']['mapShowExternalOrganizations'] = ['Externe Organisationen anzeigen', 'Wenn aktiviert, werden externe Organisationen (isExternal=1) zusätzlich auf der Karte dargestellt. Standard: nein.'];
|
$GLOBALS['TL_LANG']['tl_module']['mapShowExternalOrganizations'] = ['Externe Organisationen anzeigen', 'Wenn aktiviert, werden externe Organisationen (isExternal=1) zusätzlich auf der Karte dargestellt. Standard: nein.'];
|
||||||
$GLOBALS['TL_LANG']['tl_module']['mapShowEvents'] = ['Veranstaltungen anzeigen', 'Wenn aktiviert, werden Event- (inkl. Orts-) Marker auf der Karte dargestellt.'];
|
$GLOBALS['TL_LANG']['tl_module']['mapShowEvents'] = ['Veranstaltungen anzeigen', 'Wenn aktiviert, werden Event- (inkl. Orts-) Marker auf der Karte dargestellt.'];
|
||||||
$GLOBALS['TL_LANG']['tl_module']['mapEventColor'] = ['Event-Farbe (Kreise/Linien)', 'Farbe für Event-Cluster, Event-Punkte und Spiderfy-Verbindungslinien (Hex, z. B. #BC5067).'];
|
$GLOBALS['TL_LANG']['tl_module']['mapEventColor'] = ['Event-Farbe (Kreise/Linien)', 'Farbe für Event-Cluster, Event-Punkte und Spiderfy-Verbindungslinien (Hex, z. B. #BC5067).'];
|
||||||
$GLOBALS['TL_LANG']['tl_module']['mapOrganizationColorScheme'] = ['Farbschema (Organisationstypen)', 'Kommagetrennte Farben für Organisationstypen/Tags, z. B. ff6600,77dd33,ff99bb. Markerfarbe richtet sich nach dem ersten Tag.'];
|
$GLOBALS['TL_LANG']['tl_module']['mapOrganizationColor'] = ['Organisationsfarbe', 'Einheitliche Farbe für alle Organisations-Marker (Hex, z. B. #BC5067).'];
|
||||||
$GLOBALS['TL_LANG']['tl_module']['mapCenterMode'] = ['Karten-Zentrierung', 'Wählen Sie, ob die Karte anhand der Marker oder mit festen Koordinaten zentriert werden soll.'];
|
$GLOBALS['TL_LANG']['tl_module']['mapCenterMode'] = ['Karten-Zentrierung', 'Wählen Sie, ob die Karte anhand der Marker oder mit festen Koordinaten zentriert werden soll.'];
|
||||||
$GLOBALS['TL_LANG']['tl_module']['mapCenterMode_options'] = [
|
$GLOBALS['TL_LANG']['tl_module']['mapCenterMode_options'] = [
|
||||||
'markers' => 'Anhand der Marker (alle Marker sichtbar)',
|
'markers' => 'Anhand der Marker (alle Marker sichtbar)',
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ $GLOBALS['TL_LANG']['tl_module']['mapShowOrganizations'] = ['Show organizations'
|
|||||||
$GLOBALS['TL_LANG']['tl_module']['mapShowExternalOrganizations'] = ['Show external organizations', 'If enabled, external organizations (isExternal=1) are additionally rendered on the map. Default: no.'];
|
$GLOBALS['TL_LANG']['tl_module']['mapShowExternalOrganizations'] = ['Show external organizations', 'If enabled, external organizations (isExternal=1) are additionally rendered on the map. Default: no.'];
|
||||||
$GLOBALS['TL_LANG']['tl_module']['mapShowEvents'] = ['Show events', 'If enabled, event markers (including related locations) are rendered on the map.'];
|
$GLOBALS['TL_LANG']['tl_module']['mapShowEvents'] = ['Show events', 'If enabled, event markers (including related locations) are rendered on the map.'];
|
||||||
$GLOBALS['TL_LANG']['tl_module']['mapEventColor'] = ['Event color (circles/lines)', 'Color for event clusters, event points and spiderfy connector lines (hex, e.g. #BC5067).'];
|
$GLOBALS['TL_LANG']['tl_module']['mapEventColor'] = ['Event color (circles/lines)', 'Color for event clusters, event points and spiderfy connector lines (hex, e.g. #BC5067).'];
|
||||||
$GLOBALS['TL_LANG']['tl_module']['mapOrganizationColorScheme'] = ['Color scheme (organization types)', 'Comma-separated colors for organization type tags, e.g. ff6600,77dd33,ff99bb. Marker color follows the first tag.'];
|
$GLOBALS['TL_LANG']['tl_module']['mapOrganizationColor'] = ['Organization color', 'Unified color for all organization markers (hex, e.g. #BC5067).'];
|
||||||
$GLOBALS['TL_LANG']['tl_module']['mapCenterMode'] = ['Map centering', 'Choose whether the map should center by markers or fixed coordinates.'];
|
$GLOBALS['TL_LANG']['tl_module']['mapCenterMode'] = ['Map centering', 'Choose whether the map should center by markers or fixed coordinates.'];
|
||||||
$GLOBALS['TL_LANG']['tl_module']['mapCenterMode_options'] = [
|
$GLOBALS['TL_LANG']['tl_module']['mapCenterMode_options'] = [
|
||||||
'markers' => 'By markers (fit all visible markers)',
|
'markers' => 'By markers (fit all visible markers)',
|
||||||
|
|||||||
@@ -9,12 +9,13 @@
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="eventmanager-map-filter__toggle"
|
class="eventmanager-map-filter__toggle is-expanded"
|
||||||
data-map-filter-toggle="1"
|
data-map-filter-toggle="1"
|
||||||
aria-expanded="true"
|
aria-expanded="true"
|
||||||
aria-controls="{{ mapFilterGroupId|e('html_attr') }}"
|
aria-controls="{{ mapFilterGroupId|e('html_attr') }}"
|
||||||
>
|
>
|
||||||
Bereiche ausblenden
|
<span class="eventmanager-map-filter__toggle-label eventmanager-map-filter__toggle-label--expand">Filter einblenden</span>
|
||||||
|
<span class="eventmanager-map-filter__toggle-label eventmanager-map-filter__toggle-label--collapse">Filter ausblenden</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -23,29 +24,43 @@
|
|||||||
role="group"
|
role="group"
|
||||||
aria-label="Organisationstypen"
|
aria-label="Organisationstypen"
|
||||||
>
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="eventmanager-map-filter__tag is-active"
|
||||||
|
data-map-filter-action="all"
|
||||||
|
aria-pressed="true"
|
||||||
|
>Alle</button>
|
||||||
|
|
||||||
{% if tags is iterable and tags|length > 0 %}
|
{% if tags is iterable and tags|length > 0 %}
|
||||||
{% for tag in tags %}
|
{% for tag in tags %}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="eventmanager-map-filter__tag is-active"
|
class="eventmanager-map-filter__tag"
|
||||||
data-map-tag-filter="{{ tag.id|e('html_attr') }}"
|
data-map-tag-filter="{{ tag.id|e('html_attr') }}"
|
||||||
aria-pressed="true"
|
aria-pressed="false"
|
||||||
>{{ tag.label|e }}</button>
|
>{{ tag.label|e }}</button>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="eventmanager-map-filter__tag is-active"
|
class="eventmanager-map-filter__tag"
|
||||||
data-map-event-toggle="1"
|
data-map-event-toggle="1"
|
||||||
aria-pressed="true"
|
aria-pressed="false"
|
||||||
>Veranstaltungen</button>
|
>Veranstaltungen</button>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="eventmanager-map-filter__actions">
|
<button
|
||||||
<button type="button" data-map-filter-action="all">Alle auswählen</button>
|
type="button"
|
||||||
<button type="button" data-map-filter-action="none">Alle abwählen</button>
|
class="eventmanager-map-filter__tag"
|
||||||
<button type="button" data-map-style-mode="street" aria-pressed="true">Straße</button>
|
data-map-style-mode="street"
|
||||||
<button type="button" data-map-style-mode="satellite" aria-pressed="false">Satellit</button>
|
aria-pressed="true"
|
||||||
|
>Straße</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="eventmanager-map-filter__tag"
|
||||||
|
data-map-style-mode="satellite"
|
||||||
|
aria-pressed="false"
|
||||||
|
>Satellit</button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -57,7 +72,7 @@
|
|||||||
data-map-style="{{ mapStyleUrl|e('html_attr') }}"
|
data-map-style="{{ mapStyleUrl|e('html_attr') }}"
|
||||||
data-map-data-id="{{ mapDataElementId|e('html_attr') }}"
|
data-map-data-id="{{ mapDataElementId|e('html_attr') }}"
|
||||||
data-map-event-color="{{ mapEventColor|default('#BC5067')|e('html_attr') }}"
|
data-map-event-color="{{ mapEventColor|default('#BC5067')|e('html_attr') }}"
|
||||||
data-map-organization-colors="{{ mapOrganizationColorScheme|default('')|e('html_attr') }}"
|
data-map-organization-color="{{ mapOrganizationColor|default('#BC5067')|e('html_attr') }}"
|
||||||
data-map-center-mode="{{ mapCenterMode|default('markers')|e('html_attr') }}"
|
data-map-center-mode="{{ mapCenterMode|default('markers')|e('html_attr') }}"
|
||||||
data-map-center-lat="{{ mapCenterLat|default('')|e('html_attr') }}"
|
data-map-center-lat="{{ mapCenterLat|default('')|e('html_attr') }}"
|
||||||
data-map-center-lng="{{ mapCenterLng|default('')|e('html_attr') }}"
|
data-map-center-lng="{{ mapCenterLng|default('')|e('html_attr') }}"
|
||||||
@@ -71,8 +86,7 @@
|
|||||||
margin-bottom: .75rem;
|
margin-bottom: .75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.eventmanager-map-filter__group,
|
.eventmanager-map-filter__group {
|
||||||
.eventmanager-map-filter__actions {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: .5rem;
|
gap: .5rem;
|
||||||
@@ -89,4 +103,4 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script type="application/json" id="{{ mapDataElementId|e('html_attr') }}">{{ mapItemsJson|raw }}</script>
|
<script type="application/json" id="{{ mapDataElementId|e('html_attr') }}">{{ mapItemsJson|raw }}</script>
|
||||||
<script type="module" src="/bundles/mummertmediaeventmanager/assets/map-module.js"></script>
|
<script type="module" src="/bundles/mummertmediaeventmanager/assets/map-module.js?v=20260226"></script>
|
||||||
|
|||||||
+100
-111
@@ -199,70 +199,6 @@ const normalizeHexColor = (value, fallback = DEFAULT_EVENT_COLOR) => {
|
|||||||
return fallback;
|
return fallback;
|
||||||
};
|
};
|
||||||
|
|
||||||
const normalizeHexColorOrNull = (value) => {
|
|
||||||
const fallback = '__INVALID__';
|
|
||||||
const normalized = normalizeHexColor(value, fallback);
|
|
||||||
|
|
||||||
return normalized === fallback ? null : normalized;
|
|
||||||
};
|
|
||||||
|
|
||||||
const parseOrganizationColorScheme = (value) => {
|
|
||||||
const raw = String(value || '').trim();
|
|
||||||
|
|
||||||
if ('' === raw) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return raw
|
|
||||||
.split(',')
|
|
||||||
.map((part) => normalizeHexColorOrNull(part))
|
|
||||||
.filter((part) => null !== part);
|
|
||||||
};
|
|
||||||
|
|
||||||
const buildOrganizationTagColorMap = (container, colors, organizationItems) => {
|
|
||||||
if (!colors.length) {
|
|
||||||
return {
|
|
||||||
byTagId: {},
|
|
||||||
fallbackColor: null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const fallbackColor = colors[0];
|
|
||||||
const wrapperId = container.dataset.mapFilterWrapperId || '';
|
|
||||||
const wrapper = wrapperId ? document.getElementById(wrapperId) : null;
|
|
||||||
let orderedTagIds = [];
|
|
||||||
|
|
||||||
if (wrapper) {
|
|
||||||
orderedTagIds = Array.from(wrapper.querySelectorAll('[data-map-tag-filter]'))
|
|
||||||
.map((button) => String(button.dataset.mapTagFilter || '').trim())
|
|
||||||
.filter((value) => /^\d+$/.test(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!orderedTagIds.length) {
|
|
||||||
const seen = new Set();
|
|
||||||
|
|
||||||
organizationItems.forEach((item) => {
|
|
||||||
const firstTagId = String(item?.extra?.organizationTagIds?.[0] || '').trim();
|
|
||||||
|
|
||||||
if (/^\d+$/.test(firstTagId) && !seen.has(firstTagId)) {
|
|
||||||
seen.add(firstTagId);
|
|
||||||
orderedTagIds.push(firstTagId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const byTagId = {};
|
|
||||||
|
|
||||||
orderedTagIds.forEach((tagId, index) => {
|
|
||||||
byTagId[tagId] = colors[index] || fallbackColor;
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
byTagId,
|
|
||||||
fallbackColor,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const applyDefaultProjection = (map) => {
|
const applyDefaultProjection = (map) => {
|
||||||
if (!map || typeof map.setProjection !== 'function') {
|
if (!map || typeof map.setProjection !== 'function') {
|
||||||
return;
|
return;
|
||||||
@@ -441,21 +377,18 @@ const toFeature = (item) => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const initOrganizationMarkers = (map, organizationItems, organizationTagColorMap) => {
|
const initOrganizationMarkers = (map, organizationItems, organizationColor) => {
|
||||||
if (!organizationItems.length) {
|
if (!organizationItems.length) {
|
||||||
return {
|
return {
|
||||||
setActiveTagIds: () => {},
|
setOnlyTagId: () => {},
|
||||||
setAllVisible: () => {},
|
setAllVisible: () => {},
|
||||||
|
setVisible: () => {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const markerEntries = organizationItems.map((item) => {
|
const markerEntries = organizationItems.map((item) => {
|
||||||
const popupHtml = popupHtmlFor(item);
|
const popupHtml = popupHtmlFor(item);
|
||||||
const primaryTagId = String(item?.extra?.organizationTagIds?.[0] || '').trim();
|
const marker = new maplibregl.Marker(organizationColor ? { color: organizationColor } : undefined)
|
||||||
const configuredColor = /^\d+$/.test(primaryTagId)
|
|
||||||
? (organizationTagColorMap?.byTagId?.[primaryTagId] || organizationTagColorMap?.fallbackColor || null)
|
|
||||||
: null;
|
|
||||||
const marker = new maplibregl.Marker(configuredColor ? { color: configuredColor } : undefined)
|
|
||||||
.setLngLat([item.longitude, item.latitude]);
|
.setLngLat([item.longitude, item.latitude]);
|
||||||
|
|
||||||
if (popupHtml) {
|
if (popupHtml) {
|
||||||
@@ -471,8 +404,9 @@ const initOrganizationMarkers = (map, organizationItems, organizationTagColorMap
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
setActiveTagIds: (activeTagIds) => {
|
setOnlyTagId: (activeTagId) => {
|
||||||
const activeSet = new Set(normalizeTagIds(activeTagIds));
|
const normalizedTagId = String(activeTagId ?? '').trim();
|
||||||
|
const hasActiveTag = /^\d+$/.test(normalizedTagId);
|
||||||
|
|
||||||
markerEntries.forEach((entry) => {
|
markerEntries.forEach((entry) => {
|
||||||
const markerElement = entry.marker.getElement();
|
const markerElement = entry.marker.getElement();
|
||||||
@@ -482,8 +416,9 @@ const initOrganizationMarkers = (map, organizationItems, organizationTagColorMap
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hasTags = entry.tagIds.length > 0;
|
const hasTags = entry.tagIds.length > 0;
|
||||||
const isVisible = !hasTags
|
const isVisible = !hasActiveTag
|
||||||
|| entry.tagIds.some((tagId) => activeSet.has(tagId));
|
|| !hasTags
|
||||||
|
|| entry.tagIds.includes(normalizedTagId);
|
||||||
|
|
||||||
markerElement.style.display = isVisible ? '' : 'none';
|
markerElement.style.display = isVisible ? '' : 'none';
|
||||||
});
|
});
|
||||||
@@ -499,6 +434,17 @@ const initOrganizationMarkers = (map, organizationItems, organizationTagColorMap
|
|||||||
markerElement.style.display = '';
|
markerElement.style.display = '';
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
setVisible: (isVisible) => {
|
||||||
|
markerEntries.forEach((entry) => {
|
||||||
|
const markerElement = entry.marker.getElement();
|
||||||
|
|
||||||
|
if (!markerElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
markerElement.style.display = isVisible ? '' : 'none';
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -517,17 +463,13 @@ const bindExternalTagFilters = (container, map, organizationMarkerManager, event
|
|||||||
|
|
||||||
const tagButtons = Array.from(wrapper.querySelectorAll('[data-map-tag-filter]'));
|
const tagButtons = Array.from(wrapper.querySelectorAll('[data-map-tag-filter]'));
|
||||||
|
|
||||||
const collectActiveTagIds = () => tagButtons
|
|
||||||
.filter((button) => button.getAttribute('aria-pressed') === 'true')
|
|
||||||
.map((button) => String(button.dataset.mapTagFilter || '').trim())
|
|
||||||
.filter((value) => /^\d+$/.test(value));
|
|
||||||
|
|
||||||
const setButtonState = (button, isActive) => {
|
const setButtonState = (button, isActive) => {
|
||||||
button.setAttribute('aria-pressed', isActive ? 'true' : 'false');
|
button.setAttribute('aria-pressed', isActive ? 'true' : 'false');
|
||||||
button.classList.toggle('is-active', isActive);
|
button.classList.toggle('is-active', isActive);
|
||||||
};
|
};
|
||||||
|
|
||||||
const eventToggleButton = wrapper.querySelector('[data-map-event-toggle="1"]');
|
const eventToggleButton = wrapper.querySelector('[data-map-event-toggle="1"]');
|
||||||
|
const actionAll = wrapper.querySelector('[data-map-filter-action="all"]');
|
||||||
|
|
||||||
const setEventButtonState = (isActive) => {
|
const setEventButtonState = (isActive) => {
|
||||||
if (!eventToggleButton) {
|
if (!eventToggleButton) {
|
||||||
@@ -538,48 +480,80 @@ const bindExternalTagFilters = (container, map, organizationMarkerManager, event
|
|||||||
eventToggleButton.classList.toggle('is-active', isActive);
|
eventToggleButton.classList.toggle('is-active', isActive);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const setAllButtonState = (isActive) => {
|
||||||
|
if (!actionAll) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
actionAll.setAttribute('aria-pressed', isActive ? 'true' : 'false');
|
||||||
|
actionAll.classList.toggle('is-active', isActive);
|
||||||
|
};
|
||||||
|
|
||||||
|
let activeTagId = null;
|
||||||
|
let eventsOnly = false;
|
||||||
|
|
||||||
const applyFilter = () => {
|
const applyFilter = () => {
|
||||||
if (!tagButtons.length) {
|
if (eventsOnly) {
|
||||||
organizationMarkerManager.setAllVisible();
|
organizationMarkerManager.setVisible(false);
|
||||||
|
setAllButtonState(false);
|
||||||
|
|
||||||
|
if (eventLayerManager) {
|
||||||
|
eventLayerManager.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
organizationMarkerManager.setActiveTagIds(collectActiveTagIds());
|
organizationMarkerManager.setVisible(true);
|
||||||
|
|
||||||
|
if (activeTagId) {
|
||||||
|
organizationMarkerManager.setOnlyTagId(activeTagId);
|
||||||
|
setAllButtonState(false);
|
||||||
|
|
||||||
|
if (eventLayerManager) {
|
||||||
|
eventLayerManager.setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
organizationMarkerManager.setAllVisible();
|
||||||
|
setAllButtonState(true);
|
||||||
|
|
||||||
|
if (eventLayerManager) {
|
||||||
|
eventLayerManager.setVisible(true);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
tagButtons.forEach((button) => {
|
tagButtons.forEach((button) => {
|
||||||
button.addEventListener('click', () => {
|
button.addEventListener('click', () => {
|
||||||
|
const clickedTagId = String(button.dataset.mapTagFilter || '').trim();
|
||||||
|
|
||||||
|
if (!/^\d+$/.test(clickedTagId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const isActive = button.getAttribute('aria-pressed') === 'true';
|
const isActive = button.getAttribute('aria-pressed') === 'true';
|
||||||
setButtonState(button, !isActive);
|
activeTagId = isActive ? null : clickedTagId;
|
||||||
|
eventsOnly = false;
|
||||||
|
|
||||||
|
tagButtons.forEach((otherButton) => {
|
||||||
|
const otherTagId = String(otherButton.dataset.mapTagFilter || '').trim();
|
||||||
|
setButtonState(otherButton, !!activeTagId && otherTagId === activeTagId);
|
||||||
|
});
|
||||||
|
|
||||||
|
setEventButtonState(false);
|
||||||
applyFilter();
|
applyFilter();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const actionAll = wrapper.querySelector('[data-map-filter-action="all"]');
|
|
||||||
const actionNone = wrapper.querySelector('[data-map-filter-action="none"]');
|
|
||||||
|
|
||||||
if (actionAll) {
|
if (actionAll) {
|
||||||
actionAll.addEventListener('click', () => {
|
actionAll.addEventListener('click', () => {
|
||||||
tagButtons.forEach((button) => setButtonState(button, true));
|
activeTagId = null;
|
||||||
applyFilter();
|
eventsOnly = false;
|
||||||
|
|
||||||
if (eventLayerManager) {
|
|
||||||
setEventButtonState(true);
|
|
||||||
eventLayerManager.setVisible(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (actionNone) {
|
|
||||||
actionNone.addEventListener('click', () => {
|
|
||||||
tagButtons.forEach((button) => setButtonState(button, false));
|
tagButtons.forEach((button) => setButtonState(button, false));
|
||||||
applyFilter();
|
|
||||||
|
|
||||||
if (eventLayerManager) {
|
|
||||||
setEventButtonState(false);
|
setEventButtonState(false);
|
||||||
eventLayerManager.setVisible(false);
|
applyFilter();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -593,17 +567,30 @@ const bindExternalTagFilters = (container, map, organizationMarkerManager, event
|
|||||||
|
|
||||||
toggleButton.setAttribute('aria-expanded', nextExpanded ? 'true' : 'false');
|
toggleButton.setAttribute('aria-expanded', nextExpanded ? 'true' : 'false');
|
||||||
filterGroup.hidden = !nextExpanded;
|
filterGroup.hidden = !nextExpanded;
|
||||||
toggleButton.textContent = nextExpanded ? 'Bereiche ausblenden' : 'Bereiche anzeigen';
|
toggleButton.classList.toggle('is-expanded', nextExpanded);
|
||||||
|
toggleButton.classList.toggle('is-collapsed', !nextExpanded);
|
||||||
|
|
||||||
|
wrapper.classList.toggle('is-filter-expanded', nextExpanded);
|
||||||
|
wrapper.classList.toggle('is-filter-collapsed', !nextExpanded);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const initiallyExpanded = toggleButton.getAttribute('aria-expanded') !== 'false';
|
||||||
|
filterGroup.hidden = !initiallyExpanded;
|
||||||
|
toggleButton.classList.toggle('is-expanded', initiallyExpanded);
|
||||||
|
toggleButton.classList.toggle('is-collapsed', !initiallyExpanded);
|
||||||
|
wrapper.classList.toggle('is-filter-expanded', initiallyExpanded);
|
||||||
|
wrapper.classList.toggle('is-filter-collapsed', !initiallyExpanded);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eventToggleButton && eventLayerManager) {
|
if (eventToggleButton && eventLayerManager) {
|
||||||
eventToggleButton.addEventListener('click', () => {
|
eventToggleButton.addEventListener('click', () => {
|
||||||
const isActive = eventToggleButton.getAttribute('aria-pressed') === 'true';
|
const isActive = eventToggleButton.getAttribute('aria-pressed') === 'true';
|
||||||
const nextActive = !isActive;
|
eventsOnly = !isActive;
|
||||||
|
activeTagId = null;
|
||||||
|
|
||||||
setEventButtonState(nextActive);
|
tagButtons.forEach((button) => setButtonState(button, false));
|
||||||
eventLayerManager.setVisible(nextActive);
|
setEventButtonState(eventsOnly);
|
||||||
|
applyFilter();
|
||||||
});
|
});
|
||||||
} else if (eventToggleButton) {
|
} else if (eventToggleButton) {
|
||||||
eventToggleButton.disabled = true;
|
eventToggleButton.disabled = true;
|
||||||
@@ -1137,10 +1124,12 @@ const initSingleMap = (container) => {
|
|||||||
const locationItems = items.filter((item) => item.type === 'location');
|
const locationItems = items.filter((item) => item.type === 'location');
|
||||||
const eventItems = items.filter((item) => item.type === 'event');
|
const eventItems = items.filter((item) => item.type === 'event');
|
||||||
const allCoordinates = items.map((item) => [item.longitude, item.latitude]);
|
const allCoordinates = items.map((item) => [item.longitude, item.latitude]);
|
||||||
const organizationColors = parseOrganizationColorScheme(container.dataset.mapOrganizationColors);
|
const organizationColor = normalizeHexColor(
|
||||||
const organizationTagColorMap = buildOrganizationTagColorMap(container, organizationColors, organizationItems);
|
container.dataset.mapOrganizationColor,
|
||||||
|
eventColor,
|
||||||
|
);
|
||||||
|
|
||||||
const organizationMarkerManager = initOrganizationMarkers(map, organizationItems, organizationTagColorMap);
|
const organizationMarkerManager = initOrganizationMarkers(map, organizationItems, organizationColor);
|
||||||
const locationLayerManager = initLocationLayers(map, locationItems, eventColor);
|
const locationLayerManager = initLocationLayers(map, locationItems, eventColor);
|
||||||
let eventLayerManager = null;
|
let eventLayerManager = null;
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use Contao\CoreBundle\Controller\FrontendModule\AbstractFrontendModuleController
|
|||||||
use Contao\CoreBundle\DependencyInjection\Attribute\AsFrontendModule;
|
use Contao\CoreBundle\DependencyInjection\Attribute\AsFrontendModule;
|
||||||
use Contao\CoreBundle\Twig\FragmentTemplate;
|
use Contao\CoreBundle\Twig\FragmentTemplate;
|
||||||
use Contao\ModuleModel;
|
use Contao\ModuleModel;
|
||||||
|
use Contao\StringUtil;
|
||||||
use MummertMedia\EventManagerBundle\Service\MapModuleDataProvider;
|
use MummertMedia\EventManagerBundle\Service\MapModuleDataProvider;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
@@ -33,9 +34,13 @@ class EventMapController extends AbstractFrontendModuleController
|
|||||||
$showOrganizations = '1' === (string) ($model->mapShowOrganizations ?? '');
|
$showOrganizations = '1' === (string) ($model->mapShowOrganizations ?? '');
|
||||||
$showExternalOrganizations = '1' === (string) ($model->mapShowExternalOrganizations ?? '');
|
$showExternalOrganizations = '1' === (string) ($model->mapShowExternalOrganizations ?? '');
|
||||||
$showEvents = '1' === (string) ($model->mapShowEvents ?? '');
|
$showEvents = '1' === (string) ($model->mapShowEvents ?? '');
|
||||||
|
$selectedOrganizationTagIds = array_values(array_unique(array_filter(
|
||||||
|
array_map('intval', StringUtil::deserialize($model->organizationTypeTags ?? null, true)),
|
||||||
|
static fn (int $tagId): bool => $tagId > 0,
|
||||||
|
)));
|
||||||
$centerMode = (string) ($model->mapCenterMode ?? self::DEFAULT_CENTER_MODE);
|
$centerMode = (string) ($model->mapCenterMode ?? self::DEFAULT_CENTER_MODE);
|
||||||
$eventColor = $this->normalizeHexColor((string) ($model->mapEventColor ?? self::DEFAULT_EVENT_COLOR));
|
$eventColor = $this->normalizeHexColor((string) ($model->mapEventColor ?? self::DEFAULT_EVENT_COLOR));
|
||||||
$organizationColorScheme = trim((string) ($model->mapOrganizationColorScheme ?? ''));
|
$organizationColor = $this->normalizeHexColor((string) ($model->mapOrganizationColor ?? $eventColor), $eventColor);
|
||||||
|
|
||||||
if (!in_array($centerMode, ['markers', 'custom'], true)) {
|
if (!in_array($centerMode, ['markers', 'custom'], true)) {
|
||||||
$centerMode = self::DEFAULT_CENTER_MODE;
|
$centerMode = self::DEFAULT_CENTER_MODE;
|
||||||
@@ -48,20 +53,20 @@ class EventMapController extends AbstractFrontendModuleController
|
|||||||
$template->set('mapStyleUrl', self::MAP_STYLE_URL);
|
$template->set('mapStyleUrl', self::MAP_STYLE_URL);
|
||||||
$template->set('mapCenterMode', $centerMode);
|
$template->set('mapCenterMode', $centerMode);
|
||||||
$template->set('mapEventColor', $eventColor);
|
$template->set('mapEventColor', $eventColor);
|
||||||
$template->set('mapOrganizationColorScheme', $organizationColorScheme);
|
$template->set('mapOrganizationColor', $organizationColor);
|
||||||
$template->set('mapCenterLat', trim((string) ($model->mapCenterLat ?? '')));
|
$template->set('mapCenterLat', trim((string) ($model->mapCenterLat ?? '')));
|
||||||
$template->set('mapCenterLng', trim((string) ($model->mapCenterLng ?? '')));
|
$template->set('mapCenterLng', trim((string) ($model->mapCenterLng ?? '')));
|
||||||
$template->set('mapCenterZoom', (int) ($model->mapCenterZoom ?? 12));
|
$template->set('mapCenterZoom', (int) ($model->mapCenterZoom ?? 12));
|
||||||
$template->set('mapItemsJson', json_encode(
|
$template->set('mapItemsJson', json_encode(
|
||||||
$this->mapModuleDataProvider->getMapItems($showOrganizations, $showEvents, $showExternalOrganizations),
|
$this->mapModuleDataProvider->getMapItems($showOrganizations, $showEvents, $showExternalOrganizations, $selectedOrganizationTagIds),
|
||||||
\JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_APOS | \JSON_HEX_QUOT | \JSON_UNESCAPED_UNICODE | \JSON_THROW_ON_ERROR,
|
\JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_APOS | \JSON_HEX_QUOT | \JSON_UNESCAPED_UNICODE | \JSON_THROW_ON_ERROR,
|
||||||
));
|
));
|
||||||
$template->set('mapOrganizationTags', $this->mapModuleDataProvider->getOrganizationTags());
|
$template->set('mapOrganizationTags', $this->mapModuleDataProvider->getOrganizationTags($selectedOrganizationTagIds));
|
||||||
|
|
||||||
return $template->getResponse();
|
return $template->getResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function normalizeHexColor(string $value): string
|
private function normalizeHexColor(string $value, string $fallback = self::DEFAULT_EVENT_COLOR): string
|
||||||
{
|
{
|
||||||
$normalized = strtoupper(trim($value));
|
$normalized = strtoupper(trim($value));
|
||||||
|
|
||||||
@@ -91,6 +96,6 @@ class EventMapController extends AbstractFrontendModuleController
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::DEFAULT_EVENT_COLOR;
|
return $fallback;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,12 +26,17 @@ class MapModuleDataProvider
|
|||||||
/**
|
/**
|
||||||
* @return list<array{type:string,markerType:string,id:int,title:string,latitude:float,longitude:float,extra:array<string,mixed>}>
|
* @return list<array{type:string,markerType:string,id:int,title:string,latitude:float,longitude:float,extra:array<string,mixed>}>
|
||||||
*/
|
*/
|
||||||
public function getMapItems(bool $includeOrganizations = true, bool $includeEvents = true, bool $includeExternalOrganizations = false): array
|
public function getMapItems(bool $includeOrganizations = true, bool $includeEvents = true, bool $includeExternalOrganizations = false, array $selectedOrganizationTagIds = []): array
|
||||||
{
|
{
|
||||||
if (!$includeOrganizations && !$includeEvents) {
|
if (!$includeOrganizations && !$includeEvents) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$selectedOrganizationTagIds = array_values(array_unique(array_map(
|
||||||
|
static fn (int|string $tagId): string => (string) (int) $tagId,
|
||||||
|
array_filter($selectedOrganizationTagIds, static fn (int|string $tagId): bool => (int) $tagId > 0),
|
||||||
|
)));
|
||||||
|
|
||||||
$locationTable = $this->resolveExistingTable(['tl_location']);
|
$locationTable = $this->resolveExistingTable(['tl_location']);
|
||||||
$organizationTable = $this->resolveExistingTable(['tl_organization', 'tl_organisation']);
|
$organizationTable = $this->resolveExistingTable(['tl_organization', 'tl_organisation']);
|
||||||
$locationGeoColumns = null !== $locationTable ? $this->resolveGeoColumns($locationTable) : null;
|
$locationGeoColumns = null !== $locationTable ? $this->resolveGeoColumns($locationTable) : null;
|
||||||
@@ -76,6 +81,12 @@ class MapModuleDataProvider
|
|||||||
|
|
||||||
$seen['organisation'][$id] = true;
|
$seen['organisation'][$id] = true;
|
||||||
$tagData = $organizationTagMap[$id] ?? ['ids' => [], 'labels' => []];
|
$tagData = $organizationTagMap[$id] ?? ['ids' => [], 'labels' => []];
|
||||||
|
|
||||||
|
if ([] !== $selectedOrganizationTagIds
|
||||||
|
&& [] === array_intersect($selectedOrganizationTagIds, $tagData['ids'] ?? [])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$items[] = [
|
$items[] = [
|
||||||
'type' => 'organisation',
|
'type' => 'organisation',
|
||||||
'markerType' => $this->buildOrganizationMarkerType($tagData['ids']),
|
'markerType' => $this->buildOrganizationMarkerType($tagData['ids']),
|
||||||
@@ -152,12 +163,17 @@ class MapModuleDataProvider
|
|||||||
/**
|
/**
|
||||||
* @return list<array{id:int,label:string}>
|
* @return list<array{id:int,label:string}>
|
||||||
*/
|
*/
|
||||||
public function getOrganizationTags(): array
|
public function getOrganizationTags(array $selectedTagIds = []): array
|
||||||
{
|
{
|
||||||
if (!$this->tableExists('tl_tags')) {
|
if (!$this->tableExists('tl_tags')) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$selectedTagIds = array_values(array_unique(array_filter(
|
||||||
|
array_map('intval', $selectedTagIds),
|
||||||
|
static fn (int $tagId): bool => $tagId > 0,
|
||||||
|
)));
|
||||||
|
|
||||||
$columns = $this->getColumnMap('tl_tags');
|
$columns = $this->getColumnMap('tl_tags');
|
||||||
$labelColumn = isset($columns['title']) ? 'title' : (isset($columns['tag']) ? 'tag' : null);
|
$labelColumn = isset($columns['title']) ? 'title' : (isset($columns['tag']) ? 'tag' : null);
|
||||||
|
|
||||||
@@ -171,6 +187,12 @@ class MapModuleDataProvider
|
|||||||
->from('tl_tags', 't')
|
->from('tl_tags', 't')
|
||||||
->orderBy(sprintf('t.%s', $labelColumn), 'ASC');
|
->orderBy(sprintf('t.%s', $labelColumn), 'ASC');
|
||||||
|
|
||||||
|
if ([] !== $selectedTagIds) {
|
||||||
|
$qb
|
||||||
|
->andWhere('t.id IN (:selectedTagIds)')
|
||||||
|
->setParameter('selectedTagIds', $selectedTagIds, ArrayParameterType::INTEGER);
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($columns['invisible'])) {
|
if (isset($columns['invisible'])) {
|
||||||
$qb->andWhere("(t.invisible IS NULL OR t.invisible = '' OR t.invisible = '0')");
|
$qb->andWhere("(t.invisible IS NULL OR t.invisible = '' OR t.invisible = '0')");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user