UNPKG

@atlassian/aui

Version:

Atlassian User Interface Framework

218 lines (185 loc) 7.24 kB
'use strict'; 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;