-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #336 from RRZE-Webteam/dev
feat(accordion.js) – Add BlockEditor support
- Loading branch information
Showing
5 changed files
with
178 additions
and
119 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,159 +1,206 @@ | ||
/** | ||
* RRZE Accordion 1.0.0 | ||
* RRZE Webteam | ||
* RRZE Elements Blocks - Accordion 1.0.4 | ||
* Refactored and documented for efficiency and readability. | ||
* Uses jQuery for DOM manipulation and event handling. | ||
*/ | ||
|
||
const { __, _x, _n, sprintf } = wp.i18n; | ||
|
||
jQuery(document).ready(function($) { | ||
const { __, _x, _n, sprintf } = wp.i18n; | ||
/** | ||
* Initially hides all accordion bodies except those marked as open or should stay open, | ||
* and marks the corresponding toggles as active. | ||
*/ | ||
$('.accordion-body').not(".open, .stayopen").hide(); | ||
$('.accordion-body.open').each(function() { | ||
$(this).closest('.accordion-group').find('.accordion-toggle').first().addClass('active'); | ||
}); | ||
|
||
// Close Accordions on start, except first | ||
$('.accordion-body').not(".accordion-body.open").not('.accordion-body.stayopen').hide(); | ||
$('.accordion-body.open').each( function () { | ||
$(this).closest('.accordion-group').find('button.accordion-toggle').first().addClass('active'); | ||
}) | ||
/** | ||
* Checks if all items within an accordion are open to toggle the 'Expand All/Collapse All' button state. | ||
*/ | ||
$('.accordion').each(function() { | ||
if ($(this).find('button.expand-all').length > 0) { | ||
var items = $(this).find(".accordion-group"); | ||
var open = $(this).find(".accordion-body.open"); | ||
if (items.length == open.length) { | ||
$(this).find('button.expand-all').attr("data-status", 'open').data('status', 'open').html(__('Collapse All', 'rrze-elements')); | ||
} | ||
const $accordion = $(this); | ||
const items = $accordion.find(".accordion-group"); | ||
const openItems = items.find(".accordion-body.open"); | ||
if (items.length === openItems.length) { | ||
$accordion.find('button.expand-all').attr("data-status", 'open').data('status', 'open').html(__('Collapse All', 'rrze-elements')); | ||
} | ||
}); | ||
|
||
$('.accordion-toggle').on('mousedown', function(event) { | ||
event.preventDefault(); | ||
var $accordion = $(this).attr('href'); | ||
var $name = $(this).data('name'); | ||
toggleAccordion($accordion); | ||
// Put name attribute in URL path if available, else href | ||
if (typeof($name) !== 'undefined') { | ||
window.history.replaceState(null, null, '#' + $name); | ||
} else { | ||
window.history.replaceState(null, null, $accordion); | ||
} | ||
}); | ||
/** | ||
* Retrieves the target accordion ID from a button or link element. | ||
* @param {jQuery} $elem - The element that may contain a href or data-href attribute. | ||
* @returns {string} The target selector for the accordion to be toggled. | ||
*/ | ||
function getAccordionTarget($elem) { | ||
return sanitizeSelector($elem.data('href') || $elem.attr('href')); | ||
} | ||
|
||
// Keyboard navigation for accordions | ||
$('.accordion-toggle').keydown(function(event) { | ||
if (event.keyCode == 32) { | ||
var $accordion = $(this).attr('href'); | ||
var $name = $(this).data('name'); | ||
toggleAccordion($accordion); | ||
if (typeof($name) !== 'undefined') { | ||
window.history.replaceState(null, null, '#' + $name); | ||
} else { | ||
window.history.replaceState(null, null, $accordion); | ||
} | ||
} | ||
}); | ||
/** | ||
* Sanitizes a jQuery selector to prevent jQuery selector injection. | ||
* @param {string} selector - The selector to sanitize. | ||
* @returns {string} The sanitized selector. | ||
*/ | ||
function sanitizeSelector(selector) { | ||
return selector.replace(/[^a-zA-Z0-9_\-#]/g, ''); | ||
} | ||
|
||
/** | ||
* Toggles the visibility of an accordion's content. | ||
* @param {string} $accordion - The selector of the accordion whose visibility will be toggled. | ||
*/ | ||
function toggleAccordion($accordion) { | ||
var $thisgroup = $($accordion).closest('.accordion-group'); | ||
var $othergroups = $($accordion).closest('.accordion').find('.accordion-group').not($thisgroup); | ||
$($othergroups).children('.accordion-heading').children(' .accordion-toggle').removeClass('active'); | ||
$($othergroups).children('.accordion-body').not('.accordion-body.stayopen').slideUp(); | ||
$($thisgroup).children('.accordion-heading').children('.accordion-toggle').toggleClass('active'); | ||
$($thisgroup).children('.accordion-body').slideToggle(); | ||
// refresh Slick Gallery | ||
var $slick = $($thisgroup).find("div.slick-slider"); | ||
if ($slick.length < 0) { | ||
$slick.slick("refresh"); | ||
} | ||
const $group = $($accordion).closest('.accordion-group'); | ||
const $directBody = $group.children('.accordion-body'); | ||
const $directToggle = $group.children('.accordion-heading').children('.accordion-toggle'); | ||
const $otherGroups = $group.siblings(); | ||
|
||
$otherGroups.children('.accordion-heading').children('.accordion-toggle').removeClass('active'); | ||
$otherGroups.children('.accordion-body').not('.accordion-body.stayopen').slideUp(); | ||
|
||
$directToggle.toggleClass('active'); | ||
$directBody.slideToggle(); | ||
|
||
refreshSlickGallery($group); | ||
} | ||
|
||
/** | ||
* Refreshes the Slick Gallery within an accordion if it exists. | ||
* This is needed because changes in visibility can affect Slick's layout. | ||
* @param {jQuery} $group - The accordion group that may contain a Slick slider. | ||
*/ | ||
function refreshSlickGallery($group) { | ||
const $slick = $group.find(".slick-slider"); | ||
if ($slick.length) { | ||
$slick.slick("refresh"); | ||
} | ||
} | ||
|
||
/** | ||
* Opens an accordion based on a target anchor link and scrolls to the accordion. | ||
* @param {jQuery} $target - The target accordion body element to be opened. | ||
*/ | ||
function openAnchorAccordion($target) { | ||
if ($target.closest('.accordion').parent().closest('.accordion-group')) { | ||
var $thisgroup = $($target).closest('.accordion-group'); | ||
var $othergroups = $($target).closest('.accordion').find('.accordion-group').not($thisgroup); | ||
const $thisgroup = $($target).closest('.accordion-group'); | ||
const $othergroups = $($target).closest('.accordion').find('.accordion-group').not($thisgroup); | ||
$($othergroups).find('.accordion-toggle').removeClass('active'); | ||
$($othergroups).find('.accordion-body').not('.accordion-body.stayopen').slideUp(); | ||
$($thisgroup).find('.accordion-toggle:first').not('.active').addClass('active'); | ||
$($thisgroup).find('.accordion-body:first').slideDown(); | ||
// open parent accordion bodies if target = nested accordion | ||
$($thisgroup).parents('.accordion-group').find('.accordion-toggle:first').not('.active').addClass('active'); | ||
$($thisgroup).parents('.accordion-body').slideDown(); | ||
} | ||
var offset = $target.offset(); | ||
var $scrolloffset = offset.top - 300; | ||
const offset = $target.offset(); | ||
const $scrolloffset = offset.top - 300; | ||
$('html,body').animate({ | ||
scrollTop: $scrolloffset | ||
}, 'slow'); | ||
} | ||
|
||
/** | ||
* Checks if the URL contains a hash and opens the corresponding accordion if it exists. | ||
*/ | ||
if (window.location.hash) { | ||
var identifier = window.location.hash.split('_')[0]; | ||
var inpagenum = window.location.hash.split('_')[1]; | ||
if (identifier == '#collapse') { | ||
if ($.isNumeric(inpagenum)) { | ||
var $findid = 'collapse_' + inpagenum; | ||
var $target = $('body').find('#' + $findid); | ||
const identifier = window.location.hash.split('_')[0]; | ||
const inpagenum = window.location.hash.split('_')[1]; | ||
let $target; | ||
|
||
if (identifier === '#collapse' || identifier === '#panel') { | ||
const prefix = identifier === '#collapse' ? 'collapse_' : 'panel_'; | ||
if (inpagenum) { | ||
const $findid = prefix + inpagenum; | ||
$target = $('body').find('#' + sanitizeSelector($findid)); | ||
} | ||
} else { | ||
var $findname = window.location.hash.replace('\#', ''); | ||
var $target = $('body').find('div[name=' + $findname + ']'); | ||
const $findname = window.location.hash.replace('#', ''); | ||
$target = $('body').find('div[name=' + sanitizeSelector($findname) + ']'); | ||
} | ||
if ($target.length > 0) { | ||
|
||
if ($target && $target.length > 0) { | ||
openAnchorAccordion($target); | ||
} | ||
} | ||
|
||
$('a:not(.prev, .next)').click(function(e) { // prev und next wegen Konflikt mit Timeline ausgeschlossen | ||
// nur auf Seiten, auf denen ein Accordion existiert, | ||
// und nur, wenn der geklickte Link nicht der Accordion-Toggle-Link oder der Expand-All-Link ist | ||
if (($('[id^=accordion-]').length) && | ||
(!$(this).hasClass("accordion-toggle")) && | ||
(!$(this).hasClass("accordion-tabs-nav-toggle"))) { | ||
var $hash = $(this).prop("hash"); | ||
var identifier = $hash.split('_')[0]; | ||
var inpagenum = $hash.split('_')[1]; | ||
if (identifier == '#collapse') { | ||
if ($.isNumeric(inpagenum)) { | ||
var $findid = 'collapse_' + inpagenum; | ||
var $target = $('body').find('#' + $findid); | ||
} | ||
} else { | ||
var $findname = identifier.replace('\#', ''); | ||
var $target = $('body').find('div[name=' + $findname + ']'); | ||
} | ||
if ($target) { | ||
openAnchorAccordion($target); | ||
} | ||
/** | ||
* Binds mousedown and keydown events to accordion toggles. | ||
* Prevents default action and toggles the accordion based on the target derived from the element. | ||
* Updates the URL hash if a name is provided. | ||
*/ | ||
$('.accordion-toggle').on('mousedown keydown', function(event) { | ||
if (event.type === 'mousedown' || event.keyCode === 32) { | ||
event.preventDefault(); | ||
const $target = getAccordionTarget($(this)); | ||
const $name = $(this).data('name'); | ||
toggleAccordion($target); | ||
window.history.replaceState(null, null, $name ? '#' + $name : $target); | ||
} | ||
}); | ||
|
||
$('.expand-all').click(function(e) { | ||
var $thisgroup = $(this).closest('.accordion'); | ||
if ($(this).data('status') === 'open') { | ||
$($thisgroup).find('.accordion-body').slideUp(); | ||
$($thisgroup).find('.accordion-toggle').removeClass('active'); | ||
$(this).attr("data-status", 'closed').data('status', 'closed').html(__('Expand All', 'rrze-elements')); | ||
/** | ||
* Handles clicks on the 'expand all' or 'collapse all' buttons within an accordion. | ||
* Toggles the expansion state of all accordion bodies and toggles within the same accordion. | ||
*/ | ||
$('.expand-all').on('click', function() { | ||
const $this = $(this); | ||
const $accordion = $this.closest('.accordion'); | ||
const $bodies = $accordion.find('.accordion-body'); | ||
const $toggles = $accordion.find('.accordion-toggle'); | ||
if ($this.data('status') === 'open') { | ||
$bodies.slideUp(); | ||
$toggles.removeClass('active'); | ||
$this.attr("data-status", 'closed').data('status', 'closed').html(__('Expand All', 'rrze-elements')); | ||
} else { | ||
$($thisgroup).find('.accordion-body').slideDown(); | ||
$($thisgroup).find('.accordion-toggle').addClass('active'); | ||
$(this).attr("data-status", 'open').data('status', 'open').html(__('Collapse All', 'rrze-elements')); | ||
$bodies.slideDown(); | ||
$toggles.addClass('active'); | ||
$this.attr("data-status", 'open').data('status', 'open').html(__('Collapse All', 'rrze-elements')); | ||
} | ||
}); | ||
|
||
// Assistant tabs | ||
$('.assistant-tabs-nav a').on('click', function (event) { | ||
event.preventDefault(); | ||
var pane = $(this).attr('href'); | ||
$(this).parents('ul').find('a').removeClass('active'); | ||
$(this).addClass('active'); | ||
$(this).parents('.assistant-tabs').find('.assistant-tab-pane').removeClass('assistant-tab-pane-active'); | ||
$(pane).addClass('assistant-tab-pane-active'); | ||
/** | ||
* Binds click and keydown events to links within assistant tabs. | ||
* Activates the tab associated with the clicked link and displays its corresponding pane. | ||
*/ | ||
$('.assistant-tabs-nav a').on('click keydown', function(event) { | ||
if (event.type === 'click' || event.keyCode === 32) { | ||
event.preventDefault(); | ||
const $link = $(this); | ||
const $tabs = $link.parents('.assistant-tabs'); | ||
const $paneId = $link.attr('href'); | ||
$link.closest('ul').find('a').removeClass('active'); | ||
$link.addClass('active'); | ||
$tabs.find('.assistant-tab-pane').removeClass('assistant-tab-pane-active'); | ||
$($paneId).addClass('assistant-tab-pane-active'); | ||
} | ||
}); | ||
|
||
// Keyboard navigation for assistant tabs | ||
$('.assistant-tabs-nav a').keydown('click', function (event) { | ||
if (event.keyCode == 32) { | ||
var pane = $(this).attr('href'); | ||
$(this).parents('ul').find('a').removeClass('active'); | ||
$(this).addClass('active'); | ||
$(this).parents('.assistant-tabs').find('.assistant-tab-pane').removeClass('assistant-tab-pane-active'); | ||
$(pane).addClass('assistant-tab-pane-active'); | ||
/** | ||
* Handles clicks on links and opens the corresponding accordion if it exists. | ||
*/ | ||
$('a:not(.prev, .next)').click(function(e) { | ||
if (($('[id^=accordion-]').length) && | ||
(!$(this).hasClass("accordion-toggle")) && | ||
(!$(this).hasClass("accordion-tabs-nav-toggle"))) { | ||
const $hash = $(this).prop("hash"); | ||
const identifier = $hash.split('_')[0]; | ||
const inpagenum = $hash.split('_')[1]; | ||
let $target; | ||
|
||
if (identifier === '#collapse' || identifier === '#panel') { | ||
const prefix = identifier === '#collapse' ? 'collapse_' : 'panel_'; | ||
if (inpagenum) { | ||
const $findid = prefix + inpagenum; | ||
$target = $('body').find('#' + sanitizeSelector($findid)); | ||
} | ||
} else { | ||
const $findname = identifier.replace('#', ''); | ||
$target = $('body').find('div[name=' + sanitizeSelector($findname) + ']'); | ||
} | ||
|
||
if ($target && $target.length > 0) { | ||
openAnchorAccordion($target); | ||
} | ||
} | ||
}); | ||
|
||
}); | ||
}); |
Oops, something went wrong.