@atlassian/aui
Version:
Atlassian User Interface library
170 lines (147 loc) • 4.53 kB
JavaScript
import skate from './internal/skate';
import { getMessageLogger } from './internal/deprecation';
import { defaults, find } from 'underscore';
/**
* @typedef {"small"|"medium"|"large"} SpinnerSize
*/
/**
* @typedef {Object} SpinnerSizeConfig
* @param {SpinnerSize} name
* @param {number} px
* @param {number} radius
*/
/**
* @enum {SpinnerSizeConfig}
* @readonly
*/
const SIZE = {
SMALL: {
name: 'small',
px: 20,
radius: 9,
},
MEDIUM: {
name: 'medium',
px: 30,
radius: 13.5,
},
LARGE: {
name: 'large',
px: 50,
radius: 22.5,
},
};
const DEFAULTS = {
filled: false,
size: SIZE.MEDIUM.name,
};
const filledAttributeReplacementText = `Add CSS to the parent element of the <aui-spinner>.
Use CSS flexbox or grid to vertically align it.
See https://css-tricks.com/centering-css-complete-guide/ for techniques.`;
const filledAttributeDeprecatedLogger = getMessageLogger('<aui-spinner> filled attribute', {
sinceVersion: '7.9.4',
removeInVersion: '10.0.0',
extraInfo: filledAttributeReplacementText
});
const filledPropDeprecatedLogger = getMessageLogger('<aui-spinner> filled property', {
sinceVersion: '7.9.4',
removeInVersion: '10.0.0',
extraInfo: filledAttributeReplacementText
});
/** @deprecated */
function setMiddleTop(element, height) {
const parent = element.parentNode;
// only operate on elements, not documentFragment or comment nodes, etc.
if (parent && parent.nodeType === 1) {
const selfDomRect = element.getBoundingClientRect();
const parentDomRect = parent.getBoundingClientRect();
const parentMiddleTop = parentDomRect.top + parentDomRect.height / 2;
const spinnerMiddleTop = selfDomRect.top + height / 2;
element.querySelector('svg').style.top = `${parentMiddleTop - spinnerMiddleTop}px`;
}
}
/** @deprecated */
function removeMiddleTop(element) {
delete element.querySelector('svg').style.top;
}
function validateSize(size) {
let result = SIZE.MEDIUM.name;
if (typeof size === 'string') {
size = size.toLowerCase();
const possibleSizes = Object.keys(SIZE).map(key => key.toLowerCase());
if (possibleSizes.indexOf(size) > -1) {
result = size;
}
}
return result;
}
function setSize(element, size, radius) {
const svg = element.querySelector('svg');
const circle = element.querySelector('circle');
svg.setAttribute('size', size);
svg.setAttribute('height', size);
svg.setAttribute('width', size);
svg.setAttribute('viewBox', `0 0 ${size} ${size}`);
const circleSize = size / 2;
circle.setAttribute('cx', circleSize);
circle.setAttribute('cy', circleSize);
circle.setAttribute('r', radius);
}
function refresh(element) {
const { px, radius } = find(SIZE, s => s.name === element._data.size) || SIZE.MEDIUM;
setSize(element, px, radius);
if (element._data.filled) {
setMiddleTop(element, px);
} else {
removeMiddleTop(element);
}
}
const SpinnerEl = skate('aui-spinner', {
template(element) {
element.innerHTML = '<div class="aui-spinner spinner"><svg focusable="false"><circle></circle></svg></div>';
refresh(element);
},
attached(element) {
refresh(element);
},
attributes: {
filled: {
/** @deprecated */
created: function(element) {
filledAttributeDeprecatedLogger();
element._data.filled = true;
refresh(element);
},
/** @deprecated */
removed: function(element) {
element._data.filled = false;
refresh(element);
}
},
size(element, data) {
element._data.size = validateSize(data.newValue);
refresh(element);
},
},
prototype: {
get _data() {
return this.__data || (this._data = defaults({}, DEFAULTS));
},
set _data(data) {
return this.__data = data;
},
/** @deprecated */
set filled(isFilled) {
filledPropDeprecatedLogger();
!!isFilled ? this.setAttribute('filled', '') : this.removeAttribute('filled');
},
set size(newSize) {
const size = validateSize(newSize);
this.setAttribute('size', size);
},
},
});
export default SpinnerEl;
export {
SIZE
};