diff --git a/ubc_paragraph_entities/js/tabcordion_anchor.js b/ubc_paragraph_entities/js/tabcordion_anchor.js new file mode 100644 index 0000000..77b324e --- /dev/null +++ b/ubc_paragraph_entities/js/tabcordion_anchor.js @@ -0,0 +1,88 @@ +(function (Drupal, once) { + 'use strict'; + + function findTabByContentId(hash) { + if (!hash) return null; + const targetId = hash.replace('#', ''); + const targetElement = document.getElementById(targetId); + if (!targetElement) return null; + const tabPane = targetElement.closest('.tab-pane'); + if (!tabPane || !tabPane.id) return null; + return tabPane.id.split('-')[1]; // Return only the pane number + } + + function scrollToAnchor(hash) { + if (!hash) return; + const anchor = document.getElementById(hash.replace('#', '')); + if (anchor) { + setTimeout(() => { + anchor.scrollIntoView({ behavior: 'smooth' }); + }, 200); + } + } + + function activateTabFromAnchor(hash) { + if (!hash) return; + + const paneNumber = findTabByContentId(hash); + if (!paneNumber) return; + + const isMobile = window.innerWidth < 980; + const cleanedHash = hash.replace('#', ''); + + function triggerClick(element) { + if (element) { + element.click(); + } + } + + if (isMobile) { + const mobileTabButton = document.querySelector(`[data-bs-target="#collapse-${paneNumber}"]`); + const targetContent = document.getElementById(`collapse-${paneNumber}`); + + if (!mobileTabButton || !targetContent) return; + + // If the target tab is already open, do nothing + if (targetContent.classList.contains('show')) return; + + // Close all other open tabs before opening the new one + document.querySelectorAll('.tabcordion__heading:not(.collapsed)').forEach((heading) => { + heading.classList.add('collapsed'); + heading.setAttribute('aria-expanded', 'false'); + }); + + document.querySelectorAll('.tabcordion__content.show').forEach((content) => { + content.classList.remove('show'); + }); + + triggerClick(mobileTabButton); + } else { + const tabButton = document.getElementById(`tab-${paneNumber}`); + triggerClick(tabButton); + scrollToAnchor(cleanedHash); + } + } + + Drupal.behaviors.tabcordionAnchor = { + attach(context) { + // Page load with hash + once('tabcordionAnchorInit', 'html', context).forEach(() => { + if (window.location.hash) { + activateTabFromAnchor(window.location.hash); + } + }); + + // Anchor link clicks + once('tabcordionAnchorLink', 'a[href^="#"]', context).forEach((anchorLink) => { + anchorLink.addEventListener('click', (event) => { + const hash = anchorLink.getAttribute('href'); + if (hash) { + event.preventDefault(); + window.location.hash = hash; + activateTabFromAnchor(hash); + } + }); + }); + }, + }; +})(Drupal, once); \ No newline at end of file diff --git a/ubc_paragraph_entities/templates/paragraph--tabcordion.html.twig b/ubc_paragraph_entities/templates/paragraph--tabcordion.html.twig index f3136af..a5d93d3 100644 --- a/ubc_paragraph_entities/templates/paragraph--tabcordion.html.twig +++ b/ubc_paragraph_entities/templates/paragraph--tabcordion.html.twig @@ -39,6 +39,7 @@ */ #} +{{ attach_library('ubc_paragraph_entities/tabcordion_anchor') }} {% set layout_attributes = create_attribute() %} {% set classes = [ diff --git a/ubc_paragraph_entities/ubc_paragraph_entities.libraries.yml b/ubc_paragraph_entities/ubc_paragraph_entities.libraries.yml new file mode 100644 index 0000000..a28f58d --- /dev/null +++ b/ubc_paragraph_entities/ubc_paragraph_entities.libraries.yml @@ -0,0 +1,7 @@ +tabcordion_anchor: + version: 1.x + js: + js/tabcordion_anchor.js: {} + dependencies: + - core/drupal + - core/once \ No newline at end of file diff --git a/ubc_paragraph_entities/ubc_paragraph_entities.module b/ubc_paragraph_entities/ubc_paragraph_entities.module index 1f283b9..a0db153 100644 --- a/ubc_paragraph_entities/ubc_paragraph_entities.module +++ b/ubc_paragraph_entities/ubc_paragraph_entities.module @@ -36,4 +36,4 @@ function ubc_paragraph_entities_theme($existing, $type, $theme, $path) { 'base hook' => 'paragraph' ], ]; -} +} \ No newline at end of file