v1/web/core/themes/stable9/js/tour.js

140 lines
5.4 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* @file
* Provides backwards compatibility for Tours that no longer use Joyride.
*/
((Drupal) => {
/**
* Converts the markup of a Shepherd tour tip to match Joyride.
*
* @param {Tour} shepherdTour
* A ShepherdJS tour object.
*
* @internal
*/
Drupal.tour.convertToJoyrideMarkup = (shepherdTour) => {
/**
* Changes the tag of an element.
*
* @param {HTMLElement} element
* The element that will have its tag changed.
* @param {string} tag
* The tag the element should be changed to.
*/
const changeTag = (element, tag) => {
if (element) {
const newTagElement = document.createElement(tag);
[...element.attributes].forEach((attr) => {
newTagElement.setAttribute(attr.name, attr.value);
});
newTagElement.innerHTML = element.innerHTML;
element.parentNode.replaceChild(newTagElement, element);
}
};
// Create variables for the elements that will be rearranged.
const shepherdElement = shepherdTour.currentStep.el;
const shepherdContent = shepherdElement.querySelector('.shepherd-content');
const shepherdCancel = shepherdElement.querySelector(
'.shepherd-cancel-icon',
);
const shepherdTitle = shepherdElement.querySelector('.shepherd-title');
const shepherdText = shepherdElement.querySelector('.shepherd-text');
const shepherdNext = shepherdElement.querySelector('footer .button');
const tourProgress = shepherdElement.querySelector('.tour-progress');
// Add attributes to the elements so they match what they were when Joyride
// was providing Tour functionality.
shepherdElement.classList.add('joyride-tip-guide');
shepherdContent.classList.add('joyride-content-wrapper');
shepherdNext.classList.add('joyride-next-tip');
shepherdNext.setAttribute('href', '#');
shepherdNext.setAttribute('role', 'button');
shepherdNext.removeAttribute('type');
shepherdCancel.classList.add('joyride-close-tip');
shepherdCancel.removeAttribute('type');
shepherdCancel.setAttribute('href', '#');
shepherdCancel.setAttribute('role', 'button');
shepherdElement.setAttribute(
'data-index',
shepherdTour.currentStep.options.index,
);
shepherdElement.querySelector('footer').remove();
// Rearrange elements so their structure matches Joyride's.
shepherdContent.insertBefore(shepherdTitle, shepherdContent.firstChild);
shepherdContent.insertBefore(tourProgress, shepherdText.nextSibling);
shepherdContent.appendChild(shepherdCancel);
shepherdContent.querySelector('.shepherd-header').remove();
shepherdContent.insertBefore(shepherdNext, tourProgress.nextSibling);
shepherdCancel.innerHTML = '<span aria-hidden="true">×</span>';
shepherdTitle.classList.add('tour-tip-label');
// Convert elements to use the tags they used in Joyride.
changeTag(shepherdTitle, 'h2');
// Remove the wrapper Shepherd adds for tip content.
shepherdText.outerHTML = shepherdText.innerHTML;
// Convert the next and cancel buttons to links so they match Joyride's
// markup. They must be re-queried as they were potentially moved elsewhere
// in the DOM.
changeTag(shepherdElement.querySelector('.joyride-close-tip'), 'a');
changeTag(shepherdElement.querySelector('.joyride-next-tip'), 'a');
// The arrow protruding from a tip pointing to the element it references.
const shepherdArrow = shepherdElement.querySelector('.shepherd-arrow');
if (shepherdArrow) {
shepherdArrow.classList.add('joyride-nub');
if (shepherdTour.currentStep.options.attachTo.on) {
// Shepherd's positions are opposite of Joyride's as they specify the
// tip location relative to the corresponding element as opposed to
// their location on the tip itself.
const stepToTipPosition = {
bottom: 'top',
top: 'bottom',
left: 'right',
right: 'left',
};
shepherdArrow.classList.add(
// Split at '-' as shepherd positioning accommodates dash-delimited
// secondary axis positioning.
// shepherdTour.currentStep.options.attachTo.on.split('-')[0]
stepToTipPosition[
// Split at '-' as shepherd positioning accommodates dash-delimited
// secondary axis positioning.
shepherdTour.currentStep.options.attachTo.on.split('-')[0]
],
);
}
changeTag(shepherdArrow, 'span');
} else {
// If there is no Shepherd arrow, there still needs to be markup for a
// non-displayed nub to match Joyride's markup.
const nub = document.createElement('span');
nub.classList.add('joyride-nub');
nub.setAttribute('style', 'display: none;');
shepherdElement.insertBefore(nub, shepherdElement.firstChild);
}
// When the next and cancel buttons were converted to links, they became
// new DOM elements that no longer have their associated event listeners.
// The events must be reintroduced here.
shepherdElement
.querySelector('.joyride-next-tip')
.addEventListener('click', (e) => {
e.preventDefault();
shepherdTour.next();
});
shepherdElement
.querySelector('.joyride-close-tip')
.addEventListener('click', (e) => {
e.preventDefault();
shepherdTour.cancel();
});
shepherdElement.querySelector('.joyride-next-tip').focus();
};
})(Drupal);