Components
51
Accordion Items Article Selection Author Info Basic Carousel Basic Hero Basic Map Blog Pull Out Contact Content Accordion Content Carousel Content Image Cta Cta Bar Cta Blocks Cta Collage Event Content Events Grid Example Find Firm Firm Search Firms By Town Gated Content Download Guides Carousel Hero History Homepage Hero Image Content Cta Image List Content Industries Job Content Job Listings Local Firm Carousel Our Firms Pages Carousel Partners Partners Slider People Listing Post Carousel Post Feed Pullquote Section Wrap Service List Split Content Stats Tax Guides Team Grid Title Logos Two Column Video Video Carousel Video Old

Find Firm

There are no ACF fields assigned to this component.


debug_fields();


?>

				
@import "../../resources/scss/util/variables";
@import "../../resources/scss/util/mixins";

// Location search
@import "../../resources/scss/elements/location-search";

.block-find-firm {
	position: relative;

	.container {

		@include bp($md) {
			padding-right: 0;
		}
	}

	.line, .section-name {
		display: none;

		@include bp($md) {
			display: block;
		}
	}

	&__heading {
		text-transform: uppercase;
		margin-bottom: 0;

		@include responsive-font($heading-7, $heading-5);


		@include bp($md) {
			padding: 0 1rem;
			margin-bottom: 1rem;
		}

		@include bp($xl) {
			padding: 0;
		}
	}

	&__content-container {
		margin-top: 24px;

		@include bp($md) {
			margin-top: rem-calc(50px);
			padding: 0;
			padding-top: 1rem;
		}
	}

	&__map {
		width: 100%;
		height: 100%;
		min-height: 300px;

		&-wrapper {
			display: flex;
			flex-direction: column;
			min-height: 300px;
			padding: 0;

			@include bp($md) {
				padding-left: calc(var(--bs-gutter-x) * .5);
				padding-right: calc(var(--bs-gutter-x) * .5);
			}

			@include bp($lg) {
				flex-direction: row;
				height: 66vh;
			}
		}

		&-search {
			width: 100%;
			border-top: 3px solid $primary-blue;
			position: relative;
			background-color: rgba(2, 33, 46, 0.1);
			padding: 1rem;

			@include bp($lg) {
				max-width: 400px;
				overflow: hidden;
				padding: 1rem 10px 2rem 1rem;

				&:after {
					content: "";
					position: absolute;
					left: 0;
					right: 0;
					bottom: 0;
					background: linear-gradient(transparent, rgb(233,235,236));
					z-index: 1;
					height: 33%;
					max-height: 120px;
				}
			}

			&-heading {
				text-transform: uppercase;
				font-size: 10px;

				@include bp($lg) {
					font-size: 15px;
				}
			}

			&-scroll {
				overflow-x: scroll;
				margin-bottom: 1rem;
				padding-bottom: 10px;


				@include bp($lg) {
					padding-right: 10px;
					overflow-y: scroll;
					overflow-x: hidden;
					height: 100%;
					padding-bottom: 100px;
				}

				// Custom scrollbar styles
				&::-webkit-scrollbar {
					width: 2px; // Width of the scrollbar
					height: 2px;
				}

				&::-webkit-scrollbar-track {
					background: $white; // Background of the scrollbar track
				}

				&::-webkit-scrollbar-thumb {
					background: $prussian-blue; // Color of the scrollbar thumb
				}

				&::-webkit-scrollbar-thumb:hover {
					background: darken($primary-blue, 10%); // Darker color on hover
				}
			}

			ul {
				display: flex;
				flex-direction: row;
				margin: 0;
				padding: 0;

				@include bp($lg) {
					flex-direction: column;
				}

				.block-find-firm__map-location {
					display: flex;
					flex-direction: row;
					cursor: pointer;
					min-width: 340px;
					margin-right: 8px;
					background-color: $white;
					transition: background-color 0.3s ease;
					min-height: 90px;

					&:hover {
						background-color: #f6f6f6;
					}

					&:last-child {
						margin-bottom: 0;
					}

					@include bp($lg) {
						margin-right: 0;
						margin-bottom: 4px;
					}
				}
			}

			&-input {
				@include bp($md) {
					padding: 0 1rem 1rem 1rem;
				}

				@include bp($lg) {
					position: absolute;
					bottom: 1rem;
					left: 1rem;
					right: 1rem;
					z-index: 2;
					padding: 0;
				}
			}
		}

		&.is-loading {
			position: relative;

			&::before {
				content: '';
				position: absolute;
				top: 50%;
				left: 50%;
				width: 40px;
				height: 40px;
				margin: -20px 0 0 -20px;
				border: 4px solid #f3f3f3;
				border-top: 4px solid #00AEEF; // Using Moore's blue color
				border-radius: 50%;
				z-index: 2;
				animation: spin 1s linear infinite;
			}

			&::after {
				content: '';
				position: absolute;
				top: 0;
				left: 0;
				right: 0;
				bottom: 0;
				background: rgba(255, 255, 255, 0.8);
				z-index: 1;
			}
		}

		@keyframes spin {
			0% { transform: rotate(0deg); }
			100% { transform: rotate(360deg); }
		}

		.mapboxgl-popup-content {
			font-family: $font-family-primary;
			padding: 1rem;
			box-shadow:
				0px 3px 7px 0px #0000001A,
				0px 13px 13px 0px #00000017,
				0px 30px 18px 0px #0000000D,
				0px 54px 21px 0px #00000003,
				0px 84px 23px 0px #00000000;
			text-align: center;
			min-width: 160px;

				h3 {
					font-size: 16px;
					font-weight: 600;
					margin-bottom: 5px;
				}

				p {
					font-size: 14px;
					font-weight: 500;
					text-transform: uppercase;
					margin-bottom: 1rem;
					line-height: 1.2;
				}

				.block-find-firm__map-location-link {
					display: block;
					margin-top: 1rem;
					font-size: 14px;
					padding: 10px 1rem;
					line-height: 1;
					font-weight: 500;
				}
		}

		&-location-image {
			width: 78px;
			background-color: #ddd;
			flex: 1 0 auto;

			img {
				display: block;
			}
		}
		&-location-details {
			width: 100%;
			padding: rem-calc(20px) rem-calc(24px);
			display: flex;
			flex-direction: column;
			justify-content: center;


			h3 {
				font-size: $body-small;
				font-weight: 500;
				margin-bottom: 0;
				white-space: nowrap;
				overflow: hidden;
				text-overflow: ellipsis;
				max-width: 210px;

				@include bp($lg) {
					max-width: 100%;
					white-space: normal;
				}
			}

			p {
				font-size: 10px;
				font-weight: 500;
				margin-bottom: 0;
				text-transform: uppercase;
			}

			.block-find-firm__map-location-link {
				display: none;
			}


		}

		&-filters {
			margin-bottom: rem-calc(40px);
			display: none;

			position: absolute;
			top: 0;
			left: 0;
			right: 0;
			z-index: 3;
			background-color: $white;

			&.active {
				display: block;
			}

			@include bp($md) {
				display: flex;
				position: relative;
				background-color: transparent;
			}

			&-wrapper {
				position: relative;

				@include bp($md) {
					padding: 0 1rem;
				}

				@include bp($xl) {
					padding: 0;
				}
			}

			&-heading {
				display: flex;
				border-bottom: 1px solid $black;
				justify-content: space-between;
				margin: 1rem;
				padding: 0;
				flex-shrink: 1;

				h3 {
					margin-bottom: 10px;
					font-size: $body-medium;
					font-weight: 400;
				}

				svg {
					line {
						stroke: $primary;
					}
				}

				@include bp($md) {
					display: none;
				}
			}
			select {
				width: 100%;
				appearance: none;
				background-color: transparent;
				border: none;
				padding: 0;
				margin: 0;
				padding: 10px 20px 10px 0;
				border-bottom: 1px solid $black;
				font-size: 18px;
				margin-bottom: 24px;
				background-image: url('/wp-content/themes/moore-global/assets/icons/select-dropdown.svg');
				background-repeat: no-repeat;
				background-position: right center;
				background-size: 20px 11px;

				&:focus-visible {
					outline: none;
					box-shadow: none;
				}

				@include bp($md) {
					margin-bottom: 0;
				}
			}

			&-close {
				font-size: $body-medium;
				border-top: 1px solid $border-grey;
				border-bottom: 1px solid $border-grey;
				padding: 1rem;
				width: 100%;
				margin-bottom: 24px;
				position: relative;
				@include bp($md) {
					display: none;
				}

				span {
					position: absolute;
					right: 1rem;

					svg {
						width: 19px;
						height: 18px;
					}
				}
			}
		}
	}

	// No JavaScript Fallback Styles
	&__no-js-fallback {
		margin: 2rem 0;
		padding: 2rem;
		background-color: #f8f9fa;
		border: 1px solid #dee2e6;
		border-radius: 8px;

		.alert {
			margin: 0;
			border: none;
			background-color: transparent;
			color: #495057;
		}

		h3 {
			color: $primary;
			font-size: 1.5rem;
			font-weight: 600;
			margin-bottom: 1rem;
		}

		p {
			margin-bottom: 1rem;
			line-height: 1.6;
		}

		ul {
			margin: 1rem 0;
			padding-left: 1.5rem;
		}

		li {
			margin-bottom: 0.5rem;
			line-height: 1.4;
		}

		a {
			color: $primary-blue;
			text-decoration: none;
			font-weight: 500;

			&:hover {
				text-decoration: underline;
			}
		}
	}
}
import STQ_LocationSearch from '../../resources/js/classes/LocationSearch';

class FindFirm {
	/**
	 * Create and initialise objects of this class
	 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/constructor
	 * @param {object} block
	 */
	constructor() {


		this.blocks = document.querySelectorAll('.block-find-firm');
		this.firms = this.blocks[0].querySelectorAll('.block-find-firm__map-location');
		this.country = false;
		this.region = false;
		this.setZoom = 1.25;
		this.setCenter = [30.562, 31.562];
		this.map = null;
		this.allFirms = null;
		this.allFirmMapDetails = null;

		if (window.innerWidth < 768) {
			this.mobileShowFitlers();
		}

		this.init();
	}

	/**
	 * Example function to run class logic
	 * Can access `this.block`
	 */
	init() {
		this.filters();
		this.setupFirmClick();
		this.initMap(this.blocks[0]);

		new STQ_LocationSearch();
	}

	initMap(block) {
		if (!mapboxgl) {
			console.error('Mapbox GL not loaded');
			return;
		}

		const mapContainer = block.querySelector('.block-find-firm__map');

		// Get the coords for the map if country set
		const country_coords = mapContainer.dataset.coords;
		let country_coordsArray = [];
		if (country_coords) {
			country_coordsArray = JSON.parse(country_coords);
		}

		// Add loading state
		mapContainer.classList.add('is-loading');

		// Helper function to create canvas with willReadFrequently
		const createImageCanvas = (img) => {
			const canvas = document.createElement('canvas');
			const ctx = canvas.getContext('2d', { willReadFrequently: true });
			canvas.width = img.width;
			canvas.height = img.height;
			ctx.drawImage(img, 0, 0);
			return canvas;
		};

		// Modify the preloadImages function
		const preloadImages = () => {
			return Promise.all([
				new Promise((resolve, reject) => {
					const img = new Image();
					img.crossOrigin = 'anonymous';
					img.onload = () => {
						const canvas = createImageCanvas(img);
						resolve(canvas);
					};
					img.onerror = reject;
					img.src = window.location.origin + '/wp-content/themes/moore-global/assets/icons/map-pin.png';
				}),
				new Promise((resolve, reject) => {
					const img = new Image();
					img.crossOrigin = 'anonymous';
					img.onload = () => {
						const canvas = createImageCanvas(img);
						resolve(canvas);
					};
					img.onerror = reject;
					img.src = window.location.origin + '/wp-content/themes/moore-global/assets/icons/map-pin-selected.png';
				})
			]);
		};

		// Initialize map with preloaded data
		preloadImages().then(([defaultPinCanvas, selectedPinCanvas]) => {
			mapboxgl.accessToken = 'pk.eyJ1Ijoic3RyYXRlZ2lxIiwiYSI6ImNqc2trb252NzEyZjYzenBvYTRhbmdyNDEifQ.la_5DQo_qJaALM-bp_sc2w';

			this.map = new mapboxgl.Map({
				container: mapContainer,
				style: 'mapbox://styles/strategiq/cm6qi73pg010a01r5g5551c8i',
				center: this.setCenter,
				zoom: this.setZoom,
				willReadFrequently: true,
				cooperativeGestures: true,
				preloadImages: true
			});

			// Add this after map initialization
			const canvas = mapContainer.querySelector('canvas');
			if (canvas) {
				canvas.getContext('2d', { willReadFrequently: true });
			}

			// Rest of your map initialization code...
			this.map.on('load', () => {
				// Add the images using the canvas instead of direct image loading
				if (!this.map.hasImage('map-pin')) {
					this.map.addImage('map-pin', defaultPinCanvas.getContext('2d').getImageData(
						0, 0,
						defaultPinCanvas.width,
						defaultPinCanvas.height
					));
				}

				if (!this.map.hasImage('map-pin-selected')) {
					this.map.addImage('map-pin-selected', selectedPinCanvas.getContext('2d').getImageData(
						0, 0,
						selectedPinCanvas.width,
						selectedPinCanvas.height
					));
				}

				// Add source and layers...
				this.map.addSource('firms', {
					type: 'geojson',
					data: {
						type: 'FeatureCollection',
						features: []
					},
					cluster: true,
					clusterMaxZoom: 12,
					clusterRadius: 50,
				});

				// Add a layer for the clusters with simpler style
				this.map.addLayer({
					id: 'clusters',
					type: 'circle',
					source: 'firms',
					filter: ['has', 'point_count'],
					paint: {
						'circle-color': '#00AEEF',
						'circle-radius': 20
					}
				});

				// Add the cluster count layer
				this.map.addLayer({
					id: 'cluster-count',
					type: 'symbol',
					source: 'firms',
					filter: ['has', 'point_count'],
					layout: {
						'text-field': '{point_count_abbreviated}',
						'text-font': ['Montserrat Medium', 'Arial Unicode MS Bold'],
						'text-size': 12,
						'text-allow-overlap': true
					},
					paint: {
						'text-color': '#000000'
					}
				});

				// Make sure these layers are rendered ABOVE all other layers
				this.map.moveLayer('clusters');
				this.map.moveLayer('cluster-count');

				// Add single layer for unclustered points
				this.map.addLayer({
					'id': 'unclustered-point',
					'type': 'symbol',
					'source': 'firms',
					'filter': ['!', ['has', 'point_count']],
					'layout': {
						'icon-image': [
							'case',
							['get', 'selected'],
							'map-pin-selected',
							'map-pin'
						],
						'icon-size': 1,
						'icon-allow-overlap': true
					}
				});

				// Update the click handler for unclustered points
				this.map.on('click', 'unclustered-point', (e) => {
					const coordinates = e.features[0].geometry.coordinates.slice();
					const description = e.features[0].properties.description;
					const clickedFirmId = e.features[0].properties.firmId;

					// Reset all points
					const features = this.map.getSource('firms')._data.features.map(feature => ({
						...feature,
						properties: {
							...feature.properties,
							selected: feature.properties.firmId === clickedFirmId
						}
					}));

					this.map.getSource('firms').setData({
						type: 'FeatureCollection',
						features: features
					});

					this.showPopup(coordinates, description);
				});

				// Update the click-away handler
				this.map.on('click', (e) => {
					const features = this.map.queryRenderedFeatures(e.point, { layers: ['unclustered-point'] });
					if (!features.length) {
						// Reset all points to unselected
						const features = this.map.getSource('firms')._data.features.map(feature => ({
							...feature,
							properties: {
								...feature.properties,
								selected: false
							}
						}));

						this.map.getSource('firms').setData({
							type: 'FeatureCollection',
							features: features
						});
					}
				});

				// Add zoom and rotation controls to the map.
				this.map.addControl(new mapboxgl.NavigationControl());

				// Make sure clusters are always on top
				this.map.on('idle', () => {
					this.map.moveLayer('clusters');
					this.map.moveLayer('cluster-count');
				});

				// Inside the map.on('load') callback, after adding the cluster layers:
				this.map.on('click', 'clusters', (e) => {
					const features = this.map.queryRenderedFeatures(e.point, {
						layers: ['clusters']
					});
					const clusterId = features[0].properties.cluster_id;
					this.map.getSource('firms').getClusterExpansionZoom(
						clusterId,
						(err, zoom) => {
							if (err) return;

							this.map.easeTo({
								center: features[0].geometry.coordinates,
								zoom: zoom,
								duration: 500,
								essential: true
							});
						}
					);
				});

				// Optional: Add cursor styling for better UX
				this.map.on('mouseenter', 'clusters', () => {
					this.map.getCanvas().style.cursor = 'pointer';
				});

				this.map.on('mouseleave', 'clusters', () => {
					this.map.getCanvas().style.cursor = '';
				});

				// If we have boundary coordinates, fit the map to them after initialization
				if (country_coords) {
					this.map.fitBounds(country_coordsArray, {
						padding: { top: 50, bottom: 50, left: 50, right: 50 },
						duration: 2000,
						essential: true,
						curve: 1.42,
						easing: function (t) {
							return t * (2 - t);
						}
					});
				}

				// Remove loading state when map is ready
				mapContainer.classList.remove('is-loading');

				// Load firms after map is ready
				this.loadAllFirms();
			});
		});
	}

	// Method to show popup
	showPopup(coordinates, description) {
		new mapboxgl.Popup()
			.setLngLat(coordinates)
			.setHTML(description)
			.addTo(this.map);
	}

	goToMarker(firm) {
		const lng = parseFloat(firm.dataset.lng);
		const lat = parseFloat(firm.dataset.lat);

		// Reset all points and select the clicked one
		const features = this.map.getSource('firms')._data.features.map(feature => ({
			...feature,
			properties: {
				...feature.properties,
				selected: String(feature.properties.firmId) === String(firm.getAttribute('data-firm-id'))
			}
		}));

		this.map.getSource('firms').setData({
			type: 'FeatureCollection',
			features: features
		});

		this.map.flyTo({
			center: [lng, lat],
			zoom: 14,
			essential: true
		});

		const description = firm.querySelector('.block-find-firm__map-location-details').innerHTML;
		this.showPopup([lng, lat], description);
	}

	setupFirmClick() {
		// Get all firm elements, including newly loaded ones
		this.firms = this.blocks[0].querySelectorAll('.block-find-firm__map-location');

		this.firms.forEach(firm => {
			// Remove any existing click handlers
			firm.removeEventListener('click', this.handleFirmClick);
			// Add new click handler
			firm.addEventListener('click', () => this.goToMarker(firm));

			// Ensure accessibility attributes are set
			if (!firm.hasAttribute('role')) {
				firm.setAttribute('tabindex', '0');
				firm.setAttribute('role', 'option');

				// Add keyboard event handlers for option role
				firm.addEventListener('keydown', (e) => {
					if (e.key === 'Enter' || e.key === ' ') {
						e.preventDefault();
						this.goToMarker(firm);
					}
				});
			}
		});
	}

	filters() {
		const filters = this.blocks[0].querySelector('.block-find-firm__map-filters');
		if (!filters) return;

		const filterItems = filters.querySelectorAll('select');
		const regionFilter = filters.querySelector('select[name="region"]');
		const countryFilter = filters.querySelector('select[name="country"]');

		filterItems.forEach(filter => {
			filter.addEventListener('change', () => {
				this.region = regionFilter ? regionFilter.value : false;
				this.country = countryFilter ? countryFilter.value : false;

				// Filter firms client-side
				this.filterFirms();

				if (filter.name === 'country' || filter.name === 'region') {
					// Change heading depending on filter
					let headingText = 'All Locations';

					// If changing country
					if (filter.name === 'country') {
						if (filter.value) {
							headingText = filter.selectedOptions[0].textContent;
						} else {
							// If country is set to empty, check if region has a value
							if (regionFilter && regionFilter.value) {
								headingText = regionFilter.selectedOptions[0].textContent;
							}
						}
					}
					// If changing region
					else if (filter.name === 'region') {
						const countrySelect = document.querySelector('select[name="country"]');
						// If country has value, use it
						if (countrySelect && countrySelect.value) {
							headingText = countrySelect.selectedOptions[0].textContent;
						}
						// Otherwise use region if it has value
						else if (filter.value) {
							headingText = filter.selectedOptions[0].textContent;
						}
					}

					document.querySelector('.block-find-firm__map-search-heading').innerHTML = headingText;
				}

				if (filter.name === 'country') {
					// Check if region is selected when "All" is chosen for country
					if (!filter.value || filter.value === '') {
						if (this.region && regionFilter && regionFilter.value) {
							const regionOption = regionFilter.querySelector(`option[value="${regionFilter.value}"]`);
							if (regionOption && regionOption.dataset.coords) {
								const regionData = JSON.parse(regionOption.dataset.coords);
								const bounds = regionData.bounds;

								// Check if we have custom center and zoom settings
								if (regionData.center && regionData.zoom) {
									// Use the custom center and zoom from the JSON data
									this.map.flyTo({
										center: regionData.center,
										zoom: regionData.zoom,
										duration: 2000,
										essential: true,
										curve: 1.42,
										easing: function (t) {
											return t * (2 - t);
										}
									});
								} else {
									// Fall back to standard fitBounds if center/zoom not specified
									this.map.fitBounds(bounds, {
										padding: { top: 100, bottom: 100, left: 100, right: 100 },
										duration: 2000,
										essential: true,
										curve: 1.42,
										easing: function (t) {
											return t * (2 - t);
										}
									});
								}
							} else {
								// Region selected but no coordinates - reset to default
								this.map.setCenter(this.setCenter);
								this.map.setZoom(this.setZoom);
							}
						} else {
							// No region selected - reset to default
							this.map.setCenter(this.setCenter);
							this.map.setZoom(this.setZoom);
						}
					} else if (filter.selectedOptions[0].dataset.coords) {
						// Fit Map bounds only if coordinates are available
						const bounds = JSON.parse(filter.selectedOptions[0].dataset.coords);
						this.map.fitBounds(bounds, {
							padding: { top: 50, bottom: 50, left: 50, right: 50 },
							duration: 2000,
							essential: true,
							curve: 1.42,
							easing: function (t) {
								return t * (2 - t);
							}
						});
					}
					// If country selected but no coordinates, do nothing - keep current map position
				}

				if (filter.name === 'region') {
					const countryFilter = filters.querySelector('select[name="country"]');

					this.resetFilter(countryFilter);

					// Check if region is selected
					if (this.region && filter.value) {
						const allOptions = Array.from(countryFilter.options);
						allOptions.forEach(option => {
							if (option.value) {
								option.style.display = option.dataset.region === this.region ? 'block' : 'none';
							}
						});

						// Fit Map bounds
						if (filter.selectedOptions[0].dataset.coords) {
							const regionData = JSON.parse(filter.selectedOptions[0].dataset.coords);
							const bounds = regionData.bounds;

							// Check if we have custom center and zoom settings
							if (regionData.center && regionData.zoom) {
								// Use the custom center and zoom from the JSON data
								this.map.flyTo({
									center: regionData.center,
									zoom: regionData.zoom,
									duration: 2000,
									essential: true,
									curve: 1.42,
									easing: function (t) {
										return t * (2 - t);
									}
								});
							} else {
								// Fall back to standard fitBounds if center/zoom not specified
								this.map.fitBounds(bounds, {
									padding: { top: 100, bottom: 100, left: 100, right: 100 },
									duration: 2000,
									essential: true,
									curve: 1.42,
									easing: function (t) {
										return t * (2 - t);
									}
								});
							}
						} else {
							// Region selected but no coordinates - reset to default
							this.map.setCenter(this.setCenter);
							this.map.setZoom(this.setZoom);
						}
					} else {
						// No region selected or region reset - reset map to default
						this.map.setCenter(this.setCenter);
						this.map.setZoom(this.setZoom);
					}
				}
			});
		});
	}

	filterFirms() {
		// Get all firms from the DOM
		const firms = this.blocks[0].querySelectorAll('.block-find-firm__map-location');

		// Get selected country and region
		const selectedCountry = this.country;
		const selectedRegion = this.region;

		// Filter map details based on country and region
		const filteredMapDetails = this.allFirmMapDetails.filter(firm => {
			const firmElement = document.querySelector(`[data-firm-id="${firm.firm_id}"]`);
			if (!firmElement) return false;

			// Check for country (prefer data-country, fallback to data-location)
			const firmCountry = firmElement.dataset.country ? firmElement.dataset.country : firmElement.dataset.location;
			const firmRegion = firmElement.dataset.region;

			// Match country
			const matchesCountry = !selectedCountry || firmCountry === selectedCountry;

			// Match region
			const matchesRegion = !selectedRegion || firmRegion === selectedRegion;

			return matchesCountry && matchesRegion;
		});

		// Update map with filtered data
		if (this.map) {
			const geojson = {
				type: 'FeatureCollection',
				features: filteredMapDetails.map(firm => ({
					type: 'Feature',
					properties: {
						description: document.querySelector(`[data-firm-id="${firm.firm_id}"]`) ?
							document.querySelector(`[data-firm-id="${firm.firm_id}"] .block-find-firm__map-location-details`).innerHTML :
							'No description available',
						firmId: firm.firm_id,
						selected: false
					},
					geometry: {
						type: 'Point',
						coordinates: firm.coords
					}
				}))
			};

			const source = this.map.getSource('firms');
			if (source) {
				source.setData(geojson);
			}
		}

		// Update firms list
		firms.forEach(firm => {
			// Check for country (prefer data-country, fallback to data-location)
			const firmCountry = firm.dataset.country ? firm.dataset.country : firm.dataset.location;
			const firmRegion = firm.dataset.region;

			// Match country
			const matchesCountry = !selectedCountry || firmCountry === selectedCountry;

			// Match region
			const matchesRegion = !selectedRegion || firmRegion === selectedRegion;

			if (matchesCountry && matchesRegion) {
				firm.style.display = '';
			} else {
				firm.style.display = 'none';
			}
		});

		console.log('Filter by', selectedCountry, selectedRegion);

		// Reattach click handlers
		this.setupFirmClick();
	}

	resetFirms() {
		this.firms.forEach(firm => {
			firm.style.display = 'none';
		});
	}

	resetFilter(filter) {
		const allOptions = Array.from(filter.options);

		if (filter.name === 'country' && filter.value) {
			this.country = false; // Reset country when region is cleared
			filter.selectedIndex = 0; // Select the first option
		}

		// Reset the selected index for the current filter
		filter.selectedIndex = 0; // Select the first option
		allOptions.forEach(option => {
			option.style.display = 'block';
		});
	}

	mobileShowFitlers() {
		const filtersHeading = this.blocks[0].querySelector('.block-find-firm__map-filters-heading');
		if (!filtersHeading) return;

		const filters = this.blocks[0].querySelector('.block-find-firm__map-filters');

		filtersHeading.addEventListener('click', () => {
			filters.classList.add('active');
		});

		const closeFilters = this.blocks[0].querySelector('.block-find-firm__map-filters-close');
		if (!closeFilters) return;

		closeFilters.addEventListener('click', () => {
			filters.classList.remove('active');
		});
	}

	loadAllFirms() {
		const url = new URL('/wp-json/wp/v2/firms/map', window.location.origin);

		fetch(url)
			.then(response => response.json())
			.then(data => {
				// Store all firms and map details
				this.allFirms = data.firms;
				this.allFirmMapDetails = data.firm_map_details;

				// Populate firms list first
				const firmsList = this.blocks[0].querySelector('.block-find-firm__map-search-scroll ul');
				if (firmsList) {
					firmsList.innerHTML = data.firms.join('');
					// Make firm items focusable and accessible
					const firmItems = firmsList.querySelectorAll('.block-find-firm__map-location');
					firmItems.forEach(item => {
						item.setAttribute('tabindex', '0');
						item.setAttribute('role', 'option');

						// Add keyboard event handlers for option role
						item.addEventListener('keydown', (e) => {
							if (e.key === 'Enter' || e.key === ' ') {
								e.preventDefault();
								this.goToMarker(item);
							}
						});
					});
					// Reattach click handlers after loading firms
					this.setupFirmClick();
				}

				// Update map with firm data AFTER firms are loaded to DOM
				if (this.map) {
					const geojson = {
						type: 'FeatureCollection',
						features: data.firm_map_details.map(firm => ({
							type: 'Feature',
							properties: {
								description: document.querySelector(`[data-firm-id="${firm.firm_id}"]`) ?
									document.querySelector(`[data-firm-id="${firm.firm_id}"] .block-find-firm__map-location-details`).innerHTML :
									'No description available',
								firmId: firm.firm_id,
								selected: false
							},
							geometry: {
								type: 'Point',
								coordinates: firm.coords
							}
						}))
					};

					const source = this.map.getSource('firms');
					if (source) {
						source.setData(geojson);
					}
				}
			});
	}
}

//ON document load
document.addEventListener('DOMContentLoaded', () => {
	new FindFirm();
});
{
    "$schema": "https://schemas.wp.org/trunk/block.json",
    "apiVersion": 2,
    "name": "strategiq/find-firm",
    "title": "Find Firm",
    "description": "Find Firm",
    "category": "strategiq",
    "icon": "strategiq",
    "acf": {
        "mode": "preview",
        "renderTemplate": "block-find-firm.php"
    },
    "supports": {
        "anchor": true,
        "align": false,
        "color": {
            "background": true,
            "text": false,
            "gradients": true
        },
        "spacing": {
            "padding": [
                "top",
                "bottom"
            ],
            "margin": [
                "top",
                "bottom"
            ]
        }
    },
    "example": {
        "attributes": {
            "mode": "preview",
            "data": {
                "heading_type": "h2",
                "heading_text": "Example - Basic Map",
                "content": "This is some example content to represent what the content will look like"
            }
        }
    },
    "style": ["file:../../assets/css/find-firm/block-find-firm.css", "mapbox-styles"],
    "viewScript": ["mapbox-scripts", "find-firm"]
}
This component is not currently used on any pages.
There are is no readme file with this component.