@atlassian/aui
Version:
Atlassian User Interface Framework
218 lines (185 loc) • 7.24 kB
JavaScript
import $ from './jquery';
import _ from './underscore';
import skate from 'skatejs';
import { recomputeStyle } from './internal/animation';
import { supportsCssTransition } from './internal/browser';
import * as deprecate from './internal/deprecation';
import globalize from './internal/globalize';
const afterTransitionEvent = 'aui-progress-indicator-after-update';
const beforeTransitionEvent = 'aui-progress-indicator-before-update';
const transitionEnd = 'transitionend webkitTransitionEnd';
function updateProgress($progressBar, $progressBarContainer, progressValue) {
recomputeStyle($progressBar);
$progressBar.css('width', progressValue * 100 + '%');
$progressBarContainer.attr('data-value', progressValue);
}
function updateProgressElement(element, value) {
var $progressBarContainer = $(element).first();
var $progressBar = $progressBarContainer.children('.aui-progress-indicator-value');
var valueAttribute = $progressBarContainer.attr('data-value');
var currentProgress = parseFloat(valueAttribute) || 0;
var isProgressNotChanged = valueAttribute && currentProgress === value;
if (isProgressNotChanged) {
return;
}
var isIndeterminate = !valueAttribute;
//if the progress bar is indeterminate switch it.
if (isIndeterminate) {
$progressBar.css('width', 0);
}
transitionProgress($progressBar, $progressBarContainer, { currentProgress, value });
return $progressBarContainer;
}
function transitionProgress(progressBar, progressBarContainer, { currentProgress, value }) {
const $progressBar = $(progressBar);
const $progressBarContainer = $(progressBarContainer);
if (typeof value === 'number' && value <= 1 && value >= 0) {
$progressBarContainer.trigger(beforeTransitionEvent, [currentProgress, value]);
//trigger the event after transition end if supported, otherwise just trigger it
if (supportsCssTransition()) {
$progressBar.one(transitionEnd, function () {
$progressBarContainer.trigger(afterTransitionEvent, [currentProgress, value]);
});
updateProgress($progressBar, $progressBarContainer, value);
} else {
updateProgress($progressBar, $progressBarContainer, value);
$progressBarContainer.trigger(afterTransitionEvent, [currentProgress, value]);
}
}
}
function setIndeterminateOnProgressElement(element) {
var $progressBarContainer = $(element).first();
var $progressBar = $progressBarContainer.children('.aui-progress-indicator-value');
$progressBarContainer.removeAttr('data-value');
recomputeStyle($progressBarContainer);
$progressBar.css('width', '');
}
const DEFAULTS = {
indeterminate: false,
max: 1,
val: 0,
valAsFraction: 0,
valAsPercent: 0,
};
function parseDecimal(num, precision = 1) {
return Number(parseFloat(num).toFixed(precision));
}
function recalc(data, {val, max} = {}) {
let checkMax = _.isNumber(max) ? max : data.max;
let checkVal = _.isNumber(val) ? val : data.val;
const newMax = (checkMax > 0) ? checkMax : DEFAULTS.max;
const newVal = Math.max(0, Math.min(checkVal, newMax));
const valAsFraction = parseDecimal(newVal / newMax, 6);
const valAsPercent = parseDecimal(valAsFraction * 100, 2);
return {max: newMax, val: newVal, valAsFraction, valAsPercent, indeterminate: data.indeterminate};
}
function refresh(el) {
const {indeterminate, val, valAsFraction, max} = el._data;
const bar = el.querySelector('.aui-progress-indicator');
const oldVal = bar.getAttribute('data-value');
if (indeterminate) {
bar.removeAttribute('aria-valuenow');
setIndeterminateOnProgressElement(bar);
} else {
bar.setAttribute('aria-valuenow', val);
bar.setAttribute('aria-valuemax', max);
transitionProgress(bar.querySelector('.aui-progress-indicator-value'), bar, {
currentProgress: oldVal,
value: valAsFraction
});
}
}
function validValue(val) {
return _.isNumber(val) && _.isFinite(val) && !_.isNaN(val);
}
function parseNumeric(val, defaultVal = 1) {
const num = parseFloat(val);
return validValue(num) ? num : defaultVal;
}
skate('aui-progressbar', {
template(el) {
// Ensure max is set before value upon element creation and before rendering.
// Why is this happening in 'template' and not 'created'? Because it gets called before 'created'!
el._data.max = parseNumeric(el.getAttribute('max'), DEFAULTS.max);
el._data.val = parseNumeric(el.getAttribute('value'), DEFAULTS.val);
el._data.indeterminate = el.hasAttribute('indeterminate');
el._data = recalc(el._data);
const {val, valAsFraction, valAsPercent, max} = el._data;
const legacyValue = el._data.indeterminate ? '' : `data-value="${valAsFraction}"`;
el.innerHTML = `<div class="aui-progress-indicator"
${legacyValue}
role="progressbar"
aria-valuemin="0"
aria-valuenow="${val}"
aria-valuemax="${max}"
tabindex="0"
>
<span class="aui-progress-indicator-value" style="width: ${valAsPercent}%"></span>
</div>`;
},
attached(el) {
refresh(el);
},
attributes: {
indeterminate: {
created: function(el) {
el.indeterminate = true;
},
removed: function(el) {
el.indeterminate = false;
}
},
value(el, data) {
el.value = parseNumeric(data.newValue, data.oldValue);
},
max(el, data) {
el.max = parseNumeric(data.newValue, data.oldValue);
},
},
prototype: {
get _data() {
return this.__data || (this._data = _.defaults({}, DEFAULTS));
},
set _data(d) {
return this.__data = d;
},
get indeterminate() {
return this._data.indeterminate;
},
set indeterminate(val) {
this._data.indeterminate = !!val;
refresh(this);
},
get value() {
return this._data.val;
},
set value(num) {
if (!validValue(num)) return false;
const val = parseDecimal(num, 6);
this._data = recalc(this._data, { val });
refresh(this);
},
get max() {
return this._data.max;
},
set max(num) {
if (!validValue(num)) return false;
const max = parseDecimal(num, 6);
this._data = recalc(this._data, { max });
refresh(this);
},
}
});
const progressBars = {
update: deprecate.fn(updateProgressElement, 'AJS.progressBars.update', {
sinceVersion: '7.7.0',
extraInfo: 'Use the <aui-progressbar> web component instead'
}),
setIndeterminate: deprecate.fn(setIndeterminateOnProgressElement, 'AJS.progressBars.setIndeterminate', {
sinceVersion: '7.7.0',
extraInfo: 'Use the <aui-progressbar> web component instead'
})
};
globalize('progressBars', progressBars);
export default progressBars;
;