140 lines
5.4 KiB
JavaScript
140 lines
5.4 KiB
JavaScript
/**
|
||
* @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);
|