1 | import $ from './jquery';
|
2 | import { defaults, isNumber, isFinite, isNaN } from 'underscore';
|
3 | import skate from './internal/skate';
|
4 | import { recomputeStyle } from './internal/animation';
|
5 | import * as deprecate from './internal/deprecation';
|
6 | import globalize from './internal/globalize';
|
7 |
|
8 | const afterTransitionEvent = 'aui-progress-indicator-after-update';
|
9 | const beforeTransitionEvent = 'aui-progress-indicator-before-update';
|
10 | const transitionEnd = 'transitionend webkitTransitionEnd';
|
11 |
|
12 |
|
13 | function updateProgress($progressBar, $progressBarContainer, progressValue) {
|
14 | recomputeStyle($progressBar);
|
15 | $progressBar.css('width', progressValue * 100 + '%');
|
16 | $progressBarContainer.attr('data-value', progressValue);
|
17 | }
|
18 |
|
19 |
|
20 | function updateProgressElement(element, value) {
|
21 | if (typeof element === 'string') {
|
22 | let el = document.getElementById(element);
|
23 | if (el) {
|
24 | element = el;
|
25 | }
|
26 | }
|
27 | var $progressBarContainer = $(element).first();
|
28 | var $progressBar = $progressBarContainer.children('.aui-progress-indicator-value');
|
29 | var valueAttribute = $progressBarContainer.attr('data-value');
|
30 | var isIndeterminate = !valueAttribute;
|
31 | var currentProgress = parseFloat(valueAttribute) || 0;
|
32 | var isProgressNotChanged = valueAttribute && currentProgress === value;
|
33 |
|
34 |
|
35 | if (isProgressNotChanged) {
|
36 | const currentDemonstratedValue = parseFloat($progressBar.get(0).style.width) || 0;
|
37 | isProgressNotChanged = currentProgress === (currentDemonstratedValue * 100);
|
38 | }
|
39 |
|
40 | if (isProgressNotChanged) {
|
41 | return;
|
42 | }
|
43 |
|
44 |
|
45 | if (isIndeterminate) {
|
46 | $progressBar.css('width', 0);
|
47 | }
|
48 |
|
49 | transitionProgress($progressBar, $progressBarContainer, { currentProgress, value });
|
50 |
|
51 | return $progressBarContainer;
|
52 | }
|
53 |
|
54 | function transitionProgress(progressBar, progressBarContainer, { currentProgress, value }) {
|
55 | const $progressBar = $(progressBar);
|
56 | const $progressBarContainer = $(progressBarContainer);
|
57 |
|
58 | if (typeof value === 'number' && value <= 1 && value >= 0) {
|
59 | $progressBarContainer.trigger(beforeTransitionEvent, [currentProgress, value]);
|
60 |
|
61 |
|
62 | $progressBar.one(transitionEnd, function () {
|
63 | $progressBarContainer.trigger(afterTransitionEvent, [currentProgress, value]);
|
64 | });
|
65 | updateProgress($progressBar, $progressBarContainer, value);
|
66 | }
|
67 | }
|
68 |
|
69 | function setIndeterminateOnProgressElement(element) {
|
70 | var $progressBarContainer = $(element).first();
|
71 | var $progressBar = $progressBarContainer.children('.aui-progress-indicator-value');
|
72 |
|
73 | $progressBarContainer.removeAttr('data-value');
|
74 | recomputeStyle($progressBarContainer);
|
75 | $progressBar.css('width', '');
|
76 | }
|
77 |
|
78 | const DEFAULTS = {
|
79 | indeterminate: false,
|
80 | max: 1,
|
81 | val: 0,
|
82 | };
|
83 |
|
84 | function validNumeric(val) {
|
85 | return isNumber(val) && isFinite(val) && !isNaN(val);
|
86 | }
|
87 |
|
88 | function parseNumeric(val, defaultVal = 1) {
|
89 | const num = parseFloat(val);
|
90 | return validNumeric(num) ? num : Number(defaultVal);
|
91 | }
|
92 |
|
93 | function parseDecimal(num, precision = 1) {
|
94 | return Number(parseFloat(num).toFixed(precision));
|
95 | }
|
96 |
|
97 | function safeValue(val, max) {
|
98 | return Math.max(0, Math.min(val, max));
|
99 | }
|
100 |
|
101 | function safeMax(max) {
|
102 | return max > 0 ? max : DEFAULTS.max;
|
103 | }
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 | function calc(data) {
|
110 | const { val, max } = data;
|
111 | const parsedMax = safeMax(max);
|
112 | const parsedVal = safeValue(val, parsedMax);
|
113 | const valAsFraction = parseDecimal(parsedVal / parsedMax, 6);
|
114 | const valAsPercent = parseDecimal(valAsFraction * 100, 2);
|
115 | return {max: parsedMax, val: parsedVal, valAsFraction, valAsPercent};
|
116 | }
|
117 |
|
118 | function refresh(el) {
|
119 | const {val, valAsFraction, max} = calc(el._data);
|
120 | const bar = el.querySelector('.aui-progress-indicator');
|
121 | const oldVal = bar.getAttribute('data-value');
|
122 |
|
123 | if (el.indeterminate) {
|
124 | bar.removeAttribute('aria-valuenow');
|
125 | setIndeterminateOnProgressElement(bar);
|
126 | } else {
|
127 | bar.setAttribute('aria-valuenow', val);
|
128 | bar.setAttribute('aria-valuemax', max);
|
129 | transitionProgress(bar.querySelector('.aui-progress-indicator-value'), bar, {
|
130 | currentProgress: oldVal,
|
131 | value: valAsFraction
|
132 | });
|
133 | }
|
134 | }
|
135 |
|
136 | function setValue(el, data) {
|
137 | el._data.val = parseNumeric(data.newValue, data.oldValue || DEFAULTS.val);
|
138 | refresh(el);
|
139 | }
|
140 |
|
141 | function setMax(el, data) {
|
142 | el._data.max = parseNumeric(data.newValue, data.oldValue || DEFAULTS.max);
|
143 | refresh(el);
|
144 | }
|
145 |
|
146 | const ProgressBarEl = skate('aui-progressbar', {
|
147 | template(el) {
|
148 |
|
149 |
|
150 | el._data.max = parseNumeric(el.getAttribute('max'), DEFAULTS.max);
|
151 | el._data.val = parseNumeric(el.getAttribute('value'), DEFAULTS.val);
|
152 | el._data.indeterminate = el.hasAttribute('indeterminate');
|
153 | const {val, max, valAsFraction, valAsPercent} = calc(el._data);
|
154 |
|
155 | const legacyValue = el._data.indeterminate ? '' : `data-value="${valAsFraction}"`;
|
156 |
|
157 | el.innerHTML = `<div class="aui-progress-indicator"
|
158 | ${legacyValue}
|
159 | role="progressbar"
|
160 | aria-valuemin="0"
|
161 | aria-valuenow="${val}"
|
162 | aria-valuemax="${max}"
|
163 | tabindex="0"
|
164 | >
|
165 | <span class="aui-progress-indicator-value" style="width: ${valAsPercent}%"></span>
|
166 | </div>`;
|
167 | },
|
168 |
|
169 | attached(el) {
|
170 | refresh(el);
|
171 | },
|
172 |
|
173 | attributes: {
|
174 | indeterminate: {
|
175 | created: function(el) {
|
176 | el.indeterminate = true;
|
177 | },
|
178 | removed: function(el) {
|
179 | el.indeterminate = false;
|
180 | }
|
181 | },
|
182 | value: {
|
183 | value: DEFAULTS.val,
|
184 | fallback: function (el, data) {
|
185 | if (el._updating) {return false;}
|
186 | setValue(el, data);
|
187 | }
|
188 | },
|
189 | max: {
|
190 | value: DEFAULTS.max,
|
191 | fallback: function (el, data) {
|
192 | if (el._updating) {return false;}
|
193 | setMax(el, data);
|
194 | }
|
195 | },
|
196 | },
|
197 |
|
198 | prototype: {
|
199 | get _data() {
|
200 | return this.__data || (this._data = defaults({}, DEFAULTS));
|
201 | },
|
202 | set _data(d) {
|
203 | return this.__data = d;
|
204 | },
|
205 | get indeterminate() {
|
206 | return this._data.indeterminate;
|
207 | },
|
208 | set indeterminate(val) {
|
209 | this._data.indeterminate = !!val;
|
210 | refresh(this);
|
211 | },
|
212 | get value() {
|
213 | const { val } = calc(this._data);
|
214 | return val;
|
215 | },
|
216 | set value(num) {
|
217 | if (!validNumeric(num)) {return false;}
|
218 | const data = { newValue: parseDecimal(num, 6) };
|
219 | this._updating = true;
|
220 |
|
221 | this.setAttribute('value', data.newValue);
|
222 | this._updating = false;
|
223 | setValue(this, data);
|
224 | },
|
225 | get max() {
|
226 | const { max } = calc(this._data);
|
227 | return max;
|
228 | },
|
229 | set max(num) {
|
230 | if (!validNumeric(num)) {return false;}
|
231 | const data = { newValue: parseDecimal(num, 6) };
|
232 | this._updating = true;
|
233 |
|
234 | this.setAttribute('max', data.newValue);
|
235 | this._updating = false;
|
236 | setMax(this, data);
|
237 | },
|
238 | }
|
239 | });
|
240 |
|
241 | const progressBars = {
|
242 | update: deprecate.fn(updateProgressElement, 'AJS.progressBars.update', {
|
243 | sinceVersion: '7.7.0',
|
244 | removeInVersion: '10.0.0',
|
245 | extraInfo: 'Use the <aui-progressbar> web component instead'
|
246 | }),
|
247 | setIndeterminate: deprecate.fn(setIndeterminateOnProgressElement, 'AJS.progressBars.setIndeterminate', {
|
248 | sinceVersion: '7.7.0',
|
249 | removeInVersion: '10.0.0',
|
250 | extraInfo: 'Use the <aui-progressbar> web component instead'
|
251 | })
|
252 | };
|
253 |
|
254 | globalize('progressBars', progressBars);
|
255 |
|
256 | export default progressBars;
|
257 | export {
|
258 | ProgressBarEl
|
259 | };
|