Sabre Spark

ProgressIndicator

Option name Type Description
module components/progress-indicator.js
Example
new ProgressIndicator(el, {
  // Optional. The precision of the progress percentage.
  precision: 10 // 10 decimal places
});

ProgressIndicator

function
 ProgressIndicator() 

ProgressIndicator constructor.

Option name Type Description
el Element
params Object
var ProgressIndicator = function(el, params) {

  if (!el) {
    return;
  }

  this._setParams(this.defaults, true);
  this._cacheElements(el);
  this._setParams(params || {});
  this._bindEventListenerCallbacks();
  this._addEventListeners();
};

ProgressIndicator.prototype = {

_setParams

property
 _setParams 

Include common functionality.

_setParams: Base.setParams,
_toggleClass: Base.toggleClass,
_hasClass: Base.hasClass,
_round: Base.round,
remove: Base.remove,

_whitelistedParams

property
 _whitelistedParams 

Whitelisted parameters which can be set on construction.

Option name Type Description
_whitelistedParams: ['precision'],

defaults

property
 defaults 

Default values for internal properties we will be setting.
These are set on each construction so we don't leak properties
into the prototype chain.

Option name Type Description
defaults: {
  el: null,
  progressEl: null,
  statusEl: null,
  noteEls: null,
  meterEl: null,
  fillEl: null,
  meterHeight: 0,
  meterWidth: 0,
  notes: null,
  isDeterminate: false,
  value: null,
  precision: 0,
  lastDOMUpdateTime: 0,
  _onResizeBound: null
},

set

method
 set() 

Set the value of the indicator.

Option name Type Description
val Number
set: function(val) {

  if (val === this.value) {
    return;
  }

  if (val > 1) {
    val = 1;
  }

  this.value = val;

  if (this.isDeterminate && this.progressEl) {
    this.progressEl.setAttribute('value', Math.round(val * 100) / 100);
  }

  this._updateDOM();
},

_cacheElements

method
 _cacheElements() 

Store a reference to all the needed elements.

Option name Type Description
el Element
_cacheElements: function(el) {

  this.el = el;
  this.progressEl = this.el.querySelector('progress');
  this.statusEl = this.el.querySelector('.spark-progress__value-status, [role="status"]');
  this.noteEl = this.el.querySelector('.spark-progress__states');
  this.meterEl = this.el.querySelector('.spark-progress__meter');

  this.isDeterminate = this.progressEl.getAttribute('value') !== null;
  this.size = this._determineSize();

  // If this is a determinate value, replace the meter with the SVG.
  if (this.isDeterminate) {

    var svg = this._buildSVG();
    svg.setAttribute('class', this.meterEl.className);

    this.meterEl.parentNode.replaceChild(svg, this.meterEl);
    this.meterEl = svg;
    this.fillEl = this.meterEl.querySelector('.spark-progress__fill');
  }

  if (this.noteEl) {
    this._parseNotes(this.noteEl);
  }

  if (this.progressEl) {
    this.value = this.progressEl.value;
  }

  this._cacheSize();

  this._updateDOM();
},

_cacheSize

method
 _cacheSize() 

Cache the size of the meter.

_cacheSize: function() {
  this.meterHeight = this.meterEl.clientHeight;
  this.meterWidth = this.meterEl.clientWidth;
},

_determineSize

method
 _determineSize() 

Determine the size of the indicator.

Option name Type Description
return String
_determineSize: function() {

  if (this.el.className.indexOf('progress--sm') !== -1) {
    return 'small';
  } else if (this.el.className.indexOf('progress--xs') !== -1) {
    return 'extraSmall';
  }

  return 'large';
},

_buildSVG

method
 _buildSVG() 

Build the proper SVG element for this size indicator.

Option name Type Description
return Element
_buildSVG: function() {
  var size = sizes[this.size];
  var template = '<svg viewBox="0 0 ' + size.diameter + ' ' + size.diameter + '" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="' + size.track + '" stroke-width="' + size.stroke + '" class="spark-progress__track"/><path d="' + (size.fill || size.track) + '" stroke-width="' + size.stroke + '" class="spark-progress__fill"/><path d="' + size.border + '" class="spark-progress__border"/></g></g></svg>';
  var div = document.createElement('div');
  div.innerHTML = template;
  return div.children[0];
},

_parseNotes

method
 _parseNotes() 

Take an unordered list of notes and determine the ranges for
when to show a given note.

Option name Type Description
el Element
return Array
_parseNotes: function(el) {

  this.notes = this.notes || [];

  var len = el.children.length;
  var i = len - 1;

  for (; i >= 0; i--) {
    this.notes.push({
      min: parseInt(el.children[i].getAttribute('data-value'), 10),
      max: el.children[i + 1] ? parseInt(el.children[i + 1].getAttribute('data-value'), 10) - 1 : 100,
      el: el.children[i]
    });
  }
},

_updateDOM

method
 _updateDOM() 

Update the text visible based on the value. Also adjust the SVG.

_updateDOM: function() {

  if (!this.isDeterminate) {
    return;
  }

  var updateTime = Date.now();
  var val = this._round(this.value * 100, this.precision);

  // Don't animate if we're animating back to 0 or it's been less than 150ms since our last update.
  var noAnimation = val === 0 || this.lastDOMUpdateTime + 150 > updateTime;
  this._toggleClass(this.fillEl, 'no-animation', noAnimation);

  this.statusEl.innerHTML = val + '%';

  var dashArray = (sizes[this.size].diameter - sizes[this.size].stroke) * Math.PI;
  var dashOffset = dashArray - (dashArray * (val / 100));

  this.fillEl.setAttribute('style', 'stroke-dasharray: ' + dashArray + '; stroke-dashoffset: ' + dashOffset);

  this.lastDOMUpdateTime = updateTime;

  if (!this.notes) {
    return;
  }

  var i = 0;
  var len = this.notes.length;

  for (; i < len; i++) {
    this._toggleClass(this.notes[i].el, 'active', (this.notes[i].min <= val && this.notes[i].max >= val));
  }
},

_bindEventListenerCallbacks

method
 _bindEventListenerCallbacks() 

Create bound versions of event listener callbacks and store them.
Otherwise we can't unbind from these events later because the
function signatures won't match.

_bindEventListenerCallbacks: function() {
  this._onResizeBound = this._onResize.bind(this);
},

_addEventListeners

method
 _addEventListeners() 

Add event listeners for DOM events.

_addEventListeners: function() {
  window.addEventListener('resize', this._onResizeBound);
},

_removeEventListeners

method
 _removeEventListeners() 

Remove event listeners for DOM events..

_removeEventListeners: function() {
  window.removeEventListener('resize', this._onResizeBound);
},

_onResize

method
 _onResize() 

When the window resizes, cache the dimensions.

Option name Type Description
e Object
_onResize: function() {
  this._cacheSize();
}
  };

  Base.exportjQuery(ProgressIndicator, 'ProgressIndicator');

  return ProgressIndicator;
}));