react-select
Version:
A Select control built with and for ReactJS
218 lines (183 loc) • 5.74 kB
JavaScript
;
// ==============================
// NO OP
// ==============================
var noop = function noop() {};
// Class Name Prefixer
// ==============================
/**
String representation of component state for styling with class names.
Expects an array of strings OR a string/object pair:
- className(['comp', 'comp-arg', 'comp-arg-2'])
@returns 'react-select__comp react-select__comp-arg react-select__comp-arg-2'
- className('comp', { some: true, state: false })
@returns 'react-select__comp react-select__comp--some'
*/
function applyPrefixToName(prefix, name) {
if (!name) {
return prefix;
} else if (name[0] === '-') {
return prefix + name;
} else {
return prefix + '__' + name;
}
}
function classNames(prefix, state, className) {
var arr = [className];
if (state && prefix) {
for (var key in state) {
if (state.hasOwnProperty(key) && state[key]) {
arr.push("" + applyPrefixToName(prefix, key));
}
}
}
return arr.filter(function (i) {
return i;
}).map(function (i) {
return String(i).trim();
}).join(' ');
} // ==============================
// Clean Value
// ==============================
var cleanValue = function cleanValue(value) {
if (Array.isArray(value)) return value.filter(Boolean);
if (typeof value === 'object' && value !== null) return [value];
return [];
}; // ==============================
// Handle Input Change
// ==============================
function handleInputChange(inputValue, actionMeta, onInputChange) {
if (onInputChange) {
var newValue = onInputChange(inputValue, actionMeta);
if (typeof newValue === 'string') return newValue;
}
return inputValue;
} // ==============================
// Scroll Helpers
// ==============================
function isDocumentElement(el) {
return [document.documentElement, document.body, window].indexOf(el) > -1;
} // Normalized Scroll Top
// ------------------------------
function getScrollTop(el) {
if (isDocumentElement(el)) {
return window.pageYOffset;
}
return el.scrollTop;
}
function scrollTo(el, top) {
// with a scroll distance, we perform scroll on the element
if (isDocumentElement(el)) {
window.scrollTo(0, top);
return;
}
el.scrollTop = top;
} // Get Scroll Parent
// ------------------------------
function getScrollParent(element) {
var style = getComputedStyle(element);
var excludeStaticParent = style.position === 'absolute';
var overflowRx = /(auto|scroll)/;
var docEl = document.documentElement; // suck it, flow...
if (style.position === 'fixed') return docEl;
for (var parent = element; parent = parent.parentElement;) {
style = getComputedStyle(parent);
if (excludeStaticParent && style.position === 'static') {
continue;
}
if (overflowRx.test(style.overflow + style.overflowY + style.overflowX)) {
return parent;
}
}
return docEl;
} // Animated Scroll To
// ------------------------------
/**
@param t: time (elapsed)
@param b: initial value
@param c: amount of change
@param d: duration
*/
function easeOutCubic(t, b, c, d) {
return c * ((t = t / d - 1) * t * t + 1) + b;
}
function animatedScrollTo(element, to, duration, callback) {
if (duration === void 0) {
duration = 200;
}
if (callback === void 0) {
callback = noop;
}
var start = getScrollTop(element);
var change = to - start;
var increment = 10;
var currentTime = 0;
function animateScroll() {
currentTime += increment;
var val = easeOutCubic(currentTime, start, change, duration);
scrollTo(element, val);
if (currentTime < duration) {
window.requestAnimationFrame(animateScroll);
} else {
callback(element);
}
}
animateScroll();
} // Scroll Into View
// ------------------------------
function scrollIntoView(menuEl, focusedEl) {
var menuRect = menuEl.getBoundingClientRect();
var focusedRect = focusedEl.getBoundingClientRect();
var overScroll = focusedEl.offsetHeight / 3;
if (focusedRect.bottom + overScroll > menuRect.bottom) {
scrollTo(menuEl, Math.min(focusedEl.offsetTop + focusedEl.clientHeight - menuEl.offsetHeight + overScroll, menuEl.scrollHeight));
} else if (focusedRect.top - overScroll < menuRect.top) {
scrollTo(menuEl, Math.max(focusedEl.offsetTop - overScroll, 0));
}
} // ==============================
// Get bounding client object
// ==============================
// cannot get keys using array notation with DOMRect
function getBoundingClientObj(element) {
var rect = element.getBoundingClientRect();
return {
bottom: rect.bottom,
height: rect.height,
left: rect.left,
right: rect.right,
top: rect.top,
width: rect.width
};
}
// Touch Capability Detector
// ==============================
function isTouchCapable() {
try {
document.createEvent('TouchEvent');
return true;
} catch (e) {
return false;
}
} // ==============================
// Mobile Device Detector
// ==============================
function isMobileDevice() {
try {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
} catch (e) {
return false;
}
}
exports.animatedScrollTo = animatedScrollTo;
exports.classNames = classNames;
exports.cleanValue = cleanValue;
exports.getBoundingClientObj = getBoundingClientObj;
exports.getScrollParent = getScrollParent;
exports.getScrollTop = getScrollTop;
exports.handleInputChange = handleInputChange;
exports.isDocumentElement = isDocumentElement;
exports.isMobileDevice = isMobileDevice;
exports.isTouchCapable = isTouchCapable;
exports.noop = noop;
exports.scrollIntoView = scrollIntoView;
exports.scrollTo = scrollTo;