@instructure/ui-utils
Advanced tools
Comparing version 4.0.0-beta.0 to 4.0.0-dev.2
@@ -7,4 +7,11 @@ "use strict"; | ||
exports.default = capitalizeFirstLetter; | ||
function capitalizeFirstLetter(word) { | ||
return word ? word.charAt(0).toUpperCase() + word.slice(1) : word; | ||
/** | ||
* --- | ||
* category: utilities | ||
* --- | ||
* Capitalize the first letter in a string | ||
* @param {String} str | ||
*/ | ||
function capitalizeFirstLetter(str) { | ||
return str ? str.charAt(0).toUpperCase() + str.slice(1) : str; | ||
} |
@@ -6,4 +6,8 @@ 'use strict'; | ||
}); | ||
exports.default = createChainedFunction; | ||
/** | ||
* Safe chained function | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* Safe chained functions | ||
* | ||
@@ -14,3 +18,3 @@ * Will only create a new function if needed, | ||
* Forked from: https://github.com/react-bootstrap/react-overlays/blob/master/src/utils/createChainedFunction.js | ||
* | ||
* @module createChainedFunction | ||
* @param {function} functions to chain | ||
@@ -52,4 +56,2 @@ * @returns {function|null} | ||
exports.default = createChainedFunction; | ||
/** | ||
@@ -62,3 +64,2 @@ * Find all indexes for a value in an Array | ||
*/ | ||
function getAllIndexes(arr, val) { | ||
@@ -65,0 +66,0 @@ var indexes = []; |
@@ -7,2 +7,31 @@ 'use strict'; | ||
exports.default = debounce; | ||
/** | ||
* --- | ||
* category: utilities | ||
* --- | ||
* Creates a debounced function that delays invoking func until after wait milliseconds have elapsed | ||
* since the last time the debounced function was invoked. The debounced function comes with a cancel | ||
* method to cancel delayed func invocations and a flush method to immediately invoke them. Provide options | ||
* to indicate whether func should be invoked on the leading and/or trailing edge of the wait timeout. | ||
* The func is invoked with the last arguments provided to the debounced function. Subsequent calls to the | ||
* debounced function return the result of the last func invocation. | ||
* | ||
* [lodash.debounce](https://github.com/lodash/lodash/blob/master/debounce.js) | ||
* doesn't work well with [sinon fakeTimers](http://sinonjs.org/releases/v1.17.7/fake-timers/) | ||
* so this is forked from the lodash source. | ||
* | ||
* Note: Modified from the original to check for cancelled boolean before invoking func to prevent React setState | ||
* on unmounted components. | ||
* | ||
* @param {Function} func The function to debounce. | ||
* @param {number} [wait=0] The number of milliseconds to delay. | ||
* @param {Object} [options={}] The options object. | ||
* @param {boolean} [options.leading=false] | ||
* Specify invoking on the leading edge of the timeout. | ||
* @param {number} [options.maxWait] | ||
* The maximum time `func` is allowed to be delayed before it's invoked. | ||
* @param {boolean} [options.trailing=true] | ||
* Specify invoking on the trailing edge of the timeout. | ||
* @returns {Function} Returns the new debounced function. | ||
*/ | ||
function debounce(func) { | ||
@@ -12,7 +41,2 @@ var wait = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; | ||
// lodash doesn't work well with sinon fakeTimers so this is pulled from the lodash source: | ||
// https://github.com/lodash/lodash/blob/master/debounce.js | ||
// Note: Modified from the original to check for cancelled boolean before invoking func to prevent React setState | ||
// on unmounted components. | ||
var lastArgs = void 0, | ||
@@ -19,0 +43,0 @@ lastThis = void 0, |
@@ -14,2 +14,14 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* Wrapper function for DOM addEventListener | ||
* @module addEventListener | ||
* @param {DOMNode} el - DOM node which will have the event listener attached | ||
* @param {String} event - a string specifying the event name ('click', 'focus', etc) | ||
* @param {Function} handler - function to run when event occurs | ||
* @param {Boolean} capture - should the event be executed in the capturing or bubbling phase | ||
* @returns {Function} a method to remove the event listener | ||
*/ | ||
function addEventListener(el, event, handler, capture) { | ||
@@ -16,0 +28,0 @@ var node = el === window || el === document ? el : (0, _findDOMNode2.default)(el); |
@@ -22,2 +22,13 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* Adds a listener to an element and calls a specified handler | ||
* function whenever the position changes | ||
* @module | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @param {function} handler - function to run if the position has changed | ||
* @returns {function} remove - cancel the listener and no longer execute the handler function | ||
*/ | ||
function addPositionChangeListener(el, handler) { | ||
@@ -24,0 +35,0 @@ var node = (0, _findDOMNode2.default)(el); |
@@ -19,2 +19,15 @@ 'use strict'; | ||
// TODO: replace with https://wicg.github.io/ResizeObserver/ when it's supported | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* @module | ||
* Adds a listener to an element and calls a specified handler | ||
* function whenever the size changes | ||
* | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @param {function} handler - function to run when resize occurs | ||
* @returns {function} remove - cancel the listener and no longer execute the handler function | ||
*/ | ||
function addResizeListener(el, handler) { | ||
@@ -21,0 +34,0 @@ var node = (0, _findDOMNode2.default)(el); |
@@ -45,2 +45,24 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Calculate the coordinates to attach an element | ||
* to a designated target with specified constraints | ||
* @module | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @param {DomNode} target - the target DOM node | ||
* @param {Object} options - constraints for the positioning | ||
* @param {string} options.placement - designates where the element will be attached | ||
* ('top', 'bottom', 'left', 'right', 'top left' etc.) | ||
* @param {DomNode} options.container - DOM node where the element is contained | ||
* @param {boolean} options.over - whether or not you want the element to position over the target | ||
* @param {string} options.constrain - if the element should be constrained to 'window', | ||
* 'scroll-parent', 'parent', or 'none' | ||
* @param {string|number} options.offsetX - the horizontal offset for the positioned element | ||
* @param {string|number} options.offsetY - the vertical offset for the positioned element | ||
* @returns {Object} object containing style with the calculated position in the 'transform' | ||
* property | ||
*/ | ||
function calculateElementPosition(element, target, options) { | ||
@@ -47,0 +69,0 @@ if (!element || options.placement === 'offscreen') { |
@@ -6,2 +6,11 @@ 'use strict'; | ||
}); | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Performs simple test to determine if DOM can be accessed | ||
* @module | ||
* @returns {boolean} whether the dom can be used | ||
*/ | ||
exports.default = !!(typeof window !== 'undefined' && window.document && window.document.createElement); |
@@ -19,2 +19,13 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Determine if an element contains another DOM node | ||
* | ||
* @param {ReactComponent|DomNode} context - component or DOM node | ||
* @param {ReactComponent|DomNode} el - component or DOM node which we want to determine if contained within the context | ||
* @returns {boolean} if the element is contained within the context | ||
*/ | ||
@@ -21,0 +32,0 @@ function contains(context, el) { |
@@ -18,2 +18,12 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Determine if an element contains the active element | ||
* | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @returns {boolean} if the element contains the active element | ||
*/ | ||
function containsActiveElement(el) { | ||
@@ -20,0 +30,0 @@ var node = el && (0, _findDOMNode2.default)(el); |
@@ -14,2 +14,12 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Wrapper function for React.findDOMNode | ||
* | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @returns {DomNode} The root node of this element | ||
*/ | ||
function findDOMNode(el) { | ||
@@ -16,0 +26,0 @@ if (el === window) { |
@@ -19,2 +19,15 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Given an element, finds and returns all tabbable children. | ||
* Tabbable elements include input, select, textarea, button, and object. | ||
* Anchor tags are also tabbable if they include an href or positive | ||
* tabindex attribute. | ||
* | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @returns {Array} array of all tabbable children | ||
*/ | ||
/** | ||
* Adapted from jQuery UI core | ||
@@ -21,0 +34,0 @@ * |
@@ -37,2 +37,6 @@ 'use strict'; | ||
var _warning = require('../warning'); | ||
var _warning2 = _interopRequireDefault(_warning); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -42,2 +46,12 @@ | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* @module FocusManager | ||
* Class for focus operations. | ||
* - Scoping focus within a given context, | ||
* - Mark active element for focus later | ||
* - Return focus to the marked element | ||
*/ | ||
var FocusManager = function () { | ||
@@ -96,4 +110,3 @@ function FocusManager() { | ||
} catch (e) { | ||
// eslint-disable-next-line | ||
console.warn('\n You tried to return focus to ' + this.focusLaterElement + '\n but it is not in the DOM anymore: ' + e + '\n '); | ||
(0, _warning2.default)(false, '\n You tried to return focus to ' + this.focusLaterElement + '\n but it is not in the DOM anymore: ' + e + '\n '); | ||
} | ||
@@ -106,8 +119,7 @@ this.focusLaterElement = null; | ||
if (this.contextElement) { | ||
// eslint-disable-next-line | ||
console.warn('\n Focus is already scoped to ' + this.contextElement + '.\n '); | ||
(0, _warning2.default)(false, '\n Focus is already scoped to ' + this.contextElement + '.\n '); | ||
return; | ||
} | ||
this.contextElement = (0, _findDOMNode2.default)(el); | ||
var win = (0, _ownerWindow2.default)(this.contextElement); | ||
this.listeners.push((0, _addEventListener2.default)((0, _ownerWindow2.default)(this.contextElement), 'blur', this.handleBlur, false)); | ||
@@ -135,2 +147,2 @@ this.listeners.push((0, _addEventListener2.default)((0, _ownerDocument2.default)(this.contextElement), 'focus', this.handleFocus, true)); | ||
exports.default = new FocusManager(); | ||
exports.default = FocusManager; |
@@ -7,2 +7,13 @@ "use strict"; | ||
exports.default = getActiveElement; | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Get the active element of the specified document | ||
* | ||
* @param {DomNode} doc - document by default or user specified doc | ||
* @throws Will throw an error in ie if no active element | ||
* @return {DomNode} the active element | ||
*/ | ||
function getActiveElement(doc) { | ||
@@ -9,0 +20,0 @@ try { |
@@ -26,2 +26,12 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Gets the bounding rectangle of an element | ||
* | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @return {object} rect - object with top, left coords and height and width | ||
*/ | ||
function getBoundingClientRect(el) { | ||
@@ -28,0 +38,0 @@ var rect = { top: 0, left: 0, height: 0, width: 0 }; |
@@ -16,2 +16,15 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Produces a classList object containing functions | ||
* for both adding and removing classes from an element. | ||
* Also provides a contains function to query if the | ||
* element contains a specified class name. | ||
* | ||
* @param {ReactComponent|DomNode} element - component or DOM node | ||
* @return {Object} object containing classList functions 'contains', 'add', and 'remove' | ||
*/ | ||
function getClassList(element) { | ||
@@ -18,0 +31,0 @@ var node = (0, _findDOMNode2.default)(element); |
@@ -18,2 +18,13 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Get the associated CSS properties and values for a | ||
* specified element | ||
* | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @returns {Object} object containing css properties and values for the element | ||
*/ | ||
function getComputedStyle(el) { | ||
@@ -20,0 +31,0 @@ var style = {}; |
@@ -7,2 +7,12 @@ 'use strict'; | ||
exports.default = getFontSize; | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Gets font size in px | ||
* | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @returns {Object} font size in px | ||
*/ | ||
function getFontSize(el) { | ||
@@ -9,0 +19,0 @@ var m = document.createElement('div'); |
@@ -26,2 +26,14 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Retrieves the offset parents of a specified element. | ||
* Includes parents of nodeType 1 (Element nodes such | ||
* as <p> or <div>) that do not have static position. | ||
* | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @returns {Array} offset parents | ||
*/ | ||
function getOffsetParents(el) { | ||
@@ -28,0 +40,0 @@ var parents = []; |
@@ -22,2 +22,15 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Retrieves the scroll parents of a specified element. | ||
* Includes parents of nodeType 1 (Element nodes such | ||
* as <p> or <div>) that have overflow css properties | ||
* set to auto, scroll, or overlay | ||
* | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @returns {Array} scroll parents | ||
*/ | ||
function getScrollParents(el) { | ||
@@ -24,0 +37,0 @@ var parents = []; |
@@ -15,2 +15,6 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Simple implementation of mouseEnter and mouseLeave. | ||
@@ -22,11 +26,11 @@ * React's built version is broken: https://github.com/facebook/react/issues/4251 | ||
* @param handler {function} Callback function for handling the event | ||
* @param e {Event} The DOM Event that was fired | ||
* @param event {Event} The DOM Event that was fired | ||
*/ | ||
function handleMouseOverOut(handler, e) { | ||
var target = e.currentTarget; | ||
var related = e.relatedTarget || e.nativeEvent.toElement; | ||
function handleMouseOverOut(handler, event) { | ||
var target = event.currentTarget; | ||
var related = event.relatedTarget || event.nativeEvent.toElement; | ||
if (!related || related !== target && !(0, _contains2.default)(target, related)) { | ||
handler(e); | ||
handler(event); | ||
} | ||
} |
@@ -6,3 +6,3 @@ 'use strict'; | ||
}); | ||
exports.transformSelection = exports.scopeTab = exports.requestAnimationFrame = exports.ownerWindow = exports.ownerDocument = exports.isActiveElement = exports.handleMouseOverOut = exports.getScrollParents = exports.getOffsetParents = exports.getFontSize = exports.getComputedStyle = exports.getClassList = exports.getBoundingClientRect = exports.getActiveElement = exports.focusManager = exports.findTabbable = exports.findDOMNode = exports.containsActiveElement = exports.contains = exports.canUseDOM = exports.calculateElementPosition = exports.addResizeListener = exports.addPositionChangeListener = exports.addEventListener = undefined; | ||
exports.transformSelection = exports.scopeTab = exports.requestAnimationFrame = exports.ownerWindow = exports.ownerDocument = exports.isActiveElement = exports.handleMouseOverOut = exports.getScrollParents = exports.getOffsetParents = exports.getFontSize = exports.getComputedStyle = exports.getClassList = exports.getBoundingClientRect = exports.getActiveElement = exports.FocusManager = exports.findTabbable = exports.findDOMNode = exports.containsActiveElement = exports.contains = exports.canUseDOM = exports.calculateElementPosition = exports.addResizeListener = exports.addPositionChangeListener = exports.addEventListener = undefined; | ||
@@ -45,5 +45,5 @@ var _addEventListener2 = require('./addEventListener'); | ||
var _focusManager2 = require('./focusManager'); | ||
var _focusManager = require('./focusManager'); | ||
var _focusManager3 = _interopRequireDefault(_focusManager2); | ||
var _focusManager2 = _interopRequireDefault(_focusManager); | ||
@@ -118,3 +118,3 @@ var _getActiveElement2 = require('./getActiveElement'); | ||
exports.findTabbable = _findTabbable3.default; | ||
exports.focusManager = _focusManager3.default; | ||
exports.FocusManager = _focusManager2.default; | ||
exports.getActiveElement = _getActiveElement3.default; | ||
@@ -121,0 +121,0 @@ exports.getBoundingClientRect = _getBoundingClientRect3.default; |
@@ -18,2 +18,12 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Determine if an element is the active element | ||
* | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @returns {boolean} if the element is the active element | ||
*/ | ||
function isActiveElement(el) { | ||
@@ -20,0 +30,0 @@ var node = el && (0, _findDOMNode2.default)(el); |
@@ -14,2 +14,12 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Retrieve the owner document of a specified element | ||
* | ||
* @param {ReactElement|DOMNode} el | ||
* @returns {DomNode} the owner document | ||
*/ | ||
function ownerDocument(el) { | ||
@@ -16,0 +26,0 @@ var node = el && (0, _findDOMNode2.default)(el); |
@@ -13,2 +13,13 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* If DOM is usable, returns a function wrapper for | ||
* window.requestAnimationFrame. Otherwise sets | ||
* a manual timeout. | ||
* | ||
* @returns {function} requestAnimationFrame takes a callback function as an argument and returns a cancel method | ||
*/ | ||
exports.default = function () { | ||
@@ -15,0 +26,0 @@ var requestAnimationFrame = void 0; |
@@ -30,2 +30,13 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Scope tab in order to trap focus within a specified | ||
* element. | ||
* | ||
* @param {ReactElement|DOMNode} el | ||
* @param {Event} event the DOM Event that was fired | ||
*/ | ||
function scopeTab(element, event) { | ||
@@ -32,0 +43,0 @@ var node = (0, _findDOMNode2.default)(element); |
@@ -6,12 +6,38 @@ 'use strict'; | ||
}); | ||
exports.default = transformSelection; | ||
exports.transformCursor = transformCursor; | ||
exports.default = transformSelection; | ||
/** | ||
* transformCursor - Calculate the resulting cursor position | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* transformSelection - Calculate the resulting text selection | ||
* of a changing text-containing HTML element | ||
* @module transformSelection | ||
* @param {DomNode} element - HTML element with selection capabilities | ||
* @param {string} cleanedValue - new value that will be given to the HTML element | ||
* @return {Object} resulting selection values | ||
*/ | ||
function transformSelection(element, cleanedValue) { | ||
var selectionStart = element.selectionStart, | ||
selectionEnd = element.selectionEnd, | ||
selectionDirection = element.selectionDirection, | ||
value = element.value; | ||
return { | ||
selectionStart: transformCursor(selectionStart, value, cleanedValue), | ||
selectionEnd: transformCursor(selectionEnd, value, cleanedValue), | ||
selectionDirection: selectionDirection | ||
}; | ||
} | ||
/** | ||
* Calculate the resulting cursor position | ||
* within a string when some characters are removed | ||
* | ||
* @param {number} cursorIndex original cursor index | ||
* @param {string} dirtyValue original string | ||
* @param {string} cleanedValue original string with some characters removed | ||
* @return {number} resulting cursor index | ||
* @param {number} cursorIndex - original cursor index | ||
* @param {string} dirtyValue - original string | ||
* @param {string} cleanedValue - original string with some characters removed | ||
* @returns {number} resulting cursor index | ||
*/ | ||
@@ -35,24 +61,2 @@ function transformCursor(cursorIndex, dirtyValue, cleanedValue) { | ||
}, 0); | ||
} | ||
/** | ||
* transformSelection - Calculate the resulting text selection | ||
* of a changing text-containing HTML element | ||
* | ||
* @param {DomNode} element HTML element with selection capabilities | ||
* @param {string} cleanedValue new value that will be given to the HTML element | ||
* @return {Object} resulting selection values | ||
*/ | ||
function transformSelection(element, cleanedValue) { | ||
var selectionStart = element.selectionStart, | ||
selectionEnd = element.selectionEnd, | ||
selectionDirection = element.selectionDirection, | ||
value = element.value; | ||
return { | ||
selectionStart: transformCursor(selectionStart, value, cleanedValue), | ||
selectionEnd: transformCursor(selectionEnd, value, cleanedValue), | ||
selectionDirection: selectionDirection | ||
}; | ||
} |
@@ -8,2 +8,3 @@ 'use strict'; | ||
exports.parse = parse; | ||
exports.isValid = isValid; | ||
exports.browserTimeZone = browserTimeZone; | ||
@@ -19,6 +20,16 @@ | ||
function checkParams(locale, timezone) { | ||
if (locale == null) throw Error('locale must be specified'); | ||
if (timezone == null) throw Error('timezone must be specified'); | ||
} // eslint-disable-line import/no-extraneous-dependencies | ||
/** | ||
* --- | ||
* category: utilities/i18n | ||
* --- | ||
* A wrapper for [moment](https://momentjs.com/) utils. | ||
* @module DateTime | ||
*/ | ||
/** | ||
* Return the current localized date + time with timezone | ||
* @param {String} locale | ||
* @param {String} timezone | ||
* @returns {String} ISO 8601 string | ||
*/ | ||
function now(locale, timezone) { | ||
@@ -29,2 +40,10 @@ checkParams(locale, timezone); | ||
/** | ||
* Parses a string into a localized ISO 8601 string with timezone | ||
* @param {String} dateString | ||
* @param {String} locale | ||
* @param {String} timezone | ||
* @returns {String} ISO 8601 string | ||
*/ | ||
// eslint-disable-line import/no-extraneous-dependencies | ||
function parse(dateString, locale, timezone) { | ||
@@ -35,2 +54,16 @@ checkParams(locale, timezone); | ||
/** | ||
* Determines if a string is a valid ISO 8601 string | ||
* @param {String} dateString | ||
* @returns {Boolean} true if dateString is a valid ISO 8601 string | ||
*/ | ||
function isValid(dateString) { | ||
return (0, _moment2.default)(dateString, [_moment2.default.ISO_8601]).isValid(); | ||
} | ||
/** | ||
* Get the users's time zone (or guess) | ||
* see https://momentjs.com/timezone/docs/#/using-timezones/guessing-user-timezone/ | ||
* @returns {String} a time zone identifier (see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | ||
*/ | ||
function browserTimeZone() { | ||
@@ -43,3 +76,10 @@ return _moment2.default.tz.guess(); | ||
parse: parse, | ||
browserTimeZone: browserTimeZone | ||
}; | ||
browserTimeZone: browserTimeZone, | ||
isValid: isValid | ||
}; | ||
function checkParams(locale, timezone) { | ||
if (locale == null) throw Error('locale must be specified'); | ||
if (timezone == null) throw Error('timezone must be specified'); | ||
} |
@@ -6,11 +6,22 @@ 'use strict'; | ||
}); | ||
function browserLocale() { | ||
var nav = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : navigator; | ||
/** | ||
* --- | ||
* category: utilities/i18n | ||
* --- | ||
* Localization utilities | ||
* @module Locale | ||
*/ | ||
exports.default = { | ||
/** | ||
* Return the locale from the browser | ||
* @returns {String} locale (defaults to 'en') | ||
*/ | ||
browserLocale: function browserLocale() { | ||
var nav = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : navigator; | ||
if (typeof nav !== 'undefined') { | ||
return nav.language; | ||
if (typeof nav !== 'undefined') { | ||
return nav.language; | ||
} | ||
return 'en'; | ||
} | ||
return 'en'; | ||
} | ||
exports.default = { browserLocale: browserLocale }; | ||
}; |
@@ -13,2 +13,12 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities | ||
* --- | ||
* Deep merge N objects into a single result object. | ||
* Merging creates a new object, so that none of the arguments are modified. | ||
* | ||
* @param {Object} arguments objects to merge | ||
* @returns {Object} a new object with items from all arguments | ||
*/ | ||
function mergeDeep() { | ||
@@ -15,0 +25,0 @@ var args = [].concat(Array.prototype.slice.call(arguments)); |
@@ -41,33 +41,37 @@ 'use strict'; | ||
/** | ||
* Abstract component identifier. Helpful for picking out a specific child. | ||
* | ||
* Example: | ||
* | ||
* class App extends Component { | ||
* render () { | ||
* const title = pick(Title, this.props.children) | ||
* const content = pick(Content, this.props.children) | ||
* | ||
* return ( | ||
* <div> | ||
* {title} | ||
* <ContextBox> | ||
* {content} | ||
* </ContextBox> | ||
* </div> | ||
* ) | ||
* } | ||
* } | ||
* | ||
* class Title extends ComponentIdentifier { static displayName = "Title" } | ||
* class Content extends ComponentIdentifier { static displayName = "Content" } | ||
* | ||
* ReactDOM.render( | ||
* <App> | ||
* <Title><h2>Hello World!</h2></Title> | ||
* <Content><div>This text gets decorated within `App`.</div></Content> | ||
* </App>, | ||
* document.getElementById('container') | ||
* ) | ||
*/ | ||
--- | ||
category: utilities/react | ||
--- | ||
Abstract component identifier. Helpful for picking out a specific child. | ||
```js | ||
class App extends Component { | ||
render () { | ||
const title = pick(Title, this.props.children) | ||
const content = pick(Content, this.props.children) | ||
return ( | ||
<div> | ||
{title} | ||
<ContextBox> | ||
{content} | ||
</ContextBox> | ||
</div> | ||
) | ||
} | ||
} | ||
class Title extends ComponentIdentifier { static displayName = "Title" } | ||
class Content extends ComponentIdentifier { static displayName = "Content" } | ||
ReactDOM.render( | ||
<App> | ||
<Title><h2>Hello World!</h2></Title> | ||
<Content><div>This text gets decorated within `App`.</div></Content> | ||
</App>, | ||
document.getElementById('container') | ||
) | ||
``` | ||
**/ | ||
var ComponentIdentifier = (_temp = _class = function (_Component) { | ||
@@ -107,2 +111,6 @@ _inherits(ComponentIdentifier, _Component); | ||
/** | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* | ||
* Pick a specific child component from a component's children | ||
@@ -109,0 +117,0 @@ * |
@@ -43,2 +43,20 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* A decorator or higher order component to provide the ability to style a | ||
* React component with container queries. | ||
* | ||
* The containerQuery HOC provides a `size` getter so that you can alter the behavior | ||
* of the component based on the size of its container. | ||
* | ||
* The `size` will be updated whenever the dimensions of the container change. | ||
* | ||
* So that CSS rules can be applied based on the dimensions of the container, | ||
* custom data attributes are added to the container DOM element. | ||
* | ||
* @param {Object} query | ||
* @returns {Function} a function that creates an element with containerQuery behavior | ||
*/ | ||
function containerQuery(query) { | ||
@@ -147,2 +165,7 @@ var getSelectorMap = function getSelectorMap(el) { | ||
} | ||
}, { | ||
key: 'size', | ||
get: function get() { | ||
this._size; | ||
} | ||
}]); | ||
@@ -149,0 +172,0 @@ |
@@ -7,4 +7,11 @@ 'use strict'; | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; /** | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* Custom prop types for React components. | ||
* @module CustomPropTypes | ||
*/ | ||
var _react = require('react'); | ||
@@ -26,2 +33,6 @@ | ||
var _warning = require('../warning'); | ||
var _warning2 = _interopRequireDefault(_warning); | ||
var _canUseDOM = require('../dom/canUseDOM'); | ||
@@ -36,6 +47,6 @@ | ||
/** | ||
* | ||
* Validate that the children of a component is one of the specified types. | ||
* | ||
* Example: | ||
* | ||
* ```js | ||
* class Example extends Component { | ||
@@ -50,11 +61,15 @@ * static propTypes = { | ||
* } | ||
* ``` | ||
* | ||
* This will allow children such as: | ||
* | ||
* ```jsx | ||
* <Example> | ||
* <Foo /> | ||
* </Example> | ||
* ``` | ||
* | ||
* - OR - | ||
* OR | ||
* | ||
* ```jsx | ||
* <Example> | ||
@@ -64,5 +79,7 @@ * <Bar /> | ||
* </Example> | ||
* ``` | ||
* | ||
* But will fail on something like: | ||
* | ||
* ```jsx | ||
* <Example> | ||
@@ -72,4 +89,4 @@ * <h1>Example</h1> | ||
* </Example> | ||
* | ||
* @returns Error if validation failed | ||
* ``` | ||
* @returns {Error} if validation failed | ||
*/ | ||
@@ -95,6 +112,6 @@ oneOf: function oneOf(validTypes) { | ||
/** | ||
* | ||
* Validate the type and order of children for a component. | ||
* | ||
* Example: | ||
* | ||
* ```js | ||
* class Example extends Component { | ||
@@ -109,5 +126,7 @@ * static propTypes = { | ||
* } | ||
* ``` | ||
* | ||
* This will enforce the following: | ||
* | ||
* ```jsx | ||
* <Example> | ||
@@ -118,7 +137,7 @@ * <Foo /> | ||
* </Example> | ||
* ``` | ||
* | ||
* This validator will also allow various permutations of the order. | ||
* | ||
* Example: | ||
* | ||
* ```js | ||
* class Example extends Component { | ||
@@ -137,5 +156,7 @@ * static propTypes = { | ||
* } | ||
* ``` | ||
* | ||
* This will enforce one of the following: | ||
* | ||
* ```jsx | ||
* <Example> | ||
@@ -146,5 +167,7 @@ * <Foo /> | ||
* </Example> | ||
* ``` | ||
* | ||
* - OR - | ||
* OR | ||
* | ||
* ```jsx | ||
* <Example> | ||
@@ -154,5 +177,7 @@ * <Foo /> | ||
* </Example> | ||
* ``` | ||
* | ||
* - OR - | ||
* OR | ||
* | ||
* ```jsx | ||
* <Example> | ||
@@ -162,5 +187,6 @@ * <Bar /> | ||
* </Example> | ||
* ``` | ||
* | ||
* @param {...Array} validTypeGroups One or more Arrays of valid types | ||
* @returns Error if validation failed | ||
* @returns {Error} if validation failed | ||
*/ | ||
@@ -220,2 +246,24 @@ enforceOrder: function enforceOrder() { | ||
/** | ||
* Ensure that a corresponding handler function is provided for the given prop if the | ||
* component does not manage its own state. | ||
* | ||
* ```js | ||
* class Foo extends Component { | ||
* static propTypes = { | ||
* selected: CustomPropTypes.controllable(PropTypes.bool, 'onSelect', 'defaultSelected'), | ||
* onSelect: PropTypes.func, | ||
* defaultSelected: PropTypes.bool | ||
* } | ||
* ... | ||
* ``` | ||
* | ||
* This will throw an error if the 'selected' prop is supplied without a corresponding | ||
* 'onSelect' handler and will recommend using 'defaultSelected' instead. | ||
* | ||
* @param {function} propType - validates the prop type. Returns null if valid, error otherwise | ||
* @param {string} handlerName - name of the handler function | ||
* @param {string} defaultPropName - name of the default prop | ||
* @returns {Error} if designated prop is supplied without a corresponding handler function | ||
*/ | ||
controllable: function controllable(propType) { | ||
@@ -236,2 +284,14 @@ var handlerName = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'onChange'; | ||
}, | ||
/** | ||
* Verify that the given prop is a valid React element. | ||
* | ||
* @param {Object} props - object containing the component props | ||
* @param {string} propName - name of the given prop | ||
* @param {string} componentName - name of the component | ||
* @param {string} location | ||
* @param {string} propFullName | ||
* @returns {Error} if prop is an invalid react element | ||
*/ | ||
elementType: function elementType(props, propName, componentName, location, propFullName) { | ||
@@ -257,2 +317,37 @@ if (props[propName] === undefined) { | ||
/** | ||
* | ||
* Validate spacing prop constraining it to the following enumerated values | ||
* | ||
* - '0' | ||
* - 'none' | ||
* - 'auto' | ||
* - 'xxx-small' | ||
* - 'xx-small' | ||
* - 'x-small' | ||
* - 'small' | ||
* - 'medium' | ||
* - 'large' | ||
* - 'x-large' | ||
* - 'xx-large' | ||
* | ||
* Valid inputs include a single value or a string with CSS | ||
* shorthand like syntax with a maximum of 4 values. | ||
* | ||
* Examples of valid inputs: | ||
* 'x-small' (single value) | ||
* 'small large' (CSS shorthand) | ||
* '0 small large x-large' (CSS shorthand max 4 values) | ||
* | ||
* Examples of invalid inputs: | ||
* '5px' (must be a string from the enumerated values) | ||
* '0 large small 0 0' (invalid shorthand syntax w/5 values) | ||
* | ||
* @param {Object} props - object containing the component props | ||
* @param {string} propName - name of the given prop | ||
* @param {string} componentName - name of the component | ||
* @param {string} location | ||
* @param {string} propFullName | ||
* @returns {Error} if is not one of the enumerated values or the shorthand syntax is incorrect | ||
*/ | ||
spacing: function spacing(props, propName, componentName, location) { | ||
@@ -286,2 +381,15 @@ var validValues = ['0', 'none', 'auto', 'xxx-small', 'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large']; | ||
}, | ||
/** | ||
* | ||
* Verify that the given prop is a correctly formatted ISO 8601 formatted string. | ||
* | ||
* @param {Object} props - object containing the component props | ||
* @param {string} propName - name of the given prop | ||
* @param {string} componentName - name of the component | ||
* @param {string} location | ||
* @param {string} propFullName | ||
* @returns {Error} if prop is an invalid ISO 8601 string | ||
*/ | ||
iso8601: function iso8601(props, propName, componentName, location) { | ||
@@ -301,8 +409,15 @@ var propValue = props[propName]; | ||
}, | ||
/** | ||
* | ||
* Trigger a console warning if the specified prop variant is deprecated | ||
* | ||
* @param {function} propType - validates the prop type. Returns null if valid, error otherwise | ||
* @param {string} deprecated - name of the deprecated variant | ||
* @param {string} message - additional information to display with the warning | ||
*/ | ||
deprecatedVariant: function deprecatedVariant(propType, deprecated, message) { | ||
return function (props, propName, componentName) { | ||
if (props[propName] === deprecated) { | ||
// eslint-disable-next-line | ||
console.warn('`' + componentName + '` `' + deprecated + '` variant is deprecated. ' + (message || '')); | ||
} | ||
(0, _warning2.default)(props[propName] !== deprecated, '`' + componentName + '` `' + deprecated + '` variant is deprecated. ' + (message || '')); | ||
}; | ||
@@ -309,0 +424,0 @@ }, |
@@ -30,4 +30,25 @@ 'use strict'; | ||
/** | ||
* Deprecate a prop for a Component | ||
*/ | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* Deprecate React component props. Warnings will display in the console when deprecated | ||
* props are used. | ||
* | ||
* ```js | ||
* class Example extends Component { | ||
* static propTypes = { | ||
* currentProp: PropTypes.func | ||
* } | ||
* } | ||
* export default deprecated('3.0.0', { | ||
* deprecatedProp: 'currentProp', | ||
* nowNonExistentProp: true | ||
* })(Example) | ||
* ``` | ||
* | ||
* @module deprecated | ||
* @param {string} version | ||
* @param {object} oldProps | ||
* @return {function} React component with deprecated props behavior | ||
*/ | ||
function deprecated(version, oldProps) { | ||
@@ -34,0 +55,0 @@ return function (ComposedComponent) { |
@@ -18,2 +18,16 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* | ||
* Ensure a single child. If it is a child of length 1, return a | ||
* cloned instance of the child. If it is a child of length > 1, | ||
* wrap in a span and return the child. Return null if child has | ||
* no length. | ||
* | ||
* @param {ReactElement} child | ||
* @param {Object} props - props for child | ||
* @returns {ReactElement} cloned instance for a single child, or children wrapped in a span | ||
*/ | ||
function ensureSingleChild(child) { | ||
@@ -20,0 +34,0 @@ var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; |
@@ -14,2 +14,14 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* Get the displayName of a React component. | ||
* | ||
* For [themeable](#themeable) components defined as ES6 classes, the displayName can | ||
* be added via [babel plugin](#babel-plugin-transform-class-display-name). | ||
* | ||
* @param {ReactComponent|String} Component | ||
* @returns {String} the component displayName | ||
*/ | ||
function getDisplayName(Component) { | ||
@@ -16,0 +28,0 @@ (0, _warning2.default)(typeof Component === 'string' || typeof Component.displayName !== 'undefined', '%s is missing the property "displayName".', Component.name); |
@@ -14,2 +14,13 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* Check if a React component instance (React element) matches one of the | ||
* specified types. | ||
* | ||
* @param {ReactComponent} componentInstance | ||
* @param {Array} types an array of React components | ||
* @returns {Boolean} true if the component matches at least one of the types | ||
*/ | ||
function matchComponentTypes(componentInstance) { | ||
@@ -16,0 +27,0 @@ var types = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; |
@@ -19,2 +19,18 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* @module passthroughProps | ||
*/ | ||
/** | ||
* Return a props object with the specified propTypes omitted. | ||
* Automatically excludes ('theme', 'children', 'className', 'style') | ||
* | ||
* @param {Object} props React component props | ||
* @param {Object} propTypes React component propTypes | ||
* @param {Array} exclude an optional array of prop names to exclude | ||
* @returns {Object} props object without the excluded props | ||
*/ | ||
function omitProps(props) { | ||
@@ -29,2 +45,10 @@ var propTypes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
/** | ||
* Return a props object with only specified propTypes. | ||
* | ||
* @param {Object} props React component props | ||
* @param {Object} propTypes React component propTypes | ||
* @param {Array} include an optional array of prop names to include | ||
* @returns {Object} props object with only the included props | ||
*/ | ||
function pickProps(props) { | ||
@@ -31,0 +55,0 @@ var propTypes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; |
@@ -22,2 +22,12 @@ 'use strict'; | ||
/** | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* Clone a React element without overwriting refs. | ||
* @module safeCloneElement | ||
* @param {ReactElement} element | ||
* @param {object} props | ||
* @return {ReactElement} | ||
*/ | ||
function safeCloneElement(element, props) { | ||
@@ -24,0 +34,0 @@ var cloneRef = props.ref; |
@@ -11,2 +11,3 @@ 'use strict'; | ||
exports.default = windowMessageListener; | ||
exports.origin = origin; | ||
@@ -30,21 +31,16 @@ | ||
function origin(node) { | ||
var ownWindow = (0, _ownerWindow2.default)(node); | ||
var location = ownWindow.location; | ||
// see https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage | ||
if (location.protocol === 'file:') { | ||
return '*'; | ||
} else if (location.origin) { | ||
return location.origin; | ||
} else if (location.port) { | ||
return location.protocol + '//' + location.hostname + ':' + location.port; | ||
} else { | ||
return location.protocol + '//' + location.hostname; | ||
} | ||
} | ||
var windowMessageListener = function windowMessageListener(messageHandler, validSource) { | ||
/** | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* A decorator or higher order component that provides methods | ||
* for cross-origin communication (between iframes/windows). | ||
* | ||
* see https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage | ||
* @module windowMessageListener | ||
* @param {Function} messageHandler a handler for messages recieved by the component | ||
* @param {Function} validSource an optional function that would restrict message handling to a specified source. | ||
* @returns {Function} a function that decorates a React component with the behavior | ||
*/ | ||
function windowMessageListener(messageHandler, validSource) { | ||
return function (ComposedComponent) { | ||
@@ -112,10 +108,31 @@ var _class, _temp2; | ||
return _class; | ||
}(ComposedComponent), _class.displayName = (0, _getDisplayName2.default)(ComposedComponent), _temp2; | ||
}(ComposedComponent), _class.displayName = (0, _getDisplayName2.default)(ComposedComponent), _class.postMessage = function (target, message, origin) { | ||
target.postMessage(message, origin); | ||
}, _temp2; | ||
}; | ||
}; | ||
} | ||
windowMessageListener.postMessage = function (target, message, origin) { | ||
target.postMessage(message, origin); | ||
}; | ||
/** | ||
* Return the origin of the owner window of the DOM element | ||
* | ||
* see https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage | ||
* | ||
* @param {DOMElement} node | ||
* @returns {String} the origin | ||
*/ | ||
function origin(node) { | ||
var ownWindow = (0, _ownerWindow2.default)(node); | ||
exports.default = windowMessageListener; | ||
var location = ownWindow.location; | ||
if (location.protocol === 'file:') { | ||
return '*'; | ||
} else if (location.origin) { | ||
return location.origin; | ||
} else if (location.port) { | ||
return location.protocol + '//' + location.hostname + ':' + location.port; | ||
} else { | ||
return location.protocol + '//' + location.hostname; | ||
} | ||
} |
@@ -11,26 +11,16 @@ 'use strict'; | ||
// https://github.com/facebook/fbjs/blob/master/packages/fbjs/src/core/shallowEqual.js | ||
var hasOwnProperty = Object.prototype.hasOwnProperty; | ||
/** | ||
* inlined Object.is polyfill to avoid requiring consumers ship their own | ||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is | ||
*/ | ||
function is(x, y) { | ||
// SameValue algorithm | ||
if (x === y) { | ||
// Steps 1-5, 7-10 | ||
// Steps 6.b-6.e: +0 != -0 | ||
return x !== 0 || y !== 0 || 1 / x === 1 / y; | ||
} else { | ||
// Step 6.a: NaN == NaN | ||
return x !== x && y !== y; // eslint-disable-line no-self-compare | ||
} | ||
} | ||
/** | ||
* --- | ||
* category: utilities | ||
* --- | ||
* @module shallowEqual | ||
* Performs equality by iterating through keys on an object and returning false | ||
* when any key has values which are not strictly equal between the arguments. | ||
* Returns true when the values of all keys are strictly equal. | ||
*/ | ||
* | ||
* @param {Object} objA | ||
* @param {Object} objB | ||
* @returns {Boolean} Returns true when the values of all keys are strictly equal | ||
*/ | ||
function shallowEqual(objA, objB) { | ||
@@ -60,2 +50,18 @@ if (is(objA, objB)) { | ||
return true; | ||
} | ||
/* | ||
* inlined Object.is polyfill to avoid requiring consumers ship their own | ||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is | ||
*/ | ||
function is(x, y) { | ||
// SameValue algorithm | ||
if (x === y) { | ||
// Steps 1-5, 7-10 | ||
// Steps 6.b-6.e: +0 != -0 | ||
return x !== 0 || y !== 0 || 1 / x === 1 / y; | ||
} else { | ||
// Step 6.a: NaN == NaN | ||
return x !== x && y !== y; // eslint-disable-line no-self-compare | ||
} | ||
} |
@@ -7,2 +7,9 @@ 'use strict'; | ||
exports.default = warning; | ||
/** | ||
* --- | ||
* category: utilities | ||
* --- | ||
* @param {Boolean} condition a condition that we expect to be true | ||
* @param {String} message a message to display as a console warning in DEV env when condition is false | ||
*/ | ||
function warning(condition, message) { | ||
@@ -14,4 +21,4 @@ if (!condition && process.env.NODE_ENV !== 'production' && typeof console !== 'undefined') { | ||
console.error.apply(undefined, ['Warning: ' + message].concat(args)); | ||
console.warn.apply(undefined, ['Warning: ' + message].concat(args)); | ||
} | ||
} |
{ | ||
"name": "@instructure/ui-utils", | ||
"version": "4.0.0-beta.0", | ||
"version": "4.0.0-dev.2", | ||
"description": "A collection of utilities for UI components", | ||
"author": "Instructure, Inc. Engineering and Product Design", | ||
"main": "./lib/index.js", | ||
"module": "./es/index.js", | ||
"repository": { | ||
@@ -14,16 +15,13 @@ "type": "git", | ||
"scripts": { | ||
"test": "npm-run-all test:browser test:node", | ||
"test:browser": "cross-env NODE_ENV=test COVERAGE=1 karma start --single-run --coverage", | ||
"test:node": "node lib/index.js", | ||
"test:watch": "cross-env NODE_ENV=test DEBUG=1 karma start", | ||
"lint": "eslint src", | ||
"lint:fix": "eslint src --fix", | ||
"clean": "rimraf lib .babel-cache", | ||
"build": "cross-env NODE_ENV=production babel src --out-dir lib --ignore *.test.js", | ||
"build:dev": "cross-env NODE_ENV=development babel src --out-dir lib --ignore *.test.js" | ||
"test": "ui-test", | ||
"test:watch": "ui-test --watch", | ||
"lint": "ui-test --lint", | ||
"lint:fix": "ui-test --lint --fix", | ||
"clean": "ui-build --clean", | ||
"build": "ui-build", | ||
"build:watch": "ui-build --watch" | ||
}, | ||
"license": "MIT", | ||
"devDependencies": { | ||
"@instructure/ui-presets": "^4.0.0-beta.0", | ||
"babel-cli": "6.26.0", | ||
"@instructure/ui-presets": "^4.0.0-dev.2", | ||
"react": "15.6.1", | ||
@@ -38,6 +36,6 @@ "react-dom": "15.6.1" | ||
"moment": "^2.10.6", | ||
"moment-timezone": "^0.5.13", | ||
"moment-timezone": "^0.5.14", | ||
"no-scroll": "^2.1.0", | ||
"numeral": "^2.0.6", | ||
"object.omit": "^2.0.1", | ||
"object.omit": "^3.0.0", | ||
"object.pick": "^1.2.0", | ||
@@ -44,0 +42,0 @@ "prop-types": "^15.5.10", |
@@ -0,7 +1,11 @@ | ||
--- | ||
category: packages | ||
--- | ||
## @instructure/ui-utils | ||
[npm]: https://img.shields.io/npm/v/@instructure/ui-utils.svg | ||
[npm-url]: https://npmjs.com/package/@instructure/ui-utils | ||
[![npm][npm]][npm-url] | ||
[![build-status][build-status]][build-status-url] | ||
[![MIT License][license-badge]][LICENSE] | ||
[![Code of Conduct][coc-badge]][coc] | ||
@@ -11,3 +15,16 @@ ### Installation | ||
```sh | ||
npm install @instructure/ui-utils | ||
yarn add @instructure/ui-utils | ||
``` | ||
[npm]: https://img.shields.io/npm/v/@instructure/ui-utils.svg | ||
[npm-url]: https://npmjs.com/package/@instructure/ui-utils | ||
[build-status]: https://travis-ci.org/instructure/instructure-ui.svg?branch=master | ||
[build-status-url]: https://travis-ci.org/instructure/instructure-ui "Travis CI" | ||
[license-badge]: https://img.shields.io/npm/l/instructure-ui.svg?style=flat-square | ||
[license]: https://github.com/instructure/instructure-ui/blob/master/LICENSE | ||
[coc-badge]: https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square | ||
[coc]: https://github.com/instructure/instructure-ui/blob/master/CODE_OF_CONDUCT.md |
@@ -1,3 +0,10 @@ | ||
export default function capitalizeFirstLetter (word) { | ||
return word ? word.charAt(0).toUpperCase() + word.slice(1) : word | ||
/** | ||
* --- | ||
* category: utilities | ||
* --- | ||
* Capitalize the first letter in a string | ||
* @param {String} str | ||
*/ | ||
export default function capitalizeFirstLetter (str) { | ||
return str ? str.charAt(0).toUpperCase() + str.slice(1) : str | ||
} |
/** | ||
* Safe chained function | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* Safe chained functions | ||
* | ||
@@ -8,7 +11,7 @@ * Will only create a new function if needed, | ||
* Forked from: https://github.com/react-bootstrap/react-overlays/blob/master/src/utils/createChainedFunction.js | ||
* | ||
* @module createChainedFunction | ||
* @param {function} functions to chain | ||
* @returns {function|null} | ||
*/ | ||
function createChainedFunction (...funcs) { | ||
export default function createChainedFunction (...funcs) { | ||
return funcs | ||
@@ -40,4 +43,2 @@ .filter((f, i) => { | ||
export default createChainedFunction | ||
/** | ||
@@ -44,0 +45,0 @@ * Find all indexes for a value in an Array |
@@ -0,7 +1,31 @@ | ||
/** | ||
* --- | ||
* category: utilities | ||
* --- | ||
* Creates a debounced function that delays invoking func until after wait milliseconds have elapsed | ||
* since the last time the debounced function was invoked. The debounced function comes with a cancel | ||
* method to cancel delayed func invocations and a flush method to immediately invoke them. Provide options | ||
* to indicate whether func should be invoked on the leading and/or trailing edge of the wait timeout. | ||
* The func is invoked with the last arguments provided to the debounced function. Subsequent calls to the | ||
* debounced function return the result of the last func invocation. | ||
* | ||
* [lodash.debounce](https://github.com/lodash/lodash/blob/master/debounce.js) | ||
* doesn't work well with [sinon fakeTimers](http://sinonjs.org/releases/v1.17.7/fake-timers/) | ||
* so this is forked from the lodash source. | ||
* | ||
* Note: Modified from the original to check for cancelled boolean before invoking func to prevent React setState | ||
* on unmounted components. | ||
* | ||
* @param {Function} func The function to debounce. | ||
* @param {number} [wait=0] The number of milliseconds to delay. | ||
* @param {Object} [options={}] The options object. | ||
* @param {boolean} [options.leading=false] | ||
* Specify invoking on the leading edge of the timeout. | ||
* @param {number} [options.maxWait] | ||
* The maximum time `func` is allowed to be delayed before it's invoked. | ||
* @param {boolean} [options.trailing=true] | ||
* Specify invoking on the trailing edge of the timeout. | ||
* @returns {Function} Returns the new debounced function. | ||
*/ | ||
export default function debounce (func, wait = 0, options = {}) { | ||
// lodash doesn't work well with sinon fakeTimers so this is pulled from the lodash source: | ||
// https://github.com/lodash/lodash/blob/master/debounce.js | ||
// Note: Modified from the original to check for cancelled boolean before invoking func to prevent React setState | ||
// on unmounted components. | ||
let lastArgs, lastThis, result, lastCallTime | ||
@@ -8,0 +32,0 @@ let lastInvokeTime = 0 |
import findDOMNode from './findDOMNode' | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* Wrapper function for DOM addEventListener | ||
* @module addEventListener | ||
* @param {DOMNode} el - DOM node which will have the event listener attached | ||
* @param {String} event - a string specifying the event name ('click', 'focus', etc) | ||
* @param {Function} handler - function to run when event occurs | ||
* @param {Boolean} capture - should the event be executed in the capturing or bubbling phase | ||
* @returns {Function} a method to remove the event listener | ||
*/ | ||
export default function addEventListener (el, event, handler, capture) { | ||
@@ -4,0 +16,0 @@ const node = (el === window || el === document) ? el : findDOMNode(el) |
@@ -5,2 +5,13 @@ import findDOMNode from './findDOMNode' | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* Adds a listener to an element and calls a specified handler | ||
* function whenever the position changes | ||
* @module | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @param {function} handler - function to run if the position has changed | ||
* @returns {function} remove - cancel the listener and no longer execute the handler function | ||
*/ | ||
export default function addPositionChangeListener (el, handler) { | ||
@@ -7,0 +18,0 @@ const node = findDOMNode(el) |
@@ -5,2 +5,15 @@ import findDOMNode from './findDOMNode' | ||
// TODO: replace with https://wicg.github.io/ResizeObserver/ when it's supported | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* @module | ||
* Adds a listener to an element and calls a specified handler | ||
* function whenever the size changes | ||
* | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @param {function} handler - function to run when resize occurs | ||
* @returns {function} remove - cancel the listener and no longer execute the handler function | ||
*/ | ||
export default function addResizeListener (el, handler) { | ||
@@ -7,0 +20,0 @@ const node = findDOMNode(el) |
@@ -9,2 +9,24 @@ import getBoundingClientRect from './getBoundingClientRect' | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Calculate the coordinates to attach an element | ||
* to a designated target with specified constraints | ||
* @module | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @param {DomNode} target - the target DOM node | ||
* @param {Object} options - constraints for the positioning | ||
* @param {string} options.placement - designates where the element will be attached | ||
* ('top', 'bottom', 'left', 'right', 'top left' etc.) | ||
* @param {DomNode} options.container - DOM node where the element is contained | ||
* @param {boolean} options.over - whether or not you want the element to position over the target | ||
* @param {string} options.constrain - if the element should be constrained to 'window', | ||
* 'scroll-parent', 'parent', or 'none' | ||
* @param {string|number} options.offsetX - the horizontal offset for the positioned element | ||
* @param {string|number} options.offsetY - the vertical offset for the positioned element | ||
* @returns {Object} object containing style with the calculated position in the 'transform' | ||
* property | ||
*/ | ||
export default function calculateElementPosition (element, target, options) { | ||
@@ -11,0 +33,0 @@ if (!element || options.placement === 'offscreen') { |
@@ -0,1 +1,10 @@ | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Performs simple test to determine if DOM can be accessed | ||
* @module | ||
* @returns {boolean} whether the dom can be used | ||
*/ | ||
export default !!( | ||
@@ -2,0 +11,0 @@ typeof window !== 'undefined' && |
@@ -6,2 +6,13 @@ import findDOMNode from './findDOMNode' | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Determine if an element contains another DOM node | ||
* | ||
* @param {ReactComponent|DomNode} context - component or DOM node | ||
* @param {ReactComponent|DomNode} el - component or DOM node which we want to determine if contained within the context | ||
* @returns {boolean} if the element is contained within the context | ||
*/ | ||
function contains (context, el) { | ||
@@ -8,0 +19,0 @@ const container = findDOMNode(context) |
import findDOMNode from './findDOMNode' | ||
import getActiveElement from './getActiveElement' | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Determine if an element contains the active element | ||
* | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @returns {boolean} if the element contains the active element | ||
*/ | ||
export default function containsActiveElement (el) { | ||
@@ -5,0 +15,0 @@ const node = el && findDOMNode(el) |
import ReactDOM from 'react-dom' | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Wrapper function for React.findDOMNode | ||
* | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @returns {DomNode} The root node of this element | ||
*/ | ||
export default function findDOMNode (el) { | ||
@@ -4,0 +14,0 @@ if (el === window) { |
@@ -16,2 +16,15 @@ /** | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Given an element, finds and returns all tabbable children. | ||
* Tabbable elements include input, select, textarea, button, and object. | ||
* Anchor tags are also tabbable if they include an href or positive | ||
* tabindex attribute. | ||
* | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @returns {Array} array of all tabbable children | ||
*/ | ||
export default function findTabbable (el) { | ||
@@ -18,0 +31,0 @@ const element = findDOMNode(el) |
@@ -8,3 +8,14 @@ import findDOMNode from './findDOMNode' | ||
import containsActiveElement from './containsActiveElement' | ||
import warning from '../warning' | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* @module FocusManager | ||
* Class for focus operations. | ||
* - Scoping focus within a given context, | ||
* - Mark active element for focus later | ||
* - Return focus to the marked element | ||
*/ | ||
class FocusManager { | ||
@@ -56,4 +67,4 @@ contextElement = null | ||
} catch (e) { | ||
// eslint-disable-next-line | ||
console.warn( | ||
warning( | ||
false, | ||
` | ||
@@ -70,4 +81,4 @@ You tried to return focus to ${this.focusLaterElement} | ||
if (this.contextElement) { | ||
// eslint-disable-next-line | ||
console.warn( | ||
warning( | ||
false, | ||
` | ||
@@ -80,3 +91,3 @@ Focus is already scoped to ${this.contextElement}. | ||
this.contextElement = findDOMNode(el) | ||
const win = ownerWindow(this.contextElement) | ||
this.listeners.push(addEventListener(ownerWindow(this.contextElement), 'blur', this.handleBlur, false)) | ||
@@ -100,2 +111,2 @@ this.listeners.push(addEventListener(ownerDocument(this.contextElement), 'focus', this.handleFocus, true)) | ||
export default new FocusManager() | ||
export default FocusManager |
@@ -0,1 +1,12 @@ | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Get the active element of the specified document | ||
* | ||
* @param {DomNode} doc - document by default or user specified doc | ||
* @throws Will throw an error in ie if no active element | ||
* @return {DomNode} the active element | ||
*/ | ||
export default function getActiveElement (doc) { | ||
@@ -2,0 +13,0 @@ try { |
@@ -6,2 +6,12 @@ import findDOMNode from './findDOMNode' | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Gets the bounding rectangle of an element | ||
* | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @return {object} rect - object with top, left coords and height and width | ||
*/ | ||
export default function getBoundingClientRect (el) { | ||
@@ -8,0 +18,0 @@ const rect = { top: 0, left: 0, height: 0, width: 0 } |
@@ -5,2 +5,15 @@ import findDOMNode from './findDOMNode' | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Produces a classList object containing functions | ||
* for both adding and removing classes from an element. | ||
* Also provides a contains function to query if the | ||
* element contains a specified class name. | ||
* | ||
* @param {ReactComponent|DomNode} element - component or DOM node | ||
* @return {Object} object containing classList functions 'contains', 'add', and 'remove' | ||
*/ | ||
export default function getClassList (element) { | ||
@@ -7,0 +20,0 @@ const node = findDOMNode(element) |
import findDOMNode from './findDOMNode' | ||
import canUseDOM from './canUseDOM' | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Get the associated CSS properties and values for a | ||
* specified element | ||
* | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @returns {Object} object containing css properties and values for the element | ||
*/ | ||
export default function getComputedStyle (el) { | ||
@@ -5,0 +16,0 @@ let style = {} |
@@ -0,1 +1,11 @@ | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Gets font size in px | ||
* | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @returns {Object} font size in px | ||
*/ | ||
export default function getFontSize (el) { | ||
@@ -2,0 +12,0 @@ const m = document.createElement('div') |
@@ -6,2 +6,14 @@ import findDOMNode from './findDOMNode' | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Retrieves the offset parents of a specified element. | ||
* Includes parents of nodeType 1 (Element nodes such | ||
* as <p> or <div>) that do not have static position. | ||
* | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @returns {Array} offset parents | ||
*/ | ||
export default function getOffsetParents (el) { | ||
@@ -8,0 +20,0 @@ const parents = [] |
@@ -5,2 +5,15 @@ import findDOMNode from './findDOMNode' | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Retrieves the scroll parents of a specified element. | ||
* Includes parents of nodeType 1 (Element nodes such | ||
* as <p> or <div>) that have overflow css properties | ||
* set to auto, scroll, or overlay | ||
* | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @returns {Array} scroll parents | ||
*/ | ||
export default function getScrollParents (el) { | ||
@@ -7,0 +20,0 @@ const parents = [] |
import contains from './contains' | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Simple implementation of mouseEnter and mouseLeave. | ||
@@ -10,11 +14,11 @@ * React's built version is broken: https://github.com/facebook/react/issues/4251 | ||
* @param handler {function} Callback function for handling the event | ||
* @param e {Event} The DOM Event that was fired | ||
* @param event {Event} The DOM Event that was fired | ||
*/ | ||
export default function handleMouseOverOut (handler, e) { | ||
const target = e.currentTarget | ||
const related = e.relatedTarget || e.nativeEvent.toElement | ||
export default function handleMouseOverOut (handler, event) { | ||
const target = event.currentTarget | ||
const related = event.relatedTarget || event.nativeEvent.toElement | ||
if (!related || (related !== target && !contains(target, related))) { | ||
handler(e) | ||
handler(event) | ||
} | ||
} |
@@ -11,3 +11,3 @@ /* list utils in alphabetical order */ | ||
export findTabbable from './findTabbable' | ||
export focusManager from './focusManager' | ||
export FocusManager from './focusManager' | ||
export getActiveElement from './getActiveElement' | ||
@@ -14,0 +14,0 @@ export getBoundingClientRect from './getBoundingClientRect' |
import findDOMNode from './findDOMNode' | ||
import getActiveElement from './getActiveElement' | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Determine if an element is the active element | ||
* | ||
* @param {ReactComponent|DomNode} el - component or DOM node | ||
* @returns {boolean} if the element is the active element | ||
*/ | ||
export default function isActiveElement (el) { | ||
@@ -5,0 +15,0 @@ const node = el && findDOMNode(el) |
import findDOMNode from './findDOMNode' | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Retrieve the owner document of a specified element | ||
* | ||
* @param {ReactElement|DOMNode} el | ||
* @returns {DomNode} the owner document | ||
*/ | ||
export default function ownerDocument (el) { | ||
@@ -4,0 +14,0 @@ const node = el && findDOMNode(el) |
import findDOMNode from './findDOMNode' | ||
import ownerDocument from './ownerDocument' | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* Retrieve the owner window object associated with | ||
* the owner document of the specified element | ||
* @param {ReactElement|DOMNode} el | ||
* @returns {Object} the owner window | ||
*/ | ||
export default function (el) { | ||
@@ -5,0 +14,0 @@ const node = el && findDOMNode(el) |
import canUseDOM from './canUseDOM' | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* If DOM is usable, returns a function wrapper for | ||
* window.requestAnimationFrame. Otherwise sets | ||
* a manual timeout. | ||
* | ||
* @returns {function} requestAnimationFrame takes a callback function as an argument and returns a cancel method | ||
*/ | ||
export default (function () { | ||
@@ -4,0 +15,0 @@ let requestAnimationFrame |
@@ -7,2 +7,13 @@ import findDOMNode from './findDOMNode' | ||
/** | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* Scope tab in order to trap focus within a specified | ||
* element. | ||
* | ||
* @param {ReactElement|DOMNode} el | ||
* @param {Event} event the DOM Event that was fired | ||
*/ | ||
export default function scopeTab (element, event) { | ||
@@ -9,0 +20,0 @@ const node = findDOMNode(element) |
/** | ||
* transformCursor - Calculate the resulting cursor position | ||
* --- | ||
* category: utilities/DOM | ||
* --- | ||
* | ||
* transformSelection - Calculate the resulting text selection | ||
* of a changing text-containing HTML element | ||
* @module transformSelection | ||
* @param {DomNode} element - HTML element with selection capabilities | ||
* @param {string} cleanedValue - new value that will be given to the HTML element | ||
* @return {Object} resulting selection values | ||
*/ | ||
export default function transformSelection (element, cleanedValue) { | ||
const { | ||
selectionStart, | ||
selectionEnd, | ||
selectionDirection, | ||
value | ||
} = element | ||
return { | ||
selectionStart: transformCursor(selectionStart, value, cleanedValue), | ||
selectionEnd: transformCursor(selectionEnd, value, cleanedValue), | ||
selectionDirection | ||
} | ||
} | ||
/** | ||
* Calculate the resulting cursor position | ||
* within a string when some characters are removed | ||
* | ||
* @param {number} cursorIndex original cursor index | ||
* @param {string} dirtyValue original string | ||
* @param {string} cleanedValue original string with some characters removed | ||
* @return {number} resulting cursor index | ||
* @param {number} cursorIndex - original cursor index | ||
* @param {string} dirtyValue - original string | ||
* @param {string} cleanedValue - original string with some characters removed | ||
* @returns {number} resulting cursor index | ||
*/ | ||
@@ -31,24 +58,1 @@ export function transformCursor (cursorIndex, dirtyValue, cleanedValue) { | ||
} | ||
/** | ||
* transformSelection - Calculate the resulting text selection | ||
* of a changing text-containing HTML element | ||
* | ||
* @param {DomNode} element HTML element with selection capabilities | ||
* @param {string} cleanedValue new value that will be given to the HTML element | ||
* @return {Object} resulting selection values | ||
*/ | ||
export default function transformSelection (element, cleanedValue) { | ||
const { | ||
selectionStart, | ||
selectionEnd, | ||
selectionDirection, | ||
value | ||
} = element | ||
return { | ||
selectionStart: transformCursor(selectionStart, value, cleanedValue), | ||
selectionEnd: transformCursor(selectionEnd, value, cleanedValue), | ||
selectionDirection | ||
} | ||
} |
import moment from 'moment' // eslint-disable-line import/no-extraneous-dependencies | ||
import 'moment-timezone/builds/moment-timezone-with-data' | ||
function checkParams (locale, timezone) { | ||
if (locale == null) throw Error('locale must be specified') | ||
if (timezone == null) throw Error('timezone must be specified') | ||
} | ||
/** | ||
* --- | ||
* category: utilities/i18n | ||
* --- | ||
* A wrapper for [moment](https://momentjs.com/) utils. | ||
* @module DateTime | ||
*/ | ||
/** | ||
* Return the current localized date + time with timezone | ||
* @param {String} locale | ||
* @param {String} timezone | ||
* @returns {String} ISO 8601 string | ||
*/ | ||
export function now (locale, timezone) { | ||
@@ -14,2 +23,9 @@ checkParams(locale, timezone) | ||
/** | ||
* Parses a string into a localized ISO 8601 string with timezone | ||
* @param {String} dateString | ||
* @param {String} locale | ||
* @param {String} timezone | ||
* @returns {String} ISO 8601 string | ||
*/ | ||
export function parse (dateString, locale, timezone) { | ||
@@ -20,2 +36,16 @@ checkParams(locale, timezone) | ||
/** | ||
* Determines if a string is a valid ISO 8601 string | ||
* @param {String} dateString | ||
* @returns {Boolean} true if dateString is a valid ISO 8601 string | ||
*/ | ||
export function isValid (dateString) { | ||
return moment(dateString, [moment.ISO_8601]).isValid() | ||
} | ||
/** | ||
* Get the users's time zone (or guess) | ||
* see https://momentjs.com/timezone/docs/#/using-timezones/guessing-user-timezone/ | ||
* @returns {String} a time zone identifier (see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | ||
*/ | ||
export function browserTimeZone () { | ||
@@ -28,3 +58,9 @@ return moment.tz.guess() | ||
parse, | ||
browserTimeZone | ||
browserTimeZone, | ||
isValid | ||
} | ||
function checkParams (locale, timezone) { | ||
if (locale == null) throw Error('locale must be specified') | ||
if (timezone == null) throw Error('timezone must be specified') | ||
} |
@@ -1,8 +0,19 @@ | ||
function browserLocale (nav = navigator) { | ||
if (typeof nav !== 'undefined') { | ||
return nav.language | ||
/** | ||
* --- | ||
* category: utilities/i18n | ||
* --- | ||
* Localization utilities | ||
* @module Locale | ||
*/ | ||
export default { | ||
/** | ||
* Return the locale from the browser | ||
* @returns {String} locale (defaults to 'en') | ||
*/ | ||
browserLocale (nav = navigator) { | ||
if (typeof nav !== 'undefined') { | ||
return nav.language | ||
} | ||
return 'en' | ||
} | ||
return 'en' | ||
} | ||
export default { browserLocale } |
@@ -0,1 +1,11 @@ | ||
/** | ||
* --- | ||
* category: utilities | ||
* --- | ||
* Deep merge N objects into a single result object. | ||
* Merging creates a new object, so that none of the arguments are modified. | ||
* | ||
* @param {Object} arguments objects to merge | ||
* @returns {Object} a new object with items from all arguments | ||
*/ | ||
export default function mergeDeep () { | ||
@@ -2,0 +12,0 @@ const args = [...arguments] |
@@ -6,34 +6,39 @@ import React, { Component } from 'react' | ||
/** | ||
* Abstract component identifier. Helpful for picking out a specific child. | ||
* | ||
* Example: | ||
* | ||
* class App extends Component { | ||
* render () { | ||
* const title = pick(Title, this.props.children) | ||
* const content = pick(Content, this.props.children) | ||
* | ||
* return ( | ||
* <div> | ||
* {title} | ||
* <ContextBox> | ||
* {content} | ||
* </ContextBox> | ||
* </div> | ||
* ) | ||
* } | ||
* } | ||
* | ||
* class Title extends ComponentIdentifier { static displayName = "Title" } | ||
* class Content extends ComponentIdentifier { static displayName = "Content" } | ||
* | ||
* ReactDOM.render( | ||
* <App> | ||
* <Title><h2>Hello World!</h2></Title> | ||
* <Content><div>This text gets decorated within `App`.</div></Content> | ||
* </App>, | ||
* document.getElementById('container') | ||
* ) | ||
*/ | ||
--- | ||
category: utilities/react | ||
--- | ||
Abstract component identifier. Helpful for picking out a specific child. | ||
```js | ||
class App extends Component { | ||
render () { | ||
const title = pick(Title, this.props.children) | ||
const content = pick(Content, this.props.children) | ||
return ( | ||
<div> | ||
{title} | ||
<ContextBox> | ||
{content} | ||
</ContextBox> | ||
</div> | ||
) | ||
} | ||
} | ||
class Title extends ComponentIdentifier { static displayName = "Title" } | ||
class Content extends ComponentIdentifier { static displayName = "Content" } | ||
ReactDOM.render( | ||
<App> | ||
<Title><h2>Hello World!</h2></Title> | ||
<Content><div>This text gets decorated within `App`.</div></Content> | ||
</App>, | ||
document.getElementById('container') | ||
) | ||
``` | ||
**/ | ||
export default class ComponentIdentifier extends Component { | ||
@@ -60,2 +65,6 @@ /* eslint-disable react/require-default-props */ | ||
/** | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* | ||
* Pick a specific child component from a component's children | ||
@@ -62,0 +71,0 @@ * |
@@ -7,2 +7,20 @@ import getDisplayName from './getDisplayName' | ||
/** | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* A decorator or higher order component to provide the ability to style a | ||
* React component with container queries. | ||
* | ||
* The containerQuery HOC provides a `size` getter so that you can alter the behavior | ||
* of the component based on the size of its container. | ||
* | ||
* The `size` will be updated whenever the dimensions of the container change. | ||
* | ||
* So that CSS rules can be applied based on the dimensions of the container, | ||
* custom data attributes are added to the container DOM element. | ||
* | ||
* @param {Object} query | ||
* @returns {Function} a function that creates an element with containerQuery behavior | ||
*/ | ||
export default function containerQuery (query) { | ||
@@ -69,2 +87,6 @@ const getSelectorMap = function (el) { | ||
} | ||
get size () { | ||
this._size | ||
} | ||
} | ||
@@ -71,0 +93,0 @@ } |
@@ -0,1 +1,8 @@ | ||
/** | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* Custom prop types for React components. | ||
* @module CustomPropTypes | ||
*/ | ||
import React from 'react' | ||
@@ -5,2 +12,3 @@ import PropTypes from 'prop-types' | ||
import getDisplayName from './getDisplayName' | ||
import warning from '../warning' | ||
@@ -12,6 +20,6 @@ import canUseDOM from '../dom/canUseDOM' | ||
/** | ||
* | ||
* Validate that the children of a component is one of the specified types. | ||
* | ||
* Example: | ||
* | ||
* ```js | ||
* class Example extends Component { | ||
@@ -26,11 +34,15 @@ * static propTypes = { | ||
* } | ||
* ``` | ||
* | ||
* This will allow children such as: | ||
* | ||
* ```jsx | ||
* <Example> | ||
* <Foo /> | ||
* </Example> | ||
* ``` | ||
* | ||
* - OR - | ||
* OR | ||
* | ||
* ```jsx | ||
* <Example> | ||
@@ -40,5 +52,7 @@ * <Bar /> | ||
* </Example> | ||
* ``` | ||
* | ||
* But will fail on something like: | ||
* | ||
* ```jsx | ||
* <Example> | ||
@@ -48,4 +62,4 @@ * <h1>Example</h1> | ||
* </Example> | ||
* | ||
* @returns Error if validation failed | ||
* ``` | ||
* @returns {Error} if validation failed | ||
*/ | ||
@@ -70,6 +84,6 @@ oneOf (validTypes) { | ||
/** | ||
* | ||
* Validate the type and order of children for a component. | ||
* | ||
* Example: | ||
* | ||
* ```js | ||
* class Example extends Component { | ||
@@ -84,5 +98,7 @@ * static propTypes = { | ||
* } | ||
* ``` | ||
* | ||
* This will enforce the following: | ||
* | ||
* ```jsx | ||
* <Example> | ||
@@ -93,7 +109,7 @@ * <Foo /> | ||
* </Example> | ||
* ``` | ||
* | ||
* This validator will also allow various permutations of the order. | ||
* | ||
* Example: | ||
* | ||
* ```js | ||
* class Example extends Component { | ||
@@ -112,5 +128,7 @@ * static propTypes = { | ||
* } | ||
* ``` | ||
* | ||
* This will enforce one of the following: | ||
* | ||
* ```jsx | ||
* <Example> | ||
@@ -121,5 +139,7 @@ * <Foo /> | ||
* </Example> | ||
* ``` | ||
* | ||
* - OR - | ||
* OR | ||
* | ||
* ```jsx | ||
* <Example> | ||
@@ -129,5 +149,7 @@ * <Foo /> | ||
* </Example> | ||
* ``` | ||
* | ||
* - OR - | ||
* OR | ||
* | ||
* ```jsx | ||
* <Example> | ||
@@ -137,5 +159,6 @@ * <Bar /> | ||
* </Example> | ||
* ``` | ||
* | ||
* @param {...Array} validTypeGroups One or more Arrays of valid types | ||
* @returns Error if validation failed | ||
* @returns {Error} if validation failed | ||
*/ | ||
@@ -186,2 +209,24 @@ enforceOrder (...validTypeGroups) { | ||
/** | ||
* Ensure that a corresponding handler function is provided for the given prop if the | ||
* component does not manage its own state. | ||
* | ||
* ```js | ||
* class Foo extends Component { | ||
* static propTypes = { | ||
* selected: CustomPropTypes.controllable(PropTypes.bool, 'onSelect', 'defaultSelected'), | ||
* onSelect: PropTypes.func, | ||
* defaultSelected: PropTypes.bool | ||
* } | ||
* ... | ||
* ``` | ||
* | ||
* This will throw an error if the 'selected' prop is supplied without a corresponding | ||
* 'onSelect' handler and will recommend using 'defaultSelected' instead. | ||
* | ||
* @param {function} propType - validates the prop type. Returns null if valid, error otherwise | ||
* @param {string} handlerName - name of the handler function | ||
* @param {string} defaultPropName - name of the default prop | ||
* @returns {Error} if designated prop is supplied without a corresponding handler function | ||
*/ | ||
controllable (propType, handlerName = 'onChange', defaultPropName = 'defaultValue') { | ||
@@ -207,2 +252,12 @@ return function (props, propName, componentName) { | ||
/** | ||
* Verify that the given prop is a valid React element. | ||
* | ||
* @param {Object} props - object containing the component props | ||
* @param {string} propName - name of the given prop | ||
* @param {string} componentName - name of the component | ||
* @param {string} location | ||
* @param {string} propFullName | ||
* @returns {Error} if prop is an invalid react element | ||
*/ | ||
elementType (props, propName, componentName, location, propFullName) { | ||
@@ -233,2 +288,37 @@ if (props[propName] === undefined) { | ||
/** | ||
* | ||
* Validate spacing prop constraining it to the following enumerated values | ||
* | ||
* - '0' | ||
* - 'none' | ||
* - 'auto' | ||
* - 'xxx-small' | ||
* - 'xx-small' | ||
* - 'x-small' | ||
* - 'small' | ||
* - 'medium' | ||
* - 'large' | ||
* - 'x-large' | ||
* - 'xx-large' | ||
* | ||
* Valid inputs include a single value or a string with CSS | ||
* shorthand like syntax with a maximum of 4 values. | ||
* | ||
* Examples of valid inputs: | ||
* 'x-small' (single value) | ||
* 'small large' (CSS shorthand) | ||
* '0 small large x-large' (CSS shorthand max 4 values) | ||
* | ||
* Examples of invalid inputs: | ||
* '5px' (must be a string from the enumerated values) | ||
* '0 large small 0 0' (invalid shorthand syntax w/5 values) | ||
* | ||
* @param {Object} props - object containing the component props | ||
* @param {string} propName - name of the given prop | ||
* @param {string} componentName - name of the component | ||
* @param {string} location | ||
* @param {string} propFullName | ||
* @returns {Error} if is not one of the enumerated values or the shorthand syntax is incorrect | ||
*/ | ||
spacing (props, propName, componentName, location) { | ||
@@ -284,2 +374,13 @@ const validValues = [ | ||
/** | ||
* | ||
* Verify that the given prop is a correctly formatted ISO 8601 formatted string. | ||
* | ||
* @param {Object} props - object containing the component props | ||
* @param {string} propName - name of the given prop | ||
* @param {string} componentName - name of the component | ||
* @param {string} location | ||
* @param {string} propFullName | ||
* @returns {Error} if prop is an invalid ISO 8601 string | ||
*/ | ||
iso8601 (props, propName, componentName, location) { | ||
@@ -306,8 +407,16 @@ const propValue = props[propName] | ||
/** | ||
* | ||
* Trigger a console warning if the specified prop variant is deprecated | ||
* | ||
* @param {function} propType - validates the prop type. Returns null if valid, error otherwise | ||
* @param {string} deprecated - name of the deprecated variant | ||
* @param {string} message - additional information to display with the warning | ||
*/ | ||
deprecatedVariant (propType, deprecated, message) { | ||
return (props, propName, componentName) => { | ||
if (props[propName] === deprecated) { | ||
// eslint-disable-next-line | ||
console.warn(`\`${componentName}\` \`${deprecated}\` variant is deprecated. ${message || ''}`) | ||
} | ||
warning( | ||
(props[propName] !== deprecated), | ||
`\`${componentName}\` \`${deprecated}\` variant is deprecated. ${message || ''}` | ||
) | ||
} | ||
@@ -314,0 +423,0 @@ }, |
@@ -5,4 +5,25 @@ import getDisplayName from './getDisplayName' | ||
/** | ||
* Deprecate a prop for a Component | ||
*/ | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* Deprecate React component props. Warnings will display in the console when deprecated | ||
* props are used. | ||
* | ||
* ```js | ||
* class Example extends Component { | ||
* static propTypes = { | ||
* currentProp: PropTypes.func | ||
* } | ||
* } | ||
* export default deprecated('3.0.0', { | ||
* deprecatedProp: 'currentProp', | ||
* nowNonExistentProp: true | ||
* })(Example) | ||
* ``` | ||
* | ||
* @module deprecated | ||
* @param {string} version | ||
* @param {object} oldProps | ||
* @return {function} React component with deprecated props behavior | ||
*/ | ||
export default function deprecated (version, oldProps) { | ||
@@ -9,0 +30,0 @@ return function (ComposedComponent) { |
import React, { Children } from 'react' | ||
import safeCloneElement from './safeCloneElement' | ||
/** | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* | ||
* Ensure a single child. If it is a child of length 1, return a | ||
* cloned instance of the child. If it is a child of length > 1, | ||
* wrap in a span and return the child. Return null if child has | ||
* no length. | ||
* | ||
* @param {ReactElement} child | ||
* @param {Object} props - props for child | ||
* @returns {ReactElement} cloned instance for a single child, or children wrapped in a span | ||
*/ | ||
export default function ensureSingleChild (child, props = {}) { | ||
@@ -5,0 +19,0 @@ const childCount = Children.count(child) |
import warning from '../warning' | ||
/** | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* Get the displayName of a React component. | ||
* | ||
* For [themeable](#themeable) components defined as ES6 classes, the displayName can | ||
* be added via [babel plugin](#babel-plugin-transform-class-display-name). | ||
* | ||
* @param {ReactComponent|String} Component | ||
* @returns {String} the component displayName | ||
*/ | ||
export default function getDisplayName (Component) { | ||
@@ -4,0 +16,0 @@ warning( |
@@ -0,1 +1,13 @@ | ||
/** | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* Get the React element type for a component. | ||
* | ||
* @module getElementType | ||
* @param {ReactComponent} Component | ||
* @param {Object} props | ||
* @param {Function} getDefault an optional function that returns the default element type | ||
* @returns {String} the element type | ||
*/ | ||
export default function (Component, props, getDefault) { | ||
@@ -2,0 +14,0 @@ if (props.as && props.as !== Component.defaultProps.as) { |
import getDisplayName from './getDisplayName' | ||
/** | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* Check if a React component instance (React element) matches one of the | ||
* specified types. | ||
* | ||
* @param {ReactComponent} componentInstance | ||
* @param {Array} types an array of React components | ||
* @returns {Boolean} true if the component matches at least one of the types | ||
*/ | ||
export default function matchComponentTypes (componentInstance, types = []) { | ||
@@ -4,0 +15,0 @@ if (componentInstance && componentInstance.type) { |
import omit from 'object.omit' | ||
import pick from 'object.pick' | ||
/** | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* @module passthroughProps | ||
*/ | ||
/** | ||
* Return a props object with the specified propTypes omitted. | ||
* Automatically excludes ('theme', 'children', 'className', 'style') | ||
* | ||
* @param {Object} props React component props | ||
* @param {Object} propTypes React component propTypes | ||
* @param {Array} exclude an optional array of prop names to exclude | ||
* @returns {Object} props object without the excluded props | ||
*/ | ||
export function omitProps (props, propTypes = {}, exclude = []) { | ||
@@ -12,4 +28,12 @@ const keys = Object.keys(propTypes) | ||
/** | ||
* Return a props object with only specified propTypes. | ||
* | ||
* @param {Object} props React component props | ||
* @param {Object} propTypes React component propTypes | ||
* @param {Array} include an optional array of prop names to include | ||
* @returns {Object} props object with only the included props | ||
*/ | ||
export function pickProps (props, propTypes = {}, include = []) { | ||
return pick(props, Object.keys(propTypes).concat(include)) | ||
} |
@@ -5,2 +5,12 @@ import React from 'react' | ||
/** | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* Clone a React element without overwriting refs. | ||
* @module safeCloneElement | ||
* @param {ReactElement} element | ||
* @param {object} props | ||
* @return {ReactElement} | ||
*/ | ||
export default function safeCloneElement (element, props, ...children) { | ||
@@ -7,0 +17,0 @@ const cloneRef = props.ref |
import getDisplayName from './getDisplayName' | ||
import ownerWindow from '../dom/ownerWindow' | ||
export function origin (node) { | ||
const ownWindow = ownerWindow(node) | ||
const { location } = ownWindow | ||
// see https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage | ||
if (location.protocol === 'file:') { | ||
return '*' | ||
} else if (location.origin) { | ||
return location.origin | ||
} else if (location.port) { | ||
return `${location.protocol}//${location.hostname}:${location.port}` | ||
} else { | ||
return `${location.protocol}//${location.hostname}` | ||
} | ||
} | ||
const windowMessageListener = function (messageHandler, validSource) { | ||
/** | ||
* --- | ||
* category: utilities/react | ||
* --- | ||
* A decorator or higher order component that provides methods | ||
* for cross-origin communication (between iframes/windows). | ||
* | ||
* see https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage | ||
* @module windowMessageListener | ||
* @param {Function} messageHandler a handler for messages recieved by the component | ||
* @param {Function} validSource an optional function that would restrict message handling to a specified source. | ||
* @returns {Function} a function that decorates a React component with the behavior | ||
*/ | ||
export default function windowMessageListener (messageHandler, validSource) { | ||
return function (ComposedComponent) { | ||
@@ -26,2 +21,6 @@ return class extends ComposedComponent { | ||
static postMessage = function (target, message, origin) { | ||
target.postMessage(message, origin) | ||
} | ||
componentDidMount () { | ||
@@ -68,6 +67,25 @@ const win = ownerWindow(this) | ||
windowMessageListener.postMessage = function (target, message, origin) { | ||
target.postMessage(message, origin) | ||
/** | ||
* Return the origin of the owner window of the DOM element | ||
* | ||
* see https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage | ||
* | ||
* @param {DOMElement} node | ||
* @returns {String} the origin | ||
*/ | ||
export function origin (node) { | ||
const ownWindow = ownerWindow(node) | ||
const { location } = ownWindow | ||
if (location.protocol === 'file:') { | ||
return '*' | ||
} else if (location.origin) { | ||
return location.origin | ||
} else if (location.port) { | ||
return `${location.protocol}//${location.hostname}:${location.port}` | ||
} else { | ||
return `${location.protocol}//${location.hostname}` | ||
} | ||
} | ||
export default windowMessageListener |
// https://github.com/facebook/fbjs/blob/master/packages/fbjs/src/core/shallowEqual.js | ||
const hasOwnProperty = Object.prototype.hasOwnProperty | ||
/** | ||
* inlined Object.is polyfill to avoid requiring consumers ship their own | ||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is | ||
*/ | ||
function is (x, y) { | ||
// SameValue algorithm | ||
if (x === y) { // Steps 1-5, 7-10 | ||
// Steps 6.b-6.e: +0 != -0 | ||
return x !== 0 || y !== 0 || 1 / x === 1 / y | ||
} else { | ||
// Step 6.a: NaN == NaN | ||
return x !== x && y !== y // eslint-disable-line no-self-compare | ||
} | ||
} | ||
/** | ||
* --- | ||
* category: utilities | ||
* --- | ||
* @module shallowEqual | ||
* Performs equality by iterating through keys on an object and returning false | ||
* when any key has values which are not strictly equal between the arguments. | ||
* Returns true when the values of all keys are strictly equal. | ||
*/ | ||
* | ||
* @param {Object} objA | ||
* @param {Object} objB | ||
* @returns {Boolean} Returns true when the values of all keys are strictly equal | ||
*/ | ||
export default function shallowEqual (objA, objB) { | ||
@@ -51,1 +42,16 @@ if (is(objA, objB)) { | ||
} | ||
/* | ||
* inlined Object.is polyfill to avoid requiring consumers ship their own | ||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is | ||
*/ | ||
function is (x, y) { | ||
// SameValue algorithm | ||
if (x === y) { // Steps 1-5, 7-10 | ||
// Steps 6.b-6.e: +0 != -0 | ||
return x !== 0 || y !== 0 || 1 / x === 1 / y | ||
} else { | ||
// Step 6.a: NaN == NaN | ||
return x !== x && y !== y // eslint-disable-line no-self-compare | ||
} | ||
} |
@@ -0,5 +1,12 @@ | ||
/** | ||
* --- | ||
* category: utilities | ||
* --- | ||
* @param {Boolean} condition a condition that we expect to be true | ||
* @param {String} message a message to display as a console warning in DEV env when condition is false | ||
*/ | ||
export default function warning (condition, message, ...args) { | ||
if (!condition && process.env.NODE_ENV !== 'production' && typeof console !== 'undefined') { | ||
console.error.apply(undefined, [`Warning: ${message}`, ...args]) | ||
console.warn.apply(undefined, [`Warning: ${message}`, ...args]) | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
310670
3
37318
146
8236
30
39
70
12
277
1
+ Addedis-extendable@1.0.1(transitive)
+ Addedis-plain-object@2.0.4(transitive)
+ Addedobject.omit@3.0.0(transitive)
+ Addedregexp.prototype.flags@1.5.3(transitive)
- Removedfor-in@1.0.2(transitive)
- Removedfor-own@0.1.5(transitive)
- Removedis-extendable@0.1.1(transitive)
- Removedobject.omit@2.0.1(transitive)
- Removedregexp.prototype.flags@1.5.2(transitive)
Updatedmoment-timezone@^0.5.14
Updatedobject.omit@^3.0.0