Source: attendant/attendant.js

import 'shiny';
import 'jquery'

let attendants = new Map();

/**
 * Handles messages from the shiny server to set the progress
 * of the bar and optionally its text.
 * @function
 * @param  {JSON} msg - JSON object sent from shiny server.
 */
const handleProgress = (msg) => {
  let $el = $(`#${msg.id} .progress-bar`);
  let w = getWidth(msg.id, msg.value);
  $(`#${msg.id}`).show();
 
  $el
    .attr('aria-valuenow', msg.value)
    .css('width', w + '%');

  if(msg.text)
    $el.html(msg.text);

  if(msg.hideOnEnd && getMax(msg.id) <= msg.value)
    $(`#${msg.id}`).hide();
}

/**
 * Get the max value from a progress bar.
 * @function
 * @param  {string} id - Id of the progress bar.
 */
const getMax = (id) => {
  let max = $(`#${id} .progress-bar`)
    .attr('aria-valuemax');

  return parseFloat(max);
}

/**
 * Get the width the progress bar should be set to.
 * @function
 * @param  {string} id - Id of the progress bar.
 * @param  {number} value - Value sent from the server
 * must be more than the 'min' of the progress bar and less
 * than the 'max'.
 */
const getWidth = (id, value) => {
  let max = getMax(id);
  return (value / max) * 100;
}

Shiny.addCustomMessageHandler('attendant-set-min', (msg) => {
  $(`#${msg.id} .progress-bar`)
    .attr('aria-valuemin', msg.min);
});

Shiny.addCustomMessageHandler('attendant-set-max', (msg) => {
  $(`#${msg.id} .progress-bar`)
    .attr('aria-valuemax', msg.max);
});

Shiny.addCustomMessageHandler('attendant-set', handleProgress);

Shiny.addCustomMessageHandler('attendant-done', (msg) => {
  let existingAttendant = attendants.get(msg.id);

  if(existingAttendant != undefined)
    clearInterval(existingAttendant);

  msg.value = getMax(msg.id);
  handleProgress(msg);
})

Shiny.addCustomMessageHandler('attendant-auto', (msg) => {
  let $el = $(`#${msg.id} .progress-bar`);
  let max = getMax(msg.id);
  let value = parseFloat($el.attr('aria-valuenow'));

  let sequence = setInterval(() => {
    if(value > max)
      return ;

    value = value + 1;
    let w = getWidth(msg.id, value);
  
    $(`#${msg.id} .progress-bar`)
      .attr('aria-valuenow', value)
      .css('width', w + '%');
  }, msg.ms);

  attendants.set(msg.id, sequence);
})