Source: hostess/hostess.js

import ldBar from '@loadingio/loading-bar/lib/loading-bar.js';
import '@loadingio/loading-bar/dist/loading-bar.css';

let hostesses = [];
let intervals = [];

Shiny.addCustomMessageHandler('hostess-init', (opts) => {
  hostesses[opts.id] = new ldBar("#" + opts.id);
  
  if(!opts.infinite)
    return ;

  let value = 0,
      inc = 0,
      end = 100;

  intervals[opts.id] = setInterval(function(){
    inc = ((end - value) / (end + value));
    value = Math.round((value + inc + Number.EPSILON) * 1000) / 1000
    hostesses[opts.id].set(value);
  }, 350);
});

Shiny.addCustomMessageHandler('hostess-set', (opts) => {
  hostesses[opts.id].set(opts.value);

  if(opts.value != 100)
    return ;
  
  let notif = document.getElementById(opts.id);

  if(notif != undefined)
    notif.remove();
});

Shiny.addCustomMessageHandler('hostess-notify', (opts) => {

  // create div
  let notification = document.createElement("DIV");

  // position div
  let pos = position_to_coords(opts.position);
  notification.style.bottom = pos.bottom;
  notification.style.right = pos.right;
  notification.style.left = pos.left;
  notification.style.top = pos.top;

  notification.height = '100px';
  notification.style.color = opts.text_color;
  notification.style.backgroundColor = opts.background_color;
  notification.style.position = "fixed";
  notification.innerHTML = opts.html;
  notification.style.zIndex = 999;
  notification.id = opts.id;
  notification.classList.add("waitress-notification");
  document.body.appendChild(notification);

  hostesses[opts.id] = new ldBar("#" + opts.id);
  
  if(!opts.infinite)
    return ;

  let value = 0,
      inc = 0,
      end = 100;

  intervals[opts.id] = setInterval(function(){
    inc = ((end - value) / (end + value));
    value = Math.round((value + inc + Number.EPSILON) * 1000) / 1000
    hostesses[opts.id].set(value);
  }, 350);
});

Shiny.addCustomMessageHandler('hostess-end', (opts) => {
  let bar = document.getElementById(opts.id);
  
  if(opts.infinite){
    clearInterval(intervals[opts.id]);
    hostesses[opts.id].set(95);
  }
  
  if(bar != undefined)
    setTimeout(function(){
      bar.remove();
    }, 350);
});

/**
 * Convert position of to coordinates.
 * @function
 * @param {string} position - Convert position to an array of
 * coordinates.
 */
const position_to_coords = (position) => {
  let pos = {};

  let base_y = 100;
  let current_notifications = document.getElementsByClassName("waitress-notification");

  for(let n of current_notifications){
    base_y = base_y + 100 + n.offsetHeight;
  }

  if(position == "bl"){
    pos.top = "auto";
    pos.bottom = (base_y + 10) + 'px';
    pos.left = "10px";
    pos.right = "auto";
  } else if (position == "tl"){
    pos.top = (base_y + 10) + 'px';
    pos.bottom = "auto";
    pos.left = "10px";
    pos.right = "auto";
  } else if(position == "br"){
    pos.top = "auto";
    pos.bottom = (base_y + 10) + 'px';
    pos.left = "auto";
    pos.right = "10px";
  } else if(position == "tr"){
    pos.top = (base_y + 10) + 'px';
    pos.bottom = "auto";
    pos.left = "auto";
    pos.right = "10px";
  }

  return pos;
}