Global Elements
2
Footer Header Services Strip Services Tabs
Components
36
Card Carousel Cards Grid Content Overlap Content Strip Cta Bar Featured Article Featured Blocks Featured Expert Featured Pods Featured Tabs Form Block Full Screen Carousel Full Width Content Heading Strip Hero Hero Featured Links List Location Tabs Logo Carousel Map Block People Grid Quote Block Reviews Carousel Reviews Tabs Service Tiles Services Strip Services Tabs Staggered Content Sticky Accordion Sticky List Sub Navigation Team Carousel Team Filters Team Strip Tile Carousel Tile Grid

Location Tabs

photo-1647163926816-6125a4cfd6ab
media
environment

Locations

Gerrards Cross
London
Thame

Parking
Ample parking is situated at the end of Station Road in the public Pay and Display car park, opposite Waitrose. Unfortunately, we do not have visitor parking available at the B P Collins office.

Disabled access to our offices is available.

Parking
Ample parking can be found at the Upper Street Car Park – 0X9 3EZ, three hour free stay.

Field
Field Type
Field Name
Instructions
Block Data
tab
Heading Type
select
heading_type
Heading Text
text
heading_text
Locations
relationship
locations
Block Meta
tab
ID
text
block_id
Block Classes
text
block_classes
Block Theme
select
block_theme
Background Colors
select
background_colors
Padding Top
select
padding_top
Padding Bottom
select
padding_bottom
Margin Top
select
margin_top
Margin Bottom
select
margin_bottom

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

.block-location-tabs {
	padding-top: rem-calc(80);
	padding-bottom: rem-calc(80);
	background-color: $navy;
	position: relative;

	&__heading {
		color: $white;
		margin-bottom: rem-calc(80);

		.heading {
			color: $white;
			margin-bottom: rem-calc(80);
		}
	}

	&__map {
		width: 100% !important;
		height: rem-calc(290);
	}

	.marker {
		width: rem-calc(40);
		height: rem-calc(40);
		background-image: url('#{$asset-path}/icons/icon-pin-orange.svg');
		background-size: contain;
		background-position: center;
		background-repeat: no-repeat;
	}

	&__background {
		position: absolute;
		top: 0;
		left: 0;
		right: 0;
		bottom: 0;
		transition: all 0.3s ease-in-out;
		background-color: $navy;
		opacity: 0;
		z-index: 2;
		display: none;

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

		&.active {
			opacity: 1 !important;
			z-index: 1;
		}

		img {
			opacity: 0.2;
		}
	}

	&__container {
		position: relative;
		z-index: 3;
	}

	&__list {
		margin-bottom: rem-calc(60);

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

	&__list-item {
		color: $white;
		@include fluid-type(22, 30);
		font-family: $font-family-heading;
		margin-bottom: rem-calc(20);
		padding-bottom: rem-calc(10);
		position: relative;
		cursor: pointer;
		opacity: 0.5;
		transition: all 0.3s ease-in-out;

		@include bp($lg) {
			margin-bottom: rem-calc(60);
		}

		&:after {
			content: '';
			width: 0;
			height: rem-calc(2);
			background-color: $white;
			position: absolute;
			bottom: 0;
			left: 0;
			right: 0;
			opacity: 0.5;
			transition: all 0.3s ease-in-out;
		}

		&:last-of-type {
			margin-bottom: 0;
		}

		&:hover {
			&:after {
				width: 100%;
			}
		}

		&.active {
			opacity: 1;

			&:after {
				width: 100%;
				opacity: 1;
			}
		}
	}

	&__pod {
		// display: none;
		opacity: 0;
		pointer-events: none;
		position: absolute;
		top: 0;
		left: 0;
		right: 0;
		// bottom: 0;
		transition: all 0.3s ease-in-out;
		background-color: $white;

		&.active {
			display: block;
			opacity: 1;
			pointer-events: auto;
		}
	}

	&__pod-container {
		position: relative;

		// @include bp($lg, 'true') {
		// 	height: auto !important;
		// }
	}

	&__pod-content {
		background-color: $white;
		color: $darkBlue;
		padding: rem-calc(24);
	}

	&__pod-map {
		width: 100%;
		height: rem-calc(300);
		background-color: darken($navy, 5%);
	}

	&__detail {
		color: $darkBlue;
		font-weight: 700;
		text-decoration: none;
		margin-bottom: rem-calc(16);
		line-height: rem-calc(24);

		a {
			color: $darkBlue;
			transition: all 0.2s ease-in-out;
			display: inline-block;
			text-decoration: none;
			position: relative;

			&:hover {
				color: $orange;
			}
		}

		&--phone,
		&--email,
		&--address {
			a {
				padding-left: rem-calc(32);

				&:before {
					content: '';
					position: absolute;
					top: rem-calc(4);
					left: 0;
					width: rem-calc(16);
					height: rem-calc(16);
					background-size: contain;
					background-repeat: no-repeat;
					background-position: center;
				}
			}
		}

		&--phone {
			a {
				&:before {
					background-image: url('#{$asset-path}/icons/icon-phone-navy.svg');
				}
			}
		}

		&--email {
			a {
				&:before {
					background-image: url('#{$asset-path}/icons/icon-envelope-navy.svg');
				}
			}
		}

		&--address {
			a {
				&:before {
					background-image: url('#{$asset-path}/icons/icon-pin-navy.svg');
				}
			}
		}

	}

	&__info {
		border-top: 1px solid $lightBlue;
		padding-top: rem-calc(20);
		margin-top: rem-calc(4);

		p {
			&:last-of-type {
				margin-bottom: 0;
			}
		}
	}
}
class LocationTabs {
	/**
	 * 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-location-tabs');
		this.init();
	}

	/**
	 * Example function to run class logic
	 * Can access `this.block`
	 */
	init() {
		this.blocks.forEach((block) => {

			mapboxgl.accessToken = 'pk.eyJ1IjoiYi1wLWNvbGxpbnMiLCJhIjoiY2x0em1nazQzMDFyaDJrdDR2MWhheGx1cSJ9.xefnuzaGNVh9n9xwW_EA5w';
			const locationTabs = block.querySelectorAll('.block-location-tabs__list-item');
			const locationPods = block.querySelectorAll('.block-location-tabs__pod');
			const locationImages = block.querySelectorAll('.block-location-tabs__background');
			const podContainer = block.querySelector('.block-location-tabs__pod-container');
			const topOffset = -100;
			const podContainerOffset = podContainer.getBoundingClientRect().top + window.pageYOffset + topOffset;

			// console.log(podContainerOffset);

			const [tallestPod] = [...locationPods].sort((a, b) => b.clientHeight - a.clientHeight);

			// Set container height to tallest pod
			let containerHeight = () => {
				if (podContainer) {
					podContainer.style.height = `${tallestPod.clientHeight}px`;
				}
			}
			containerHeight();
			window.addEventListener('resize', function(event) {
				containerHeight();
			}, true);


			locationPods.forEach((pod) => {
				let mapBox = pod.querySelector(".block-location-tabs__map");
				let mapBoxID = mapBox.id;
				let lng = mapBox.getAttribute('data-lng');
				let lat = mapBox.getAttribute('data-lat');
				let coords = [lng, lat];

				// console.log(coords);

				var map = new mapboxgl.Map({
					container: mapBoxID,
					style: 'mapbox://styles/b-p-collins/clv6g124w00lk01qv3s2t634d',
					center: coords,
					zoom: 15,
				});

				var el = document.createElement('div');
				el.className = 'marker';

				new mapboxgl.Marker(el)
				.setLngLat(coords)
				.addTo(map);

				map.scrollZoom.disable();
				map.touchZoomRotate.disableRotation();
				map.addControl(new mapboxgl.FullscreenControl());
				map.addControl(new mapboxgl.NavigationControl());

				if (window.innerWidth < 992) {
					map.dragPan.disable();
				}

			});


			locationTabs.forEach((tab) => {
				// Hovering over location tab
				tab.addEventListener("mouseover", () => {
					let locationID = tab.getAttribute('data-tab');

					if (locationID) {
						let locationImage = block.querySelector(`[data-image="${locationID}"]`);

						if (locationImage) {
							locationImage.style.opacity = 1;
						}
					}
				});

				// Hovering out of location tab
				tab.addEventListener("mouseout", () => {
					let locationID = tab.getAttribute('data-tab');

					if (locationID) {
						let locationImage = block.querySelector(`[data-image="${locationID}"]`);

						if (locationImage) {
							locationImage.style.opacity = 0;
						}
					}
				});

				// Tab click action
				tab.addEventListener("click", () => {
					let locationID = tab.getAttribute('data-tab');
					let locationImage = block.querySelector(`[data-image="${locationID}"]`);

					locationTabs.forEach((tabs) => {
						tabs.classList.remove("active");
					});
					tab.classList.add("active");

					if (locationID) {
						let locationBlock = block.querySelector(`#${locationID}`);

						locationPods.forEach((pod) => {
							pod.classList.remove("active");
						});

						if (locationBlock) {
							locationBlock.classList.add("active");
						}
					}

					if (locationImage) {
						locationImages.forEach((image) => {
							image.classList.remove("active");
						});
						locationImage.classList.add("active");
					}


					if (window.innerWidth < 992) {
						window.scrollTo({top: podContainerOffset, behavior: 'smooth'});
					}

				});
			});

		});
	}
}

new LocationTabs();
Page Title
Page Type
Page URL

Animation / States

  • When a user clicks on a location - their corresponding map will fade in on the right, as well as the background image change & fade to the featured image assigned to that location

External Libraries

  • Swiper
  • ScrollTrigger (GSAP)

Notes (Design / Dev / SEO)

  • On the left hand side the location names will be dynamically populated from the "Locations" post type. Alternatively, the user can select which locations to feature within the component