Components
53
Accordion Items Article Selection Author Info Basic Carousel Basic Hero Basic Map Blog Pull Out Carousel Carousel 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

Contact

Field
Field Type
Field Name
Instructions
Lede
tab
Sidebar Text
text
sidebar_text
Content
wysiwyg
content
Forms
tab
Forms
repeater
forms
-- Heading
text
heading
-- Form
forms
form
Background
tab
Image
image
background_image
Position
select
position
Size
select
size

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

.block-contact {
  position: relative;
	padding: rem-calc(60px) 0;

	/** Removed Temp for sync
	&.block-contact--no-tabs {
		.block-contact__content>div:nth-of-type(2) {
			grid-row: 1;
		}
		.tab-content {
			padding-top: 30px;
			@include bp($xl) {
				padding-top: 0;
			}
		}
	}
		**/

  .gform_wrapper.gravity-theme {
    input::placeholder,
    textarea::placeholder,
    select::placeholder {
      color: rgba(255, 255, 255, 0.4);
      opacity: 1; /* Firefox requires this to customize placeholders */
    }
  }

  &.has-secondary-background-color {
    .gform_wrapper.gravity-theme {
      input[type=submit].gform_button {
        background-color: $primary-blue;
        border-color: $primary-blue;

        &:hover {
          background-color: $primary;
          border-color: $primary;
        }
      }
    }
  }

  &.has-background:not(.has-light-blue-background-color) {
    color: $white;

    p {
      color: $white;
    }

    .block-contact__sidebar {
      &:after {
        background-color: rgba(255, 255, 255, .2);
      }
    }

    .gform_wrapper.gravity-theme {
      .gfield_required {
        color: $white;
      }

      .gform_body {
        .gfield {
          input,
          textarea,
          select {
            border-color: rgba(255, 255, 255, .2);
          }

          select {
            background-image: url('/wp-content/themes/moore-global/assets/icons/select-dropdown-light.svg');
          }
        }

        .gfield_error .gfield_label,
        .gfield_error label {
          color: $white;
        }

        .gfield_radio {
          input[type=radio]:after {
            background-color: $white;
          }
        }

        .gfield_checkbox input[type=checkbox]:checked {
          background: url('/wp-content/themes/moore-global/assets/icons/checked-light.svg') center/40% no-repeat transparent;
        }

        .gfield_html {
          a {
            color: $white;
          }
        }

        .ginput_container_consent input[type=checkbox]:checked {
          background: url('/wp-content/themes/moore-global/assets/icons/checked-light.svg') center/40% no-repeat transparent;
        }

        // .gfield_description.gfield_validation_message {
        //   padding: 8px 12px;
        //   background: #fff9f9;
        //   border-radius: 5px;
        // }
      }
    }
  }

  &__sidebar {
		@include section-sidebar;
    background-color: transparent;
	}

  &__content {
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    padding: 0 16px;
    column-gap: $grid-gutter-width;
    position: relative;
    z-index: 2;

    @include bp($lg) {
      padding: 0 80px;
    }

    & > div {
      &:nth-of-type(1) {
        grid-row: 1;
        grid-column: 1 / 13;
        margin-bottom: 30px;

        @include bp($xl) {
          margin-bottom: 0;
          grid-column: 2 / 6;
        }
      }

      &:nth-of-type(2) {
        grid-row: 2;
        grid-column: 1 / 13;

        @include bp($xl) {
          grid-row: 1;
          grid-column: 7 / 13;
        }
      }
    }

    h1, h2, h3, h4, h5, h6 {
      @include fluid-type(24, 40, 375, $max-container-size);
      font-weight: 400;
    }

    p {
      line-height: 1.5;
    }
  }

  &__tabs {
    @include list-unstyled;
    margin: 0 0 35px;
    display: flex;
    align-items: center;
    flex-wrap: nowrap;
    column-gap: clamp(20px, 2vw, 40px);
    row-gap: 16px;
    overflow-x: auto;
    width: calc(100% + 32px);
    position: relative;
    left: -16px;
    padding: 0 16px;
    -ms-overflow-style: none;
    scrollbar-width: none;

    @include bp($md) {
      flex-wrap: wrap;
      overflow: hidden;
      width: auto;
      left: auto;
      padding: 0;
    }

    &::-webkit-scrollbar {
      display: none;
    }

    li {
      text-transform: uppercase;
      letter-spacing: 0.05em;
      line-height: 1;
      position: relative;
      cursor: pointer;
      padding-bottom: 5px;
      white-space: nowrap;

      &:after {
        content: "";
        width: 100%;
        height: 2px;
        display: block;
        background-color: $primary-blue;
        position: absolute;
        left: 0;
        bottom: 0;
        max-width: 0;
        transition: max-width .2s;
      }

      &.current {
        font-weight: 600;

        &:after {
          max-width: 100%;
        }
      }

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

  .tab-content {
    display: none;

    &.current {
      display: block;
    }
  }

  picture {
    z-index: 1;
  }

  // Single form styles
  &--single-form {
    .block-contact__content {
      & > div {
        &:nth-of-type(1) {
          margin-bottom: 30px;

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

        &:nth-of-type(2) {
          grid-row: 2;
          grid-column: 1 / 13;
          margin: 0;
          padding: 0;

          @include bp($xl) {
            grid-row: 1;
            grid-column: 7 / 13;
          }

          > div {
            margin: 0;
            padding: 0;
          }
        }
      }

      .gform_wrapper {
        .gform_description,
        .gfield_description {
          margin: 0;
          padding: 0;
        }
      }
    }

    .block-contact__tabs {
      display: none;
      margin: 0;
      padding: 0;
    }

    .tab-content {
      display: block !important;
      opacity: 1 !important;
      margin: 0;
      padding: 0;
    }

    .tab-content-wrapper {
      margin: 0;
      padding: 0;
    }
  }

  // Tab content wrapper styles
  .tab-content-wrapper {
    // Add any specific tab content wrapper styles here if needed
  }
}
class ContactBlock {
	constructor() {
		this.init();
	}

	init() {
		// Find all contact blocks on the page
		this.blocks = document.querySelectorAll('.block-contact');

		if (!this.blocks.length) return;

		// Set up tab click handlers
		this.setupTabClickHandlers();

		// Check URL parameters on page load
		this.handleUrlParameters();

		// Expose global navigation function
		window.navigateToContactTab = this.navigateToContactTab.bind(this);

		// Set up special link interceptor
		this.setupLinkInterceptor();

		//console.log('ContactBlock initialized with', this.blocks.length, 'blocks found');
	}

	setupLinkInterceptor() {
		// Add a global click handler for all links on the page
		document.addEventListener('click', (event) => {
			// Check if the click was on an anchor or its child
			const link = event.target.closest('a');
			if (!link) return;

			// Get the href attribute
			const href = link.getAttribute('href');
			if (!href) return;

			// Check if it matches our special pattern: #fragment?tab=number
			if (href.includes('#') && href.includes('?tab=')) {
				// Prevent the default navigation
				event.preventDefault();

				//console.log('Intercepted link click with URL:', href);

				// Parse the URL
				let hashPart = '';
				let tabParam = null;

				// Case 1: Full URL with hash and parameters
				if (href.includes('://' && href.includes('#'))) {
					const urlParts = href.split('#');
					if (urlParts.length > 1) {
						const hashAndQuery = urlParts[1];
						if (hashAndQuery.includes('?')) {
							const fragmentParts = hashAndQuery.split('?');
							hashPart = '#' + fragmentParts[0];
							const queryPart = new URLSearchParams(fragmentParts[1]);
							tabParam = queryPart.get('tab');
						}
					}
				}
				// Case 2: Just hash and parameters
				else if (href.startsWith('#') && href.includes('?')) {
					const fragmentParts = href.split('?');
					hashPart = fragmentParts[0];
					const queryPart = new URLSearchParams(fragmentParts[1]);
					tabParam = queryPart.get('tab');
				}

				//console.log('Parsed link parts:', { hashPart, tabParam });

				// If we have the hash and tab parameter, handle the navigation
				if (hashPart && tabParam !== null) {
					// Get the target element
					const targetId = hashPart.startsWith('#') ? hashPart : `#${hashPart}`;
					let targetElement = document.querySelector(targetId);

					// Try alternative methods to find the element
					if (!targetElement && targetId.startsWith('#')) {
						const idWithoutHash = targetId.substring(1);
						targetElement = document.getElementById(idWithoutHash);
					}

					// If still not found and it's contact-section, try the contact block
					if (!targetElement && (targetId === '#contact-section' || targetId === 'contact-section')) {
						targetElement = document.querySelector('.block-contact');
					}

					// If still not found, just use any contact block
					if (!targetElement) {
						targetElement = document.querySelector('.block-contact');
					}

					if (targetElement) {
						// Process the tab parameter
						const tabIndex = parseInt(tabParam) - 1;

						// Select the tab first
						this.blocks.forEach(block => {
							const tabs = block.querySelectorAll('.tab-link');
							if (tabIndex >= 0 && tabIndex < tabs.length) {
								tabs[tabIndex].click();
							}
						});

						// Then scroll to the target
						setTimeout(() => {
							const headerHeight = document.querySelector('header')?.offsetHeight || 0;
							const additionalOffset = 60;

							const scrollPosition = targetElement.getBoundingClientRect().top +
								window.pageYOffset - headerHeight - additionalOffset;

							window.scrollTo({
								top: scrollPosition,
								behavior: 'smooth'
							});

							// Update the URL hash without page reload
							history.pushState(null, null, hashPart);
						}, 100);
					}
				}
			}
		});
	}

	setupTabClickHandlers() {
		this.blocks.forEach(block => {
			const tabs = block.querySelectorAll('.tab-link');
			const tabContents = block.querySelectorAll('.tab-content');

			//console.log('Setting up tab handlers for', tabs.length, 'tabs');

			tabs.forEach(tab => {
				tab.addEventListener('click', () => {
					// Remove current class from all tabs and contents
					tabs.forEach(t => t.classList.remove('current'));
					tabContents.forEach(c => c.classList.remove('current'));

					// Add current class to clicked tab
					tab.classList.add('current');

					// Show the corresponding tab content
					const tabId = tab.getAttribute('data-tab');
					const tabContent = block.querySelector(`#${tabId}`);
					//console.log('Tab clicked:', tabId, tabContent ? 'found' : 'not found');
					if (tabContent) {
						tabContent.classList.add('current');
					}
				});
			});
		});
	}

	handleUrlParameters() {
		// Check for regular tab parameter in query string
		const urlParams = new URLSearchParams(window.location.search);
		let tabParam = urlParams.get('tab');

		// Check for the special case where #section?tab=1 format is used
		// This happens when the hash is followed by query parameters
		if (!tabParam && window.location.hash && window.location.hash.includes('?')) {
			const hashParts = window.location.hash.split('?');
			const hashFragment = hashParts[0];
			const hashQuery = new URLSearchParams(hashParts[1]);
			tabParam = hashQuery.get('tab');

			// Store the clean hash for later use
			this.cleanHash = hashFragment;
			//console.log('Special URL format detected:', window.location.hash, 'cleaned to', this.cleanHash, 'with tab param', tabParam);
		} else {
			this.cleanHash = window.location.hash;
			//console.log('Standard URL format:', window.location.hash, 'with search params', window.location.search, 'tab param:', tabParam);
		}

		// If we have a tab parameter, select the tab
		if (tabParam !== null) {
			// Find the corresponding tab (tab indexes are 0-based in code but 1-based in URL)
			const tabIndex = parseInt(tabParam) - 1;
			//console.log('Setting active tab to index', tabIndex);

			this.blocks.forEach(block => {
				const tabs = block.querySelectorAll('.tab-link');

				// If the tab index is valid
				if (tabIndex >= 0 && tabIndex < tabs.length) {
					// Click the tab to activate it
					setTimeout(() => {
						//console.log('Clicking tab at index', tabIndex);
						tabs[tabIndex].click();
					}, 100);
				} else {
					//console.log('Tab index out of range:', tabIndex, 'max is', tabs.length - 1);
				}
			});
		}

		// Check for fragment identifier (anchor)
		if (this.cleanHash) {
			// Allow the page to first render and tabs to be set
			setTimeout(() => {
				//console.log('Looking for element with ID:', this.cleanHash);

				// Try multiple selector methods
				let targetElement = null;

				// Method 1: Direct querySelector
				targetElement = document.querySelector(this.cleanHash);

				// Method 2: Try without the # if it starts with one
				if (!targetElement && this.cleanHash.startsWith('#')) {
					const idWithoutHash = this.cleanHash.substring(1);
					targetElement = document.getElementById(idWithoutHash);
					//console.log('Trying getElementById with:', idWithoutHash, targetElement ? 'found' : 'not found');
				}

				// Method 3: If we're looking for contact-section, get the contact block directly
				if (!targetElement && (this.cleanHash === '#contact-section' || this.cleanHash === 'contact-section')) {
					targetElement = document.querySelector('.block-contact');
					//console.log('Falling back to .block-contact selector:', targetElement ? 'found' : 'not found');
				}

				// Method 4: Last resort - find any contact block with an ID
				if (!targetElement) {
					targetElement = document.querySelector('.block-contact[id]');
					//console.log('Last resort - any contact block with ID:', targetElement ? 'found' : 'not found');
				}

				if (targetElement) {
					//console.log('Target element found:', targetElement);
					// Get header height for proper offset
					const headerHeight = document.querySelector('header')?.offsetHeight || 0;
					const additionalOffset = 60;

					// Calculate scroll position
					const scrollPosition = targetElement.getBoundingClientRect().top +
						window.pageYOffset - headerHeight - additionalOffset;

					//console.log('Scrolling to position:', scrollPosition, 'with offset adjustments:',
					//	'headerHeight=', headerHeight, 'additionalOffset=', additionalOffset);

					// Scroll to element
					window.scrollTo({
						top: scrollPosition,
						behavior: 'smooth'
					});
				} else {
					console.error('Target element not found for selector:', this.cleanHash);
					// List all IDs on the page to help debug
					const allElements = document.querySelectorAll('[id]');
					//console.log('Available IDs on page:', Array.from(allElements).map(el => el.id));

					// Last resort: Find the contact block and scroll to it anyway
					const contactBlock = document.querySelector('.block-contact');
					if (contactBlock) {
						//console.log('Falling back to scrolling to contact block without ID');
						const headerHeight = document.querySelector('header')?.offsetHeight || 0;
						const additionalOffset = 60;

						const scrollPosition = contactBlock.getBoundingClientRect().top +
							window.pageYOffset - headerHeight - additionalOffset;

						window.scrollTo({
							top: scrollPosition,
							behavior: 'smooth'
						});
					}
				}
			}, 300); // Increased timeout to ensure page is fully rendered
		}
	}

	// Function to be called from buttons/links to navigate to a specific tab and section
	navigateToContactTab(tabIndex, sectionId) {
		//console.log('navigateToContactTab called with:', tabIndex, sectionId);
		// Find the correct tab and click it
		this.blocks.forEach(block => {
			const tabs = block.querySelectorAll('.tab-link');

			// Convert to 0-based index if needed (tab indexes are 0-based in code but may be 1-based in parameters)
			const adjustedIndex = tabIndex > 0 ? tabIndex - 1 : tabIndex;

			// If the tab index is valid
			if (adjustedIndex >= 0 && adjustedIndex < tabs.length) {
				// Click the tab to activate it
				tabs[adjustedIndex].click();
			}
		});

		// Then scroll to the section
		if (sectionId) {
			// Add # if not already present
			const fullSectionId = sectionId.startsWith('#') ? sectionId : `#${sectionId}`;

			// Wait a moment for any DOM updates to complete
			setTimeout(() => {
				const targetElement = document.querySelector(fullSectionId);
				if (targetElement) {
					// Get header height for proper offset
					const headerHeight = document.querySelector('header')?.offsetHeight || 0;
					const additionalOffset = 60;

					// Calculate scroll position
					const scrollPosition = targetElement.getBoundingClientRect().top +
						window.pageYOffset - headerHeight - additionalOffset;

					// Scroll to element
					window.scrollTo({
						top: scrollPosition,
						behavior: 'smooth'
					});

					// Update URL without reload
					history.pushState(null, null, fullSectionId);
				} else {
					console.error('Target element not found:', fullSectionId);
				}
			}, 100);
		}
	}
}

// Initialize on document ready
document.addEventListener('DOMContentLoaded', () => {
	//console.log('DOM loaded, initializing ContactBlock');
	new ContactBlock();
});

// Also add a direct link handler for special format URLs
window.addEventListener('load', function() {
	// Check if we have a URL in the format /#contact-section?tab=1
	if (window.location.hash && window.location.hash.includes('?')) {
		const hashParts = window.location.hash.split('?');
		const hashFragment = hashParts[0];
		const hashQuery = new URLSearchParams(hashParts[1]);
		const tabParam = hashQuery.get('tab');

		if (tabParam !== null) {
			//console.log('Direct hash handler: Found tab param', tabParam, 'and fragment', hashFragment);

			// Find the contact block and scroll to it
			setTimeout(() => {
				let targetElement = null;

				// Try to find by hash fragment
				if (hashFragment) {
					targetElement = document.querySelector(hashFragment);
					if (!targetElement && hashFragment.startsWith('#')) {
						targetElement = document.getElementById(hashFragment.substring(1));
					}
				}

				// If not found, try to find any contact block
				if (!targetElement) {
					targetElement = document.querySelector('.block-contact');
				}

				if (targetElement) {
					//console.log('Direct hash handler: Found target element', targetElement);

					// Get header height for proper offset
					const headerHeight = document.querySelector('header')?.offsetHeight || 0;
					const additionalOffset = 60;

					// Calculate scroll position
					const scrollPosition = targetElement.getBoundingClientRect().top +
						window.pageYOffset - headerHeight - additionalOffset;

					// Scroll to the element
					window.scrollTo({
						top: scrollPosition,
						behavior: 'smooth'
					});

					// Now handle the tab parameter
					const tabIndex = parseInt(tabParam) - 1;
					const contactBlocks = document.querySelectorAll('.block-contact');

					contactBlocks.forEach(block => {
						const tabs = block.querySelectorAll('.tab-link');

						if (tabIndex >= 0 && tabIndex < tabs.length) {
							setTimeout(() => {
								tabs[tabIndex].click();
							}, 100);
						}
					});
				} else {
					console.error('Direct hash handler: Could not find target element');
				}
			}, 500);
		}
	}
});
{
  "$schema": "https://schemas.wp.org/trunk/block.json",
  "apiVersion": 2,
  "name": "strategiq/contact",
  "title": "Contact Forms",
  "description": "Example block to be used as a template",
  "category": "strategiq",
  "icon": "strategiq",
  "acf": {
      "mode": "preview",
      "renderTemplate": "block-contact.php"
  },
  "supports": {
      "anchor": true,
      "align": false,
      "color": {
          "background": true,
          "text": false,
          "gradients": true
      },
      "spacing": {
          "margin": [
              "top",
              "bottom"
          ],
          "padding": [
              "top",
              "bottom"
          ]
      }
  },
  "example": {
      "attributes": {
          "mode": "preview",
          "data": {
              "heading_type": "h2",
              "heading_text": "Example - Contact Forms",
              "content": "This is some example content to represent what the content will look like"
          }
      }
  },
  "style": "file:../../assets/css/contact/block-contact.css",
  "viewScript": ["contact"]
}
This component is not currently used on any pages.
There are is no readme file with this component.