@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.