refreshless-8.x-1.x-dev/modules/refreshless_turbo/js/progress_bar.js
modules/refreshless_turbo/js/progress_bar.js
/**
* @file
* Implements our progress bar, replacing Turbo's implementation.
*
* @see https://www.drupal.org/project/refreshless/issues/3488464
* Issue for adapting this out of Turbo.
*/
(function(ProgressBar, ProgressBarDelay, $, delayAmount) {
'use strict';
/**
* Our event namespace.
*
* @type {String}
*
* @see https://learn.jquery.com/events/event-basics/#namespacing-events
*/
const eventNamespace = 'refreshless-turbo-progress-bar';
/**
* RefreshLess Turbo progress bar class.
*/
class TurboProgressBar {
/**
* The context element to attach to; usually the <html> element.
*
* @type {HTMLElement}
*/
#context;
/**
* Progress bar class instance.
*
* @type {ProgressBar}
*/
#progressBar;
/**
* Progress bar delay class instance.
*
* @type {ProgressBarDelay}
*/
#progressBarDelay;
constructor(context) {
this.#context = context;
this.#progressBar = new ProgressBar();
// @todo Make delay amount configurable.
this.#progressBarDelay = new ProgressBarDelay(
delayAmount, this.#progressBar,
);
this.#bindEventHandlers();
}
/**
* Destroy this instance.
*/
destroy() {
this.#unbindEventHandlers();
// This will also destroy the progress bar it wraps.
this.#progressBarDelay.destroy();
}
/**
* Bind all of our event handlers.
*/
#bindEventHandlers() {
// @see https://ambientimpact.com/web/snippets/javascript-template-literal-as-object-property-name
$(this.#context).on({
[`refreshless:before-fetch-request.${eventNamespace}`]: (event) => {
this.#beforeFetchRequestHandler(event);
},
[`refreshless:form-submit-start.${eventNamespace}`]: (event) => {
this.#submitStartHandler(event);
},
});
}
/**
* Unbind all of our event handlers.
*/
#unbindEventHandlers() {
$(this.#context).off(`.${eventNamespace}`);
}
/**
* 'refreshless:before-fetch-request' event handler.
*
* @param {jQuery.Event} event
*
* @todo Reduce duplicate code with submit handler.
*/
#beforeFetchRequestHandler(event) {
if (
// Ignore prefetch and preload requests.
event.detail.isPrefetch === true ||
event.detail.isPreload === true ||
// We want to specifically ignore forms as those are handled separately
// via the 'refreshless:form-submit-start' and
// 'refreshless:form-submit-response' event handlers.
event.detail.isFormSubmit === true
) {
return;
}
this.#progressBarDelay.showVisitAfterDelay();
const hide = () => {
this.#progressBarDelay.hideVisit();
$(this.#context).off([
`refreshless:before-render.${eventNamespace}`,
`refreshless:reload.${eventNamespace}`,
`refreshless:fetch-request-error.${eventNamespace}`,
].join(' '), hide);
};
$(this.#context).one([
// We're using refreshless:before-render rather than refreshless:load
// because the progress bar is noticeably less smoothly transitioned
// out when loading some complex/heavy pages on mobile in some
// browsers, specifically Firefox on Android. The
// refreshless:before-render is early enough that it should give the
// progress bar enough time to transition out before the new <body>
// contents are rendered.
`refreshless:before-render.${eventNamespace}`,
// If a reload occurs as a result of this navigation, hide the progress
// bar. Since we're using the before-render event, that would never get
// triggered during a reload since Turbo isn't about to render
// anything.
`refreshless:reload.${eventNamespace}`,
`refreshless:fetch-request-error.${eventNamespace}`,
].join(' '), hide);
}
/**
* 'refreshless:form-submit-start' event handler.
*
* @param {jQuery.Event} event
*
* @todo Reduce duplicate code with before fetch request handler.
*/
#submitStartHandler(event) {
this.#progressBarDelay.showFormAfterDelay();
const hide = () => {
this.#progressBarDelay.hideForm();
$(this.#context).off([
`refreshless:form-submit-response.${eventNamespace}`,
`refreshless:fetch-request-error.${eventNamespace}`,
].join(' '), hide);
};
$(this.#context).one([
`refreshless:form-submit-response.${eventNamespace}`,
`refreshless:fetch-request-error.${eventNamespace}`,
].join(' '), hide);
}
}
// Merge Drupal.RefreshLess.classes into the existing Drupal global.
$.extend(true, Drupal, {RefreshLess: {classes: {
TurboProgressBar: TurboProgressBar,
}}});
})(
Drupal.RefreshLess.classes.ProgressBar,
Drupal.RefreshLess.classes.ProgressBarDelay,
jQuery,
Turbo.session.progressBarDelay,
);
