UNPKG

react-select-module

Version:
237 lines (199 loc) 5.73 kB
// ============================== // NO OP // ============================== export const noop = () => {}; export const emptyString = () => ''; // ============================== // 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; } } export function classNames(prefix, state, className) { const arr = [className]; if (state && prefix) { for (let key in state) { if (state.hasOwnProperty(key) && state[key]) { arr.push(`${applyPrefixToName(prefix, key)}`); } } } return arr .filter(i => i) .map(i => String(i).trim()) .join(' '); } // ============================== // Clean Value // ============================== export const cleanValue = value => { if (Array.isArray(value)) return value.filter(Boolean); if (typeof value === 'object' && value !== null) return [value]; return []; }; // ============================== // Handle Input Change // ============================== export function handleInputChange(inputValue, actionMeta, onInputChange) { if (onInputChange) { const newValue = onInputChange(inputValue, actionMeta); if (typeof newValue === 'string') return newValue; } return inputValue; } // ============================== // Scroll Helpers // ============================== export function isDocumentElement(el) { return [document.documentElement, document.body, window].indexOf(el) > -1; } // Normalized Scroll Top // ------------------------------ export function normalizedHeight(el) { if (isDocumentElement(el)) { return window.innerHeight; } return el.clientHeight; } // Normalized scrollTo & scrollTop // ------------------------------ export function getScrollTop(el) { if (isDocumentElement(el)) { return window.pageYOffset; } return el.scrollTop; } export 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 // ------------------------------ export function getScrollParent(element) { let style = getComputedStyle(element); const excludeStaticParent = style.position === 'absolute'; const overflowRx = /(auto|scroll)/; const docEl = document.documentElement; // suck it, flow... if (style.position === 'fixed') return docEl; for (let 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; } export function animatedScrollTo(element, to, duration = 200, callback = noop) { const start = getScrollTop(element); const change = to - start; const increment = 10; let currentTime = 0; function animateScroll() { currentTime += increment; const val = easeOutCubic(currentTime, start, change, duration); scrollTo(element, val); if (currentTime < duration) { window.requestAnimationFrame(animateScroll); } else { callback(element); } } animateScroll(); } // Scroll Into View // ------------------------------ export function scrollIntoView(menuEl, focusedEl) { const menuRect = menuEl.getBoundingClientRect(); const focusedRect = focusedEl.getBoundingClientRect(); const 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 export function getBoundingClientObj(element) { const rect = element.getBoundingClientRect(); return { bottom: rect.bottom, height: rect.height, left: rect.left, right: rect.right, top: rect.top, width: rect.width, }; } // ============================== // String to Key (kebabify) // ============================== export function toKey(str) { return str.replace(/\W/g, '-'); } // ============================== // Touch Capability Detector // ============================== export function isTouchCapable() { try { document.createEvent('TouchEvent'); return true; } catch (e) { return false; } } // ============================== // Mobile Device Detector // ============================== export function isMobileDevice() { try { return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( navigator.userAgent ); } catch (e) { return false; } }