Components
54
Accordion Items Basic Hero Basic Map Benefits Card Grid Career Testimonials Case Study Carousel Case Study Content Two Column Contact Form Content Centred Content Image Content Three Columns Content Two Column Content Video Cookie Table Cta Blocks Example Faqs Featured Card Featured Latest Cards Footer Banner Glossary Items Hero Insights Hero Rounded Hero With Featured Insight Home Hero Icon Carousel Image Link Carousel Image Link Carousel 2×2 Image Link Grid Insight Content Job Listing Large Image Large Testimonial Leadership Grid Link Grid Locations Locations Archive Logo Scroller Page Heading People Carousel Related Services Resources Carousel Section Anchors Separator Service Hero Services List Services Tabs Tech Partners Testimonial Carousel Testimonial Single Use Case Component Values Scroller Video

Locations

Field
Field Type
Field Name
Instructions
Block Data
tab
Heading Type
select
heading_type
Heading Text
textarea
heading_text
Locations
repeater
locations
-- Location Label
text
location_label
-- Location details
group
location_details
---- General Enquiry
text
general_enquiry
---- Help & Support
text
help_support
---- Email
email
email
---- Address
textarea
address
---- Map
google_map
map_location
Block Meta
tab
ID
text
block_id
Block Classes
text
block_classes
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/variables";
@import "../../resources/scss/util/mixins";

.block-locations{
    padding: 3em 0;
    
    @include bp($lg){
        padding: rem-calc(56 0 96 0);
    }
    .heading-row{
        align-items: center;
        margin-bottom: rem-calc(32);
        @include bp($lg){
            margin-bottom: rem-calc(56);
        }
    }
    .heading{
        font-size: rem-calc(28);
        line-height: rem-calc(32);
        letter-spacing: 0.02em;
        font-weight: 400;
        margin-bottom: 0;

        @include bp($lg){
            font-size: rem-calc(56);
            line-height: rem-calc(64);            
        }
    }

    .location-tabs {
        list-style: none;
        margin: 0 0 30px 0;
        padding: 0;
    }
    
    .location-tabs__nav_link {
        cursor: pointer;
        text-decoration: none;
    }
    
    .location-tabs__content {
        display: block;
        transition: opacity 0.2s;
    }
    
    .location-tabs.style__horizontal .location-tabs__content,
    .location-tabs.style__vertical .location-tabs__content {
        height: 0;
        overflow: hidden;
        opacity: 0;
        visibility: hidden;
    }
    
    .location-tabs.style__horizontal .location-tabs__content.is__active,
    .location-tabs.style__vertical .location-tabs__content.is__active,
    .location-tabs__content.is__active {
        opacity: 1;
        height: auto;
        visibility: visible;
    }

    /*------------------------------------------------------------------
    3. Vertical tabs styles
    -------------------------------------------------------------------*/
    .location-tabs.style__vertical {
        display: flex;
        justify-content: space-between;
        width: 100%;
    }
    
    .location-tabs.style__vertical .location-tabs__nav {
        flex: 0 0 49%;
    }
    
    .location-tabs.style__vertical .location-tabs__nav .location-tabs__nav_link {
        border-bottom: 1px solid $grey-secondary;
        margin: rem-calc(0 0 47 0);
        padding: rem-calc(0 0 17 0);
        display: block;
        font-weight: 400;
        font-size: rem-calc(24);
        line-height: rem-calc(24);
        letter-spacing: 0.02em;
        transition: 0.3s ease color;
        &:hover{
            > a{
                color: $orange;
            }
        }
    }
    
    .location-tabs.style__vertical .location-tabs__nav .location-tabs__nav_link.is__active {
        border-bottom: 1px solid $secondary;
        font-weight: 600;
    }
    
    .location-tabs.style__vertical .location-tabs__content {
        flex-grow: 1;
        width: 0;
    }
    
    .location-tabs.style__vertical .location-tabs__content .location-tabs__nav_link {
        display: none;
    }
    
    .location-tabs.style__vertical .location-tabs__content.is__active {
        position: relative;
        z-index: 1;
        width: 100%;
        @include bp($lg){
            width: 50%;
            padding-left: 40px;
        }  
    }
    .location-tabs.is__responsive .location-tabs__content.is__active{
        border-bottom: 1px solid $secondary;
    }
    .location-tabs__content_wrapper{
        @include bp($lg){
            margin: 0 0 0 auto;
            max-width: rem-calc(525);
        }
    }
    /*------------------------------------------------------------------
    4. Accordion / responsive styles
    -------------------------------------------------------------------*/
    .location-tabs.style__accordion,
    .location-tabs.is__responsive {
        display: block;
    }
    .location-tabs.style__accordion .location-tabs__nav,
    .location-tabs.is__responsive .location-tabs__nav {
        display: none;
    }
    
    .location-tabs.style__accordion .location-tabs__nav_link,
    .location-tabs.is__responsive .location-tabs__nav_link,
    .location-tabs.style__accordion .location-tabs__content .location-tabs__nav_link,
    .location-tabs.is__responsive .location-tabs__content .location-tabs__nav_link {
        display: block;
        padding: 18px;
        margin: -18px;
    }
    
    .location-tabs.style__accordion .location-tabs__nav_link.is__active,
    .location-tabs.is__responsive .location-tabs__nav_link.is__active {
        font-weight: 600;
        margin-bottom: 0;
    }
    
    .location-tabs.style__accordion .location-tabs__content,
    .location-tabs.is__responsive .location-tabs__content {
        border-bottom: 1px solid $grey-secondary;
        padding: rem-calc(0 0 17 0);
        margin: rem-calc(0 0 24 0);
        display: block;
        height: auto;
        flex: none;
        opacity: 1;
        visibility: visible;
        overflow: hidden;
        width: 100%;
        &:hover{
            > a{
                color: $orange;
            }
        }
    }
    
    .location-tabs.style__accordion .location-tabs__content_wrapper,
    .location-tabs.is__responsive .location-tabs__content_wrapper { 
        height: 0;
        transform: scaleY(0);
        overflow: hidden;
        transition: transform 0.2s;
        transform-origin: top left;
    }
    
    .location-tabs.style__accordion .location-tabs__content.is__active .location-tabs__content_wrapper,
    .location-tabs.is__responsive .location-tabs__content.is__active .location-tabs__content_wrapper {
        height: auto;
        transform: scaleY(1);
    }

    /**Block map css**/
    .location-map-wrapper{
        width: 100%;
        margin-bottom: 32px;
        .location-map__map-container{
            aspect-ratio: 16/9;
            border-radius: 32px;
        }
    }
    .location-contact-details{
        display: flex;
        flex-direction: column;
        @include bp($md){
            flex-direction: row;
            justify-content: space-between;
        }
        .location-contact-col{
            width: 100%;
            @include bp($md){
                width: 49%;
            }
        }
        .location-contact-row{
            font-size: rem-calc(18);
            line-height: rem-calc(28);
            letter-spacing: 0.02em;
            margin-bottom: rem-calc(32);

            a{
                color: $black;
                text-decoration: underline;
                &:hover{
                    color: $orange;
                    text-decoration: none;
                }
            }
        }
        .location-contact-heading{
            font-weight: 600;            
        }
    }
}
class VanillaTabs {

	constructor( opts ) {

		const DEFAULTS = {
			'selector': '.location-tabs',
			'type': 'horizontal',
			'responsiveBreak': 840,
			'activeIndex' : 0
		}

		this.options = Object.assign( DEFAULTS, opts );
		this.elems = document.querySelectorAll( this.options.selector );

		// skip building tabs if they were already initialized
		this.skipIfInitialized = ( tabsElem ) => {

			// skip element if already initialized
			if( tabsElem.classList.contains('location-tabs__initialized') ) {
				return;
			}

		}

		this.buildUI();
		this.handleNavigation();
		this.handleResponsive();

	}

    initLocationMap( block ) {
        if ( ! mapboxgl ) {
            console.error( mapboxgl );
            return;
        }

        const mapContainer = block.querySelector( '.location-map__map-container' );

        // Create a new token - https://account.mapbox.com/
        mapboxgl.accessToken = 'pk.eyJ1Ijoic3RyYXRlZ2lxIiwiYSI6ImNsZ3Z6dXk3bzBjb3czZm11ZDV0bWd3MnUifQ.vZI6gGKApjCOMQLPqFdF_g';

        let lng  = parseFloat( mapContainer.getAttribute('data-lng') );
        let lat  = parseFloat( mapContainer.getAttribute('data-lat') );
        let zoom = parseFloat( mapContainer.getAttribute('data-zoom') );

        this.map = new mapboxgl.Map({
            container: mapContainer,
            style: 'mapbox://styles/mapbox/streets-v11',
            center: [lng, lat],
            zoom: zoom,
        });

        new mapboxgl.Marker({
            color: "#FF2C31",
        }).setLngLat( [lng, lat] )
        .addTo( this.map );

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

	// initialize the UI Elements
	buildUI(){
        let self=this;
		let tabs = this.elems;

		// walk on all tabs on the page
		tabs.forEach( ( el, i ) => {

			let tabsElem = el,
			childNodes = tabsElem.childNodes,
			tabsTitles = [],
			tabsStyle = this.options.type;

			this.skipIfInitialized( tabsElem );

			tabsElem.classList.add( 'style__' + this.options.type );
			tabsElem.classList.add( 'location-tabs__initialized' );

			for( let i = 0; i < childNodes.length; i++ ) {

				let tabItem = childNodes[i];

				if ( tabItem.nodeType != Node.TEXT_NODE ) {

					// add tab__content CSS class
					tabItem.classList.add( 'location-tabs__content');

					// grab tab title from data attribute
					let tabTitle = tabItem.dataset.title ? tabItem.dataset.title : '';
					tabsTitles.push( tabTitle );

					// wrap tab content
					let tabContent = tabItem.innerHTML;
					tabItem.innerHTML = '
' + tabContent + '
'; // insert nav link for accordion navigation tabItem.insertAdjacentHTML( 'afterbegin', '' + tabTitle + ''); } } // create horizontal / vertical tabs navigation elements let navElemsHTML = ''; tabsTitles.forEach( ( title ) => { navElemsHTML = navElemsHTML + '' + title + ''; }); tabsElem.insertAdjacentHTML( 'afterbegin', '
  • ' + navElemsHTML + '
  • '); // set initial active tab let activeTabIndex = Number( this.options.activeIndex ); // validate active tab index. but, you can specify -1 for accordion tabs to make all of them closed by defaults if( tabsStyle != 'accordion' && activeTabIndex != -1 ) { if( activeTabIndex > (tabsTitles.length - 1) ) { console.warn( 'VANILLA TABS: Active tab number from settings is bigger than tabs count. Please remember, that index starts from Zero! To avoid crashes, activeIndex option was reverted to 0.'); activeTabIndex = 0; } tabsElem.querySelectorAll( '.location-tabs__nav > .location-tabs__nav_link')[ activeTabIndex ].classList.add( 'is__active' ); tabsElem.querySelectorAll( '.location-tabs__content')[ activeTabIndex ].classList.add( 'is__active' ); tabsElem.querySelectorAll( '.location-tabs__content > .location-tabs__nav_link')[ activeTabIndex ].classList.add( 'is__active' ); setTimeout(function() { self.initLocationMap(tabsElem.querySelectorAll( '.location-tabs__content')[ activeTabIndex ]); }, 800); } }); } // navigation: assign click events handleNavigation() { let self = this; let tabs = this.elems, tabsStyle = this.options.type; // walk on all tabs on the page tabs.forEach( ( el, i ) => { let tabsElem = el; this.skipIfInitialized( tabsElem ); tabsElem.addEventListener( 'click', ( e ) => { if( e.target && e.target.classList.contains( 'location-tabs__nav_link') ) { e.preventDefault(); let activeTabIndex; // if we click on main navigation link if( e.target.parentElement.classList == 'location-tabs__nav' ) { activeTabIndex = Array.prototype.slice.call( e.target.parentElement.children ).indexOf( e.target ); // if we click on accordion nav link } else { activeTabIndex = Array.prototype.slice.call( e.target.parentElement.parentElement.children ).indexOf( e.target.parentElement ) - 1; } let tabsContent = tabsElem.getElementsByClassName( 'location-tabs__content'), mainNavLinks = tabsElem.querySelectorAll( '.location-tabs__nav > .location-tabs__nav_link'), accordionNavLinks = tabsElem.querySelectorAll( '.location-tabs__content > .location-tabs__nav_link'); // toggle accordion panel if( ( tabsStyle == 'accordion' || tabsElem.classList.contains( 'is__responsive') ) && e.target.classList.contains( 'is__active') ) { tabsContent[ activeTabIndex ].classList.remove( 'is__active'); mainNavLinks[ activeTabIndex ].classList.remove( 'is__active'); accordionNavLinks[ activeTabIndex ].classList.remove( 'is__active'); return; } // remove active class for inactive tabs for( let i = 0; i < tabsContent.length; i++ ) { tabsContent[ i ].classList.remove( 'is__active'); } // add active class for a current (active) tab tabsContent[ activeTabIndex ].classList.add( 'is__active'); // add active classes and remove inactive for main nav links mainNavLinks.forEach( ( el ) => { el.classList.remove( 'is__active'); }); mainNavLinks[ activeTabIndex ].classList.add( 'is__active'); if( window.innerWidth <= Number( this.options.responsiveBreak ) ) { $('html, body').animate({ scrollTop: tabsContent[ activeTabIndex ].offsetTop - ($('.site-header').height() + 74) }, 0); } // add active classes and remove inactive for accordion nav links accordionNavLinks.forEach( ( el ) => { el.classList.remove( 'is__active'); }); accordionNavLinks[ activeTabIndex ].classList.add( 'is__active'); self.initLocationMap(tabsContent[ activeTabIndex ]); } }); }); } // responsive: tabs to accordion handleResponsive() { let tabs = this.elems, responsiveClassName = 'is__responsive', tabsStyle = this.options.type; window.addEventListener( 'resize', () => { // walk on all tabs on the page tabs.forEach( ( el, i ) => { let tabsElem = el, tabsContent = tabsElem.getElementsByClassName( 'location-tabs__content'), mainNavLinks = tabsElem.querySelectorAll( '.location-tabs__nav > .location-tabs__nav_link'), accordionNavLinks = tabsElem.querySelectorAll( '.location-tabs__content > .location-tabs__nav_link'); this.skipIfInitialized( tabsElem ); if( window.innerWidth > Number( this.options.responsiveBreak ) ) { tabsElem.classList.remove( responsiveClassName ); if( tabsStyle != 'accordion' ) { // set first active tab if all of tabs were closed in accordion mode let openTabs = tabsElem.querySelectorAll( '.location-tabs__nav_link.is__active'); if( openTabs.length == 0 ) { tabsContent[0].classList.add('is__active'); mainNavLinks[0].classList.add('is__active'); accordionNavLinks[0].classList.add('is__active'); } } } else { tabsElem.classList.add( responsiveClassName ); } }); }); // manually fire resize event window.dispatchEvent( new Event( 'resize' )); } } class LocationsBlock { /** * 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-locations'); this.mapBlocks = document.querySelectorAll('.location-map-wrapper'); this.init(); } init() { new VanillaTabs({ 'selector': '.location-tabs', // default is ".tabs" 'type': 'vertical', // can be horizontal / vertical / accordion 'responsiveBreak': 996, // tabs become accordion on this device width 'activeIndex' : 0 // active tab index (starts from 0 ). Can be -1 for accordions. }); } } new LocationsBlock();
    This component is not currently used on any pages.
    There are is no readme file with this component.