diff --git a/HISTORY.md b/HISTORY.md index c4a7996b..f5733d11 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -3,6 +3,18 @@ * (react-select-plus) On dropdown open, scroll to element above focused option instead of scrolling all the way to the top of the option group. +## v1.0.0-rc / 2016-09-04 + +* fixed; reset value to `[]` when `multi=true`, thanks [Michael Williamson](https://github.com/mwilliamson) +* added; pass index to `renderLabel` method, thanks [nhducit](https://github.com/nhducit) +* fixed; uncontrolled to controlled component warning in React 15.3 +* fixed; props cleanup, thanks [Forbes Lindesay](https://github.com/ForbesLindesay) +* fixed; issue where a value of the number `0` would be assumed to be no value, thanks [Hanwen Cheng](https://github.com/hanwencheng) +* fixed; internal refs converted to callbacks instead of strings, thanks [Johnny Nguyen](https://github.com/gojohnnygo) +* added; optional `instanceId` prop for server-side rendering, thanks [Jevin Anderson](https://github.com/JevinAnderson) +* added; `onCloseResetsInput` prop, thanks [Frankie](https://github.com/frankievx) +* added; `Creatable` component, replaces pre-1.0 `allowCreate` prop, thanks [Brian Vaughn](https://github.com/bvaughn) + ## v1.0.0-beta14 / 2016-07-17 * fixed; `react-input-autosize` has been udpated to `1.1.0`, which includes fixes for the new warnings that React 15.2 logs diff --git a/README.md b/README.md index 6ae27524..b2a87660 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,11 @@ React-Select-Plus A Select control for [React](http://facebook.github.io/react/index.html) based on [JedWatson/React-Select](https://github.com/JedWatson/react-select/). -## New version 1.0.0-beta +## New version 1.0.0-rc -I've nearly completed a major rewrite of this component (see issue [#568](https://github.com/JedWatson/react-select/issues/568) for details and progress). The new code has been merged into `master`, and `react-select@1.0.0-beta` has been published to npm and bower. +I've nearly completed a major rewrite of this component (see issue [#568](https://github.com/JedWatson/react-select/issues/568) for details and progress). The new code has been merged into `master`, and `react-select@1.0.0-rc` has been published to npm and bower. -1.0.0 has some breaking changes. The documentation below still needs to be updated for the new API; notes on the changes can be found in [CHANGES.md](https://github.com/JedWatson/react-select/blob/master/CHANGES.md) and will be finalised into [HISTORY.md](https://github.com/JedWatson/react-select/blob/master/HISTORY.md) soon. - -Our tests need some major updates to work with the new API (see [#571](https://github.com/JedWatson/react-select/issues/571)) and are causing the build to fail, but the component is stable and robust in actual usage. +1.0.0 has some breaking changes. The documentation is still being updated for the new API; notes on the changes can be found in [CHANGES.md](https://github.com/JedWatson/react-select/blob/master/CHANGES.md) and will be finalised into [HISTORY.md](https://github.com/JedWatson/react-select/blob/master/HISTORY.md) soon. Testing, feedback and PRs for the new version are appreciated. @@ -54,7 +52,7 @@ You can also use the standalone build by including `react-select-plus.js` and `r - + ``` @@ -193,6 +191,29 @@ var isLoadingExternally = true; /> ``` +### User-created tags + +The `Creatable` component enables users to create new tags within react-select. +It decorates a `Select` and so it supports all of the default properties (eg single/multi mode, filtering, etc) in addition to a couple of custom ones (shown below). +The easiest way to use it is like so: + +```js +import { Creatable } from 'react-select'; + +function render (selectProps) { + return ; +}; +``` + +##### Creatable properties + +Property | Type | Description +:---|:---|:--- +`isOptionUnique` | function | Searches for any matching option within the set of options. This function prevents duplicate options from being created. By default this is a basic, case-sensitive comparison of label and value. Expected signature: `({ option: Object, options: Array, labelKey: string, valueKey: string }): boolean` | +`isValidNewOption` | function | Determines if the current input text represents a valid option. By default any non-empty string will be considered valid. Expected signature: `({ label: string }): boolean` | +`newOptionCreator` | function | Factory to create new option. Expected signature: `({ label: string, labelKey: string, valueKey: string }): Object` | +`shouldKeyDownEventCreateNewOption` | function | Decides if a keyDown event (eg its `keyCode`) should result in the creation of a new option. ENTER, TAB and comma keys create new options by dfeault. Expected signature: `({ keyCode: number }): boolean` | + ### Filtering options You can control how options are filtered with the following properties: @@ -213,7 +234,13 @@ You can also completely replace the method used to filter either a single option For multi-select inputs, when providing a custom `filterOptions` method, remember to exclude current values from the returned array of options. -#### Effeciently rendering large lists with windowing +#### Filtering large lists + +The default `filterOptions` method scans the options array for matches each time the filter text changes. +This works well but can get slow as the options array grows to several hundred objects. +For larger options lists a custom filter function like [`react-select-fast-filter-options`](https://github.com/bvaughn/react-select-fast-filter-options) will produce better results. + +### Effeciently rendering large lists with windowing The `menuRenderer` property can be used to override the default drop-down list of options. This should be done when the list is large (hundreds or thousands of items) for faster rendering. @@ -250,13 +277,36 @@ function cleanInput(inputValue) { /> ``` +### Overriding default key-down behavior with onInputKeyDown + +`Select` listens to `keyDown` events to select items, navigate drop-down list via arrow keys, etc. +You can extend or override this behavior by providing a `onInputKeyDown` callback. + +```js +function onInputKeyDown(event) { + switch (event.keyCode) { + case 9: // TAB + // Extend default TAB behavior by doing something here + break; + case 13: // ENTER + // Override default ENTER behavior by doing stuff here and then preventing default + event.preventDefault(); + break; + } +} + +` tag - newOptionCreator | func | undefined | factory to create new options when `allowCreate` is true [NOTE: not available in 1.0.0-beta] noResultsText | string | 'No results found' | placeholder displayed when there are no matching search results or a falsy value to hide it onBlur | func | undefined | onBlur handler: `function(event) {}` onBlurResetsInput | bool | true | whether to clear input on blur or not onChange | func | undefined | onChange handler: `function(newValue) {}` onClose | func | undefined | handler for when the menu closes: `function () {}` + onCloseResetInput | bool | true | whether to clear input when closing the menu through the arrow onFocus | func | undefined | onFocus handler: `function(event) {}` onInputChange | func | undefined | onInputChange handler: `function(inputValue) {}` + onInputKeyDown | func | undefined | input keyDown handler; call `event.preventDefault()` to override default `Select` behavior: `function(event) {}` onOpen | func | undefined | handler for when the menu opens: `function () {}` onValueClick | func | undefined | onClick handler for value labels: `function (value, event) {}` openOnFocus | bool | false | open the options menu when the input gets focus (requires searchable = true) diff --git a/bower.json b/bower.json index 18cfcbd2..bf99700c 100644 --- a/bower.json +++ b/bower.json @@ -4,7 +4,7 @@ "dist/react-select-plus.min.js", "dist/react-select-plus.min.css" ], - "version": "1.0.0-beta14.patch1", + "version": "1.0.0-rc.1", "homepage": "https://github.com/HubSpot/react-select-plus", "authors": [ "Trevor Burnham" @@ -17,7 +17,7 @@ ], "dependencies": { "classnames": "^2.2.0", - "react-input-autosize": "^0.6.5" + "react-input-autosize": "^1.1.0" }, "keywords": [ "react", diff --git a/dist/react-select-plus.css b/dist/react-select-plus.css index 2a932607..3edc1ca8 100644 --- a/dist/react-select-plus.css +++ b/dist/react-select-plus.css @@ -23,6 +23,7 @@ .Select.is-disabled .Select-arrow-zone { cursor: default; pointer-events: none; + opacity: 0.35; } .Select-control { background-color: #fff; diff --git a/dist/react-select-plus.js b/dist/react-select-plus.js index 4ea87400..2b2b87bf 100644 --- a/dist/react-select-plus.js +++ b/dist/react-select-plus.js @@ -1,26 +1,4 @@ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Select = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } + +var _react = (typeof window !== "undefined" ? window['React'] : typeof global !== "undefined" ? global['React'] : null); + +var _react2 = _interopRequireDefault(_react); + +var _Select = require('./Select'); + +var _Select2 = _interopRequireDefault(_Select); + +var _utilsDefaultFilterOptions = require('./utils/defaultFilterOptions'); + +var _utilsDefaultFilterOptions2 = _interopRequireDefault(_utilsDefaultFilterOptions); + +var _utilsDefaultMenuRenderer = require('./utils/defaultMenuRenderer'); + +var _utilsDefaultMenuRenderer2 = _interopRequireDefault(_utilsDefaultMenuRenderer); + +var Creatable = _react2['default'].createClass({ + displayName: 'CreatableSelect', + + propTypes: { + // See Select.propTypes.filterOptions + filterOptions: _react2['default'].PropTypes.any, + + // Searches for any matching option within the set of options. + // This function prevents duplicate options from being created. + // ({ option: Object, options: Array, labelKey: string, valueKey: string }): boolean + isOptionUnique: _react2['default'].PropTypes.func, + + // Determines if the current input text represents a valid option. + // ({ label: string }): boolean + isValidNewOption: _react2['default'].PropTypes.func, + + // See Select.propTypes.menuRenderer + menuRenderer: _react2['default'].PropTypes.any, + + // Factory to create new option. + // ({ label: string, labelKey: string, valueKey: string }): Object + newOptionCreator: _react2['default'].PropTypes.func, + + // Creates prompt/placeholder option text. + // (filterText: string): string + promptTextCreator: _react2['default'].PropTypes.func, + + // Decides if a keyDown event (eg its `keyCode`) should result in the creation of a new option. + shouldKeyDownEventCreateNewOption: _react2['default'].PropTypes.func + }, + + // Default prop methods + statics: { + isOptionUnique: isOptionUnique, + isValidNewOption: isValidNewOption, + newOptionCreator: newOptionCreator, + promptTextCreator: promptTextCreator, + shouldKeyDownEventCreateNewOption: shouldKeyDownEventCreateNewOption + }, + + getDefaultProps: function getDefaultProps() { + return { + filterOptions: _utilsDefaultFilterOptions2['default'], + isOptionUnique: isOptionUnique, + isValidNewOption: isValidNewOption, + menuRenderer: _utilsDefaultMenuRenderer2['default'], + newOptionCreator: newOptionCreator, + promptTextCreator: promptTextCreator, + shouldKeyDownEventCreateNewOption: shouldKeyDownEventCreateNewOption + }; + }, + + createNewOption: function createNewOption() { + var _props = this.props; + var isValidNewOption = _props.isValidNewOption; + var newOptionCreator = _props.newOptionCreator; + var shouldKeyDownEventCreateNewOption = _props.shouldKeyDownEventCreateNewOption; + var _select$props = this.select.props; + var labelKey = _select$props.labelKey; + var options = _select$props.options; + var valueKey = _select$props.valueKey; + + var inputValue = this.select.getInputValue(); + + if (isValidNewOption({ label: inputValue })) { + var option = newOptionCreator({ label: inputValue, labelKey: labelKey, valueKey: valueKey }); + var _isOptionUnique = this.isOptionUnique({ option: option }); + + // Don't add the same option twice. + if (_isOptionUnique) { + options.unshift(option); + + this.select.selectValue(option); + } + } + }, + + filterOptions: function filterOptions() { + var _props2 = this.props; + var filterOptions = _props2.filterOptions; + var isValidNewOption = _props2.isValidNewOption; + var promptTextCreator = _props2.promptTextCreator; + + var filteredOptions = filterOptions.apply(undefined, arguments); + + var inputValue = this.select ? this.select.getInputValue() : ''; + + if (isValidNewOption({ label: inputValue })) { + var _newOptionCreator = this.props.newOptionCreator; + var _select$props2 = this.select.props; + var labelKey = _select$props2.labelKey; + var options = _select$props2.options; + var valueKey = _select$props2.valueKey; + + var option = _newOptionCreator({ label: inputValue, labelKey: labelKey, valueKey: valueKey }); + + // TRICKY Compare to all options (not just filtered options) in case option has already been selected). + // For multi-selects, this would remove it from the filtered list. + var _isOptionUnique2 = this.isOptionUnique({ option: option, options: options }); + + if (_isOptionUnique2) { + var _prompt = promptTextCreator(inputValue); + + this._createPlaceholderOption = _newOptionCreator({ label: _prompt, labelKey: labelKey, valueKey: valueKey }); + + filteredOptions.unshift(this._createPlaceholderOption); + } + } + + return filteredOptions; + }, + + isOptionUnique: function isOptionUnique(_ref) { + var option = _ref.option; + var options = _ref.options; + + if (!this.select) { + return false; + } + + var isOptionUnique = this.props.isOptionUnique; + var _select$props3 = this.select.props; + var labelKey = _select$props3.labelKey; + var valueKey = _select$props3.valueKey; + + options = options || this.select.filterFlatOptions(); + + return isOptionUnique({ + labelKey: labelKey, + option: option, + options: options, + valueKey: valueKey + }); + }, + + menuRenderer: function menuRenderer(params) { + var menuRenderer = this.props.menuRenderer; + + return menuRenderer(_extends({}, params, { + onSelect: this.onOptionSelect + })); + }, + + onInputKeyDown: function onInputKeyDown(event) { + var shouldKeyDownEventCreateNewOption = this.props.shouldKeyDownEventCreateNewOption; + + var focusedOption = this.select.getFocusedOption(); + + if (focusedOption && focusedOption === this._createPlaceholderOption && shouldKeyDownEventCreateNewOption({ keyCode: event.keyCode })) { + this.createNewOption(); + + // Prevent decorated Select from doing anything additional with this keyDown event + event.preventDefault(); + } + }, + + onOptionSelect: function onOptionSelect(option, event) { + if (option === this._createPlaceholderOption) { + this.createNewOption(); + } else { + this.select.selectValue(option); + } + }, + + render: function render() { + var _this = this; + + var _props3 = this.props; + var newOptionCreator = _props3.newOptionCreator; + var shouldKeyDownEventCreateNewOption = _props3.shouldKeyDownEventCreateNewOption; + + var restProps = _objectWithoutProperties(_props3, ['newOptionCreator', 'shouldKeyDownEventCreateNewOption']); + + return _react2['default'].createElement(_Select2['default'], _extends({}, restProps, { + allowCreate: true, + filterOptions: this.filterOptions, + menuRenderer: this.menuRenderer, + onInputKeyDown: this.onInputKeyDown, + ref: function (ref) { + return _this.select = ref; + } + })); + } +}); + +function isOptionUnique(_ref2) { + var option = _ref2.option; + var options = _ref2.options; + var labelKey = _ref2.labelKey; + var valueKey = _ref2.valueKey; + + return options.filter(function (existingOption) { + return existingOption[labelKey] === option[labelKey] || existingOption[valueKey] === option[valueKey]; + }).length === 0; +}; + +function isValidNewOption(_ref3) { + var label = _ref3.label; + + return !!label; +}; + +function newOptionCreator(_ref4) { + var label = _ref4.label; + var labelKey = _ref4.labelKey; + var valueKey = _ref4.valueKey; + + var option = {}; + option[valueKey] = label; + option[labelKey] = label; + option.className = 'Select-create-option-placeholder'; + return option; +}; + +function promptTextCreator(label) { + return 'Create option "' + label + '"'; +} + +function shouldKeyDownEventCreateNewOption(_ref5) { + var keyCode = _ref5.keyCode; + + switch (keyCode) { + case 9: // TAB + case 13: // ENTER + case 188: + // COMMA + return true; + } + + return false; +}; + +module.exports = Creatable; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./Select":6,"./utils/defaultFilterOptions":8,"./utils/defaultMenuRenderer":9}],3:[function(require,module,exports){ (function (global){ 'use strict'; @@ -452,6 +695,8 @@ var _extends = Object.assign || function (target) { for (var i = 1; i < argument function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } +function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } + function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } var _react = (typeof window !== "undefined" ? window['React'] : typeof global !== "undefined" ? global['React'] : null); @@ -470,13 +715,13 @@ var _classnames = (typeof window !== "undefined" ? window['classNames'] : typeof var _classnames2 = _interopRequireDefault(_classnames); -var _blacklist = require('blacklist'); +var _utilsDefaultFilterOptions = require('./utils/defaultFilterOptions'); -var _blacklist2 = _interopRequireDefault(_blacklist); +var _utilsDefaultFilterOptions2 = _interopRequireDefault(_utilsDefaultFilterOptions); -var _utilsStripDiacritics = require('./utils/stripDiacritics'); +var _utilsDefaultMenuRenderer = require('./utils/defaultMenuRenderer'); -var _utilsStripDiacritics2 = _interopRequireDefault(_utilsStripDiacritics); +var _utilsDefaultMenuRenderer2 = _interopRequireDefault(_utilsDefaultMenuRenderer); var _Async = require('./Async'); @@ -486,6 +731,10 @@ var _Dropdown = require('./Dropdown'); var _Dropdown2 = _interopRequireDefault(_Dropdown); +var _Creatable = require('./Creatable'); + +var _Creatable2 = _interopRequireDefault(_Creatable); + var _Option = require('./Option'); var _Option2 = _interopRequireDefault(_Option); @@ -508,11 +757,19 @@ function clone(obj) { return copy; } +function isGroup(option) { + return option && Array.isArray(option.options); +} + function stringifyValue(value) { - if (typeof value === 'object') { + if (typeof value === 'string') { + return value; + } else if (typeof value === 'object') { return JSON.stringify(value); + } else if (value || value === 0) { + return String(value); } else { - return value; + return ''; } } @@ -526,7 +783,6 @@ var Select = _react2['default'].createClass({ propTypes: { addLabelText: _react2['default'].PropTypes.string, // placeholder displayed when you want to add a label on a multi-value input - allowCreate: _react2['default'].PropTypes.bool, // whether to allow creation of new entries 'aria-label': _react2['default'].PropTypes.string, // Aria label (for assistive tech) 'aria-labelledby': _react2['default'].PropTypes.string, // HTML ID of an element that should be used as the label (for assistive tech) autoBlur: _react2['default'].PropTypes.bool, // automatically blur the component when an option is selected @@ -549,6 +805,7 @@ var Select = _react2['default'].createClass({ ignoreCase: _react2['default'].PropTypes.bool, // whether to perform case-insensitive filtering inputProps: _react2['default'].PropTypes.object, // custom attributes for the Input inputRenderer: _react2['default'].PropTypes.func, // returns a custom input component + instanceId: _react2['default'].PropTypes.string, // set the components instanceId isLoading: _react2['default'].PropTypes.bool, // whether the Select is loading externally or not (such as options being loaded) isOpen: _react2['default'].PropTypes.bool, // whether the Select dropdown menu is open or not joinValues: _react2['default'].PropTypes.bool, // joins multiple values into a single form field with the delimiter (legacy mode) @@ -561,14 +818,15 @@ var Select = _react2['default'].createClass({ menuStyle: _react2['default'].PropTypes.object, // optional style to apply to the menu multi: _react2['default'].PropTypes.bool, // multi-value input name: _react2['default'].PropTypes.string, // generates a hidden tag with this field name for html forms - newOptionCreator: _react2['default'].PropTypes.func, // factory to create new options when allowCreate set noResultsText: stringOrNode, // placeholder displayed when there are no matching search results onBlur: _react2['default'].PropTypes.func, // onBlur handler: function (event) {} onBlurResetsInput: _react2['default'].PropTypes.bool, // whether input is cleared on blur onChange: _react2['default'].PropTypes.func, // onChange handler: function (newValue) {} onClose: _react2['default'].PropTypes.func, // fires when the menu is closed + onCloseResetsInput: _react2['default'].PropTypes.bool, // whether input is cleared when menu is closed through the arrow onFocus: _react2['default'].PropTypes.func, // onFocus handler: function (event) {} onInputChange: _react2['default'].PropTypes.func, // onInputChange handler: function (inputValue) {} + onInputKeyDown: _react2['default'].PropTypes.func, // input keyDown handler: function (event) {} onMenuScrollToBottom: _react2['default'].PropTypes.func, // fires when the menu is scrolled to the bottom; can be used to paginate options onOpen: _react2['default'].PropTypes.func, // fires when the menu is opened onValueClick: _react2['default'].PropTypes.func, // onClick handler for value labels: function (value, event) {} @@ -597,13 +855,12 @@ var Select = _react2['default'].createClass({ wrapperStyle: _react2['default'].PropTypes.object }, // optional style to apply to the component wrapper - statics: { Async: _Async2['default'] }, + statics: { Async: _Async2['default'], Creatable: _Creatable2['default'] }, getDefaultProps: function getDefaultProps() { return { addLabelText: 'Add "{label}"?', autosize: true, - allowCreate: false, backspaceRemoves: true, backspaceToRemoveMessage: 'Press backspace to remove {label}', clearable: true, @@ -613,7 +870,7 @@ var Select = _react2['default'].createClass({ disabled: false, dropdownComponent: _Dropdown2['default'], escapeClearsValue: true, - filterOptions: true, + filterOptions: _utilsDefaultFilterOptions2['default'], ignoreAccents: true, ignoreCase: true, inputProps: {}, @@ -623,9 +880,11 @@ var Select = _react2['default'].createClass({ matchPos: 'any', matchProp: 'any', menuBuffer: 0, + menuRenderer: _utilsDefaultMenuRenderer2['default'], multi: false, noResultsText: 'No results found', onBlurResetsInput: true, + onCloseResetsInput: true, openAfterFocus: false, optionComponent: _Option2['default'], optionGroupComponent: _OptionGroup2['default'], @@ -633,7 +892,6 @@ var Select = _react2['default'].createClass({ placeholder: 'Select...', renderInvalidValues: false, required: false, - resetValue: null, scrollMenuIntoView: true, searchable: true, simpleValue: false, @@ -647,7 +905,6 @@ var Select = _react2['default'].createClass({ return { inputValue: '', isFocused: false, - isLoading: false, isOpen: false, isPseudoFocused: false, required: false @@ -656,7 +913,7 @@ var Select = _react2['default'].createClass({ componentWillMount: function componentWillMount() { this._flatOptions = this.flattenOptions(this.props.options); - this._instancePrefix = 'react-select-' + ++instanceId + '-'; + this._instancePrefix = 'react-select-' + (this.props.instanceId || ++instanceId) + '-'; var valueArray = this.getValueArray(this.props.value); if (this.props.required) { @@ -688,6 +945,7 @@ var Select = _react2['default'].createClass({ componentWillUpdate: function componentWillUpdate(nextProps, nextState) { if (nextState.isOpen !== this.state.isOpen) { + this.toggleTouchOutsideEvent(nextState.isOpen); var handler = nextState.isOpen ? nextProps.onOpen : nextProps.onClose; handler && handler(); } @@ -695,11 +953,11 @@ var Select = _react2['default'].createClass({ componentDidUpdate: function componentDidUpdate(prevProps, prevState) { // focus to the selected option - if (this.refs.menu && this.refs.focused && this.state.isOpen && !this.hasScrolledToOption) { - var focusedOptionNode = _reactDom2['default'].findDOMNode(this.refs.focused); + if (this.menu && this.focused && this.state.isOpen && !this.hasScrolledToOption) { + var focusedOptionNode = _reactDom2['default'].findDOMNode(this.focused); var focusedOptionPreviousSibling = focusedOptionNode.previousSibling; var focusedOptionParent = focusedOptionNode.parentElement; - var menuNode = _reactDom2['default'].findDOMNode(this.refs.menu); + var menuNode = _reactDom2['default'].findDOMNode(this.menu); if (focusedOptionPreviousSibling) { menuNode.scrollTop = focusedOptionPreviousSibling.offsetTop; } else if (focusedOptionParent && focusedOptionParent === 'Select-menu') { @@ -712,18 +970,18 @@ var Select = _react2['default'].createClass({ this.hasScrolledToOption = false; } - if (this._scrollToFocusedOptionOnUpdate && this.refs.focused && this.refs.menu) { + if (this._scrollToFocusedOptionOnUpdate && this.focused && this.menu) { this._scrollToFocusedOptionOnUpdate = false; - var focusedDOM = _reactDom2['default'].findDOMNode(this.refs.focused); - var menuDOM = _reactDom2['default'].findDOMNode(this.refs.menu); + var focusedDOM = _reactDom2['default'].findDOMNode(this.focused); + var menuDOM = _reactDom2['default'].findDOMNode(this.menu); var focusedRect = focusedDOM.getBoundingClientRect(); var menuRect = menuDOM.getBoundingClientRect(); if (focusedRect.bottom > menuRect.bottom || focusedRect.top < menuRect.top) { menuDOM.scrollTop = focusedDOM.offsetTop + focusedDOM.clientHeight - menuDOM.offsetHeight; } } - if (this.props.scrollMenuIntoView && this.refs.menuContainer) { - var menuContainerRect = this.refs.menuContainer.getBoundingClientRect(); + if (this.props.scrollMenuIntoView && this.menuContainer) { + var menuContainerRect = this.menuContainer.getBoundingClientRect(); if (window.innerHeight < menuContainerRect.bottom + this.props.menuBuffer) { window.scrollBy(0, menuContainerRect.bottom + this.props.menuBuffer - window.innerHeight); } @@ -734,9 +992,28 @@ var Select = _react2['default'].createClass({ } }, + componentWillUnmount: function componentWillUnmount() { + document.removeEventListener('touchstart', this.handleTouchOutside); + }, + + toggleTouchOutsideEvent: function toggleTouchOutsideEvent(enabled) { + if (enabled) { + document.addEventListener('touchstart', this.handleTouchOutside); + } else { + document.removeEventListener('touchstart', this.handleTouchOutside); + } + }, + + handleTouchOutside: function handleTouchOutside(event) { + // handle touch outside on ios to dismiss menu + if (this.wrapper && !this.wrapper.contains(event.target)) { + this.closeMenu(); + } + }, + focus: function focus() { - if (!this.refs.input) return; - this.refs.input.focus(); + if (!this.input) return; + this.input.focus(); if (this.props.openAfterFocus) { this.setState({ @@ -746,8 +1023,8 @@ var Select = _react2['default'].createClass({ }, blurInput: function blurInput() { - if (!this.refs.input) return; - this.refs.input.blur(); + if (!this.input) return; + this.input.blur(); }, handleTouchMove: function handleTouchMove(event) { @@ -807,7 +1084,7 @@ var Select = _react2['default'].createClass({ // Call focus() again here to be safe. this.focus(); - var input = this.refs.input; + var input = this.input; if (typeof input.getInput === 'function') { // Get the actual DOM input if the ref is an component input = input.getInput(); @@ -859,11 +1136,19 @@ var Select = _react2['default'].createClass({ }, closeMenu: function closeMenu() { - this.setState({ - isOpen: false, - isPseudoFocused: this.state.isFocused && !this.props.multi, - inputValue: '' - }); + if (this.props.onCloseResetsInput) { + this.setState({ + isOpen: false, + isPseudoFocused: this.state.isFocused && !this.props.multi, + inputValue: '' + }); + } else { + this.setState({ + isOpen: false, + isPseudoFocused: this.state.isFocused && !this.props.multi, + inputValue: this.state.inputValue + }); + } this.hasScrolledToOption = false; }, @@ -881,7 +1166,7 @@ var Select = _react2['default'].createClass({ handleInputBlur: function handleInputBlur(event) { // The check for menu.contains(activeElement) is necessary to prevent IE11's scrollbar from closing the menu in certain contexts. - if (this.refs.menu && (this.refs.menu === document.activeElement || this.refs.menu.contains(document.activeElement))) { + if (this.menu && (this.menu === document.activeElement || this.menu.contains(document.activeElement))) { this.focus(); return; } @@ -902,6 +1187,7 @@ var Select = _react2['default'].createClass({ handleInputChange: function handleInputChange(event) { var newInputValue = event.target.value; + if (this.state.inputValue !== event.target.value && this.props.onInputChange) { var nextState = this.props.onInputChange(newInputValue); // Note: != used deliberately here to catch undefined and null @@ -909,6 +1195,7 @@ var Select = _react2['default'].createClass({ newInputValue = '' + nextState; } } + this.setState({ isOpen: true, isPseudoFocused: false, @@ -918,6 +1205,14 @@ var Select = _react2['default'].createClass({ handleKeyDown: function handleKeyDown(event) { if (this.props.disabled) return; + + if (typeof this.props.onInputKeyDown === 'function') { + this.props.onInputKeyDown(event); + if (event.defaultPrevented) { + return; + } + } + switch (event.keyCode) { case 8: // backspace @@ -973,15 +1268,6 @@ var Select = _react2['default'].createClass({ // home key this.focusStartOption(); break; - // case 188: // , - // if (this.props.allowCreate && this.props.multi) { - // event.preventDefault(); - // event.stopPropagation(); - // this.selectFocusedOption(); - // } else { - // return; - // } - // break; default: return; } @@ -1088,7 +1374,7 @@ var Select = _react2['default'].createClass({ selectValue: function selectValue(value) { var _this3 = this; - //NOTE: update value in the callback to make sure the input value is empty so that there are no sttyling issues (Chrome had issue otherwise) + //NOTE: update value in the callback to make sure the input value is empty so that there are no styling issues (Chrome had issue otherwise) this.hasScrolledToOption = false; if (this.props.multi) { this.setState({ @@ -1136,13 +1422,23 @@ var Select = _react2['default'].createClass({ } event.stopPropagation(); event.preventDefault(); - this.setValue(this.props.resetValue); + this.setValue(this.getResetValue()); this.setState({ isOpen: false, inputValue: '' }, this.focus); }, + getResetValue: function getResetValue() { + if (this.props.resetValue !== undefined) { + return this.props.resetValue; + } else if (this.props.multi) { + return []; + } else { + return null; + } + }, + focusOption: function focusOption(option) { this.setState({ focusedOption: option @@ -1234,10 +1530,15 @@ var Select = _react2['default'].createClass({ }); }, + getFocusedOption: function getFocusedOption() { + return this._focusedOption; + }, + + getInputValue: function getInputValue() { + return this.state.inputValue; + }, + selectFocusedOption: function selectFocusedOption() { - // if (this.props.allowCreate && !this.state.focusedOption) { - // return this.selectValue(this.state.inputValue); - // } if (this._focusedOption) { return this.selectValue(this._focusedOption); } @@ -1278,7 +1579,7 @@ var Select = _react2['default'].createClass({ onRemove: _this4.removeValue, value: value }, - renderLabel(value), + renderLabel(value, i), _react2['default'].createElement( 'span', { className: 'Select-aria-only' }, @@ -1303,6 +1604,8 @@ var Select = _react2['default'].createClass({ }, renderInput: function renderInput(valueArray, focusedOptionIndex) { + var _this5 = this; + if (this.props.inputRenderer) { return this.props.inputRenderer(); } else { @@ -1327,13 +1630,19 @@ var Select = _react2['default'].createClass({ onBlur: this.handleInputBlur, onChange: this.handleInputChange, onFocus: this.handleInputFocus, - ref: 'input', + ref: function ref(_ref2) { + return _this5.input = _ref2; + }, required: this.state.required, value: this.state.inputValue }); if (this.props.disabled || !this.props.searchable) { - var divProps = (0, _blacklist2['default'])(this.props.inputProps, 'inputClassName'); + var _props$inputProps = this.props.inputProps; + var inputClassName = _props$inputProps.inputClassName; + + var divProps = _objectWithoutProperties(_props$inputProps, ['inputClassName']); + return _react2['default'].createElement('div', _extends({}, divProps, { role: 'combobox', 'aria-expanded': isOpen, @@ -1343,7 +1652,9 @@ var Select = _react2['default'].createClass({ tabIndex: this.props.tabIndex || 0, onBlur: this.handleInputBlur, onFocus: this.handleInputFocus, - ref: 'input', + ref: function (ref) { + return _this5.input = ref; + }, 'aria-readonly': '' + !!this.props.disabled, style: { border: 0, width: 1, display: 'inline-block' } })); } @@ -1360,7 +1671,7 @@ var Select = _react2['default'].createClass({ }, renderClear: function renderClear() { - if (!this.props.clearable || !this.props.value || this.props.multi && !this.props.value.length || this.props.disabled || this.props.isLoading) return; + if (!this.props.clearable || !this.props.value || this.props.value === 0 || this.props.multi && !this.props.value.length || this.props.disabled || this.props.isLoading) return; return _react2['default'].createElement( 'span', { className: 'Select-clear-zone', title: this.props.multi ? this.props.clearAllText : this.props.clearValueText, @@ -1382,151 +1693,111 @@ var Select = _react2['default'].createClass({ ); }, - filterOptions: function filterOptions(options, excludeOptions) { - var _this5 = this; - - var excludeOptionValues = null; + filterFlatOptions: function filterFlatOptions(excludeOptions) { var filterValue = this.state.inputValue; - if (typeof this.props.filterOptions === 'function') { - return this.props.filterOptions.call(this, options, filterValue, excludeOptions); - } else if (this.props.filterOptions) { - var _ret = (function () { - if (_this5.props.ignoreAccents) { - filterValue = (0, _utilsStripDiacritics2['default'])(filterValue); - } - if (_this5.props.ignoreCase) { - filterValue = filterValue.toLowerCase(); - } - if (excludeOptions) excludeOptionValues = excludeOptions.map(function (i) { - return i[_this5.props.valueKey]; - }); - var includeOption = function includeOption(option) { - if (excludeOptionValues && excludeOptionValues.indexOf(option[_this5.props.valueKey]) > -1) return false; - if (_this5.props.filterOption) return _this5.props.filterOption.call(_this5, option, filterValue); - if (!filterValue) return true; - var valueTest = String(option[_this5.props.valueKey]); - var labelTest = String(option[_this5.props.labelKey]); - if (_this5.props.ignoreAccents) { - if (_this5.props.matchProp !== 'label') valueTest = (0, _utilsStripDiacritics2['default'])(valueTest); - if (_this5.props.matchProp !== 'value') labelTest = (0, _utilsStripDiacritics2['default'])(labelTest); - } - if (_this5.props.ignoreCase) { - if (_this5.props.matchProp !== 'label') valueTest = valueTest.toLowerCase(); - if (_this5.props.matchProp !== 'value') labelTest = labelTest.toLowerCase(); - } - return _this5.props.matchPos === 'start' ? _this5.props.matchProp !== 'label' && valueTest.substr(0, filterValue.length) === filterValue || _this5.props.matchProp !== 'value' && labelTest.substr(0, filterValue.length) === filterValue : _this5.props.matchProp !== 'label' && valueTest.indexOf(filterValue) >= 0 || _this5.props.matchProp !== 'value' && labelTest.indexOf(filterValue) >= 0; - }; - var filteredOptions = []; - options.forEach(function (option) { - if (_this5.isGroup(option)) { - var filteredGroup = clone(option); - filteredGroup.options = _this5.filterOptions(option.options, excludeOptions); - if (filteredGroup.options.length) { - filteredOptions.push(filteredGroup); - }; - } else if (includeOption(option)) { - filteredOptions.push(option); - }; - }); - return { - v: filteredOptions - }; - })(); - - if (typeof _ret === 'object') return _ret.v; + var flatOptions = this._flatOptions; + if (this.props.filterOptions) { + // Maintain backwards compatibility with boolean attribute + var filterOptions = typeof this.props.filterOptions === 'function' ? this.props.filterOptions : _utilsDefaultFilterOptions2['default']; + + return filterOptions(flatOptions, filterValue, excludeOptions, { + filterOption: this.props.filterOption, + ignoreAccents: this.props.ignoreAccents, + ignoreCase: this.props.ignoreCase, + labelKey: this.props.labelKey, + matchPos: this.props.matchPos, + matchProp: this.props.matchProp, + valueKey: this.props.valueKey + }); } else { - return options; + return flatOptions; } }, - isGroup: function isGroup(option) { - return option && Array.isArray(option.options); - }, - - flattenOptions: function flattenOptions(options) { - if (!options) return; + flattenOptions: function flattenOptions(options, group) { + if (!options) return []; var flatOptions = []; for (var i = 0; i < options.length; i++) { - if (this.isGroup(options[i])) { - flatOptions = flatOptions.concat(this.flattenOptions(options[i].options)); + // We clone each option with a pointer to its parent group for efficient unflattening + var optionCopy = clone(options[i]); + optionCopy.isInTree = false; + if (group) { + optionCopy.group = group; + } + if (isGroup(optionCopy)) { + flatOptions = flatOptions.concat(this.flattenOptions(optionCopy.options, optionCopy)); + optionCopy.options = []; } else { - flatOptions.push(options[i]); + flatOptions.push(optionCopy); } } return flatOptions; }, - renderMenu: function renderMenu(options, valueArray, focusedOption) { - var _this6 = this; + unflattenOptions: function unflattenOptions(flatOptions) { + var groupedOptions = []; + var parent = undefined, + child = undefined; - if (options && options.length) { - if (this.props.menuRenderer) { - return this.props.menuRenderer({ - focusedOption: focusedOption, - focusOption: this.focusOption, - labelKey: this.props.labelKey, - options: options, - selectValue: this.selectValue, - valueArray: valueArray - }); - } else { - var _ret2 = (function () { - var OptionGroup = _this6.props.optionGroupComponent; - var Option = _this6.props.optionComponent; - var renderLabel = _this6.props.optionRenderer || _this6.getOptionLabel; + // Remove all ancestor groups from the tree + flatOptions.forEach(function (option) { + option.isInTree = false; + parent = option.group; + while (parent) { + if (parent.isInTree) { + parent.options = []; + parent.isInTree = false; + } + parent = parent.group; + } + }); - return { - v: options.map(function (option, i) { - if (_this6.isGroup(option)) { - var optionGroupClass = (0, _classnames2['default'])({ - 'Select-option-group': true - }); - - return _react2['default'].createElement( - OptionGroup, - { - className: optionGroupClass, - key: 'option-group-' + i, - label: renderLabel(option), - option: option - }, - _this6.renderMenu(option.options, valueArray, focusedOption) - ); - } else { - var isSelected = valueArray && valueArray.indexOf(option) > -1; - var isFocused = option === focusedOption; - var optionRef = isFocused ? 'focused' : null; - var optionClass = (0, _classnames2['default'])(_this6.props.optionClassName, { - 'Select-option': true, - 'is-selected': isSelected, - 'is-focused': isFocused, - 'is-disabled': option.disabled - }); - - return _react2['default'].createElement( - Option, - { - instancePrefix: _this6._instancePrefix, - optionIndex: i, - className: optionClass, - isDisabled: option.disabled, - isFocused: isFocused, - key: 'option-' + i + '-' + option[_this6.props.valueKey], - onSelect: _this6.selectValue, - onFocus: _this6.focusOption, - option: option, - isSelected: isSelected, - ref: optionRef - }, - renderLabel(option) - ); - } - }) - }; - })(); + // Now reconstruct the options tree + flatOptions.forEach(function (option) { + child = option; + parent = child.group; + while (parent) { + if (!child.isInTree) { + parent.options.push(child); + child.isInTree = true; + } - if (typeof _ret2 === 'object') return _ret2.v; + child = parent; + parent = child.group; } + if (!child.isInTree) { + groupedOptions.push(child); + child.isInTree = true; + } + }); + return groupedOptions; + }, + + onOptionRef: function onOptionRef(ref, isFocused) { + if (isFocused) { + this.focused = ref; + } + }, + + renderMenu: function renderMenu(options, valueArray, focusedOption) { + if (options && options.length) { + return this.props.menuRenderer({ + focusedOption: focusedOption, + focusOption: this.focusOption, + instancePrefix: this._instancePrefix, + labelKey: this.props.labelKey, + onFocus: this.focusOption, + onOptionRef: this.onOptionRef, + onSelect: this.selectValue, + optionClassName: this.props.optionClassName, + optionComponent: this.props.optionComponent, + optionGroupComponent: this.props.optionGroupComponent, + optionRenderer: this.props.optionRenderer || this.getOptionLabel, + options: options, + selectValue: this.selectValue, + valueArray: valueArray, + valueKey: this.props.valueKey + }); } else if (this.props.noResultsText) { return _react2['default'].createElement( 'div', @@ -1539,16 +1810,18 @@ var Select = _react2['default'].createClass({ }, renderHiddenField: function renderHiddenField(valueArray) { - var _this7 = this; + var _this6 = this; if (!this.props.name) return; if (this.props.joinValues) { var value = valueArray.map(function (i) { - return stringifyValue(i[_this7.props.valueKey]); + return stringifyValue(i[_this6.props.valueKey]); }).join(this.props.delimiter); return _react2['default'].createElement('input', { type: 'hidden', - ref: 'value', + ref: function (ref) { + return _this6.value = ref; + }, name: this.props.name, value: value, disabled: this.props.disabled }); @@ -1557,9 +1830,9 @@ var Select = _react2['default'].createClass({ return _react2['default'].createElement('input', { key: 'hidden.' + index, type: 'hidden', ref: 'value' + index, - name: _this7.props.name, - value: stringifyValue(item[_this7.props.valueKey]), - disabled: _this7.props.disabled }); + name: _this6.props.name, + value: stringifyValue(item[_this6.props.valueKey]), + disabled: _this6.props.disabled }); }); }, @@ -1582,6 +1855,8 @@ var Select = _react2['default'].createClass({ }, renderOuter: function renderOuter(options, valueArray, focusedOption) { + var _this7 = this; + var Dropdown = this.props.dropdownComponent; var menu = this.renderMenu(options, valueArray, focusedOption); if (!menu) { @@ -1593,10 +1868,14 @@ var Select = _react2['default'].createClass({ null, _react2['default'].createElement( 'div', - { ref: 'menuContainer', className: 'Select-menu-outer', style: this.props.menuContainerStyle }, + { ref: function (ref) { + return _this7.menuContainer = ref; + }, className: 'Select-menu-outer', style: this.props.menuContainerStyle }, _react2['default'].createElement( 'div', - { ref: 'menu', role: 'listbox', className: 'Select-menu', id: this._instancePrefix + '-list', + { ref: function (ref) { + return _this7.menu = ref; + }, role: 'listbox', className: 'Select-menu', id: this._instancePrefix + '-list', style: this.props.menuStyle, onScroll: this.handleMenuScroll, onMouseDown: this.handleMouseDownOnMenu }, @@ -1607,9 +1886,11 @@ var Select = _react2['default'].createClass({ }, render: function render() { + var _this8 = this; + var valueArray = this.getValueArray(this.props.value); - var options = this.filterOptions(this.props.options || [], this.props.multi ? valueArray : null); - this._visibleOptions = this.flattenOptions(options); + this._visibleOptions = this.filterFlatOptions(this.props.multi ? valueArray : null); + var options = this.unflattenOptions(this._visibleOptions); var isOpen = typeof this.props.isOpen === 'boolean' ? this.props.isOpen : this.state.isOpen; if (this.props.multi && !options.length && valueArray.length && !this.state.inputValue) isOpen = false; var focusedOptionIndex = this.getFocusableOptionIndex(valueArray[0]); @@ -1643,13 +1924,17 @@ var Select = _react2['default'].createClass({ return _react2['default'].createElement( 'div', - { ref: 'wrapper', + { ref: function (ref) { + return _this8.wrapper = ref; + }, className: className, style: this.props.wrapperStyle }, this.renderHiddenField(valueArray), _react2['default'].createElement( 'div', - { ref: 'control', + { ref: function (ref) { + return _this8.control = ref; + }, className: 'Select-control', style: this.props.style, onKeyDown: this.handleKeyDown, @@ -1679,7 +1964,7 @@ exports['default'] = Select; module.exports = exports['default']; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./Async":2,"./Dropdown":3,"./Option":4,"./OptionGroup":5,"./Value":7,"./utils/stripDiacritics":8,"blacklist":1}],7:[function(require,module,exports){ +},{"./Async":1,"./Creatable":2,"./Dropdown":3,"./Option":4,"./OptionGroup":5,"./Value":7,"./utils/defaultFilterOptions":8,"./utils/defaultMenuRenderer":9}],7:[function(require,module,exports){ (function (global){ 'use strict'; @@ -1792,6 +2077,151 @@ module.exports = Value; },{}],8:[function(require,module,exports){ 'use strict'; +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _stripDiacritics = require('./stripDiacritics'); + +var _stripDiacritics2 = _interopRequireDefault(_stripDiacritics); + +function filterOptions(options, filterValue, excludeOptions, props) { + var _this = this; + + if (props.ignoreAccents) { + filterValue = (0, _stripDiacritics2['default'])(filterValue); + } + + if (props.ignoreCase) { + filterValue = filterValue.toLowerCase(); + } + + if (excludeOptions) excludeOptions = excludeOptions.map(function (i) { + return i[props.valueKey]; + }); + + return options.filter(function (option) { + if (excludeOptions && excludeOptions.indexOf(option[props.valueKey]) > -1) return false; + if (props.filterOption) return props.filterOption.call(_this, option, filterValue); + if (!filterValue) return true; + var valueTest = String(option[props.valueKey]); + var labelTest = String(option[props.labelKey]); + if (props.ignoreAccents) { + if (props.matchProp !== 'label') valueTest = (0, _stripDiacritics2['default'])(valueTest); + if (props.matchProp !== 'value') labelTest = (0, _stripDiacritics2['default'])(labelTest); + } + if (props.ignoreCase) { + if (props.matchProp !== 'label') valueTest = valueTest.toLowerCase(); + if (props.matchProp !== 'value') labelTest = labelTest.toLowerCase(); + } + return props.matchPos === 'start' ? props.matchProp !== 'label' && valueTest.substr(0, filterValue.length) === filterValue || props.matchProp !== 'value' && labelTest.substr(0, filterValue.length) === filterValue : props.matchProp !== 'label' && valueTest.indexOf(filterValue) >= 0 || props.matchProp !== 'value' && labelTest.indexOf(filterValue) >= 0; + }); +} + +module.exports = filterOptions; + +},{"./stripDiacritics":10}],9:[function(require,module,exports){ +(function (global){ +'use strict'; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _classnames = (typeof window !== "undefined" ? window['classNames'] : typeof global !== "undefined" ? global['classNames'] : null); + +var _classnames2 = _interopRequireDefault(_classnames); + +var _react = (typeof window !== "undefined" ? window['React'] : typeof global !== "undefined" ? global['React'] : null); + +var _react2 = _interopRequireDefault(_react); + +function isGroup(option) { + return option && Array.isArray(option.options); +} + +function menuRenderer(_ref) { + var focusedOption = _ref.focusedOption; + var instancePrefix = _ref.instancePrefix; + var labelKey = _ref.labelKey; + var onFocus = _ref.onFocus; + var onOptionRef = _ref.onOptionRef; + var onSelect = _ref.onSelect; + var optionClassName = _ref.optionClassName; + var optionComponent = _ref.optionComponent; + var optionGroupComponent = _ref.optionGroupComponent; + var optionRenderer = _ref.optionRenderer; + var options = _ref.options; + var valueArray = _ref.valueArray; + var valueKey = _ref.valueKey; + + var OptionGroup = optionGroupComponent; + var Option = optionComponent; + var renderLabel = optionRenderer || this.getOptionLabel; + + var renderOptions = function renderOptions(optionsSubset) { + return optionsSubset.map(function (option, i) { + if (isGroup(option)) { + var optionGroupClass = (0, _classnames2['default'])({ + 'Select-option-group': true + }); + + return _react2['default'].createElement( + OptionGroup, + { + className: optionGroupClass, + key: 'option-group-' + i, + label: renderLabel(option), + option: option, + optionIndex: i + }, + renderOptions(option.options) + ); + } else { + var _ret = (function () { + var isSelected = valueArray && valueArray.indexOf(option) > -1; + var isFocused = option === focusedOption; + var optionRef = isFocused ? 'focused' : null; + var optionClass = (0, _classnames2['default'])(optionClassName, { + 'Select-option': true, + 'is-selected': isSelected, + 'is-focused': isFocused, + 'is-disabled': option.disabled + }); + + return { + v: _react2['default'].createElement( + Option, + { + className: optionClass, + instancePrefix: instancePrefix, + isDisabled: option.disabled, + isFocused: isFocused, + isSelected: isSelected, + key: 'option-' + i + '-' + option[valueKey], + onFocus: onFocus, + onSelect: onSelect, + option: option, + optionIndex: i, + ref: function (ref) { + onOptionRef(ref, isFocused); + } + }, + renderLabel(option, i) + ) + }; + })(); + + if (typeof _ret === 'object') return _ret.v; + } + }); + }; + + return renderOptions(options); +} + +module.exports = menuRenderer; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],10:[function(require,module,exports){ +'use strict'; + var map = [{ 'base': 'A', 'letters': /[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g }, { 'base': 'AA', 'letters': /[\uA732]/g }, { 'base': 'AE', 'letters': /[\u00C6\u01FC\u01E2]/g }, { 'base': 'AO', 'letters': /[\uA734]/g }, { 'base': 'AU', 'letters': /[\uA736]/g }, { 'base': 'AV', 'letters': /[\uA738\uA73A]/g }, { 'base': 'AY', 'letters': /[\uA73C]/g }, { 'base': 'B', 'letters': /[\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181]/g }, { 'base': 'C', 'letters': /[\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E]/g }, { 'base': 'D', 'letters': /[\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779]/g }, { 'base': 'DZ', 'letters': /[\u01F1\u01C4]/g }, { 'base': 'Dz', 'letters': /[\u01F2\u01C5]/g }, { 'base': 'E', 'letters': /[\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E]/g }, { 'base': 'F', 'letters': /[\u0046\u24BB\uFF26\u1E1E\u0191\uA77B]/g }, { 'base': 'G', 'letters': /[\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E]/g }, { 'base': 'H', 'letters': /[\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D]/g }, { 'base': 'I', 'letters': /[\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197]/g }, { 'base': 'J', 'letters': /[\u004A\u24BF\uFF2A\u0134\u0248]/g }, { 'base': 'K', 'letters': /[\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2]/g }, { 'base': 'L', 'letters': /[\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780]/g }, { 'base': 'LJ', 'letters': /[\u01C7]/g }, { 'base': 'Lj', 'letters': /[\u01C8]/g }, { 'base': 'M', 'letters': /[\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C]/g }, { 'base': 'N', 'letters': /[\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4]/g }, { 'base': 'NJ', 'letters': /[\u01CA]/g }, { 'base': 'Nj', 'letters': /[\u01CB]/g }, { 'base': 'O', 'letters': /[\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C]/g }, { 'base': 'OI', 'letters': /[\u01A2]/g }, { 'base': 'OO', 'letters': /[\uA74E]/g }, { 'base': 'OU', 'letters': /[\u0222]/g }, { 'base': 'P', 'letters': /[\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754]/g }, { 'base': 'Q', 'letters': /[\u0051\u24C6\uFF31\uA756\uA758\u024A]/g }, { 'base': 'R', 'letters': /[\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782]/g }, { 'base': 'S', 'letters': /[\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784]/g }, { 'base': 'T', 'letters': /[\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786]/g }, { 'base': 'TZ', 'letters': /[\uA728]/g }, { 'base': 'U', 'letters': /[\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244]/g }, { 'base': 'V', 'letters': /[\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245]/g }, { 'base': 'VY', 'letters': /[\uA760]/g }, { 'base': 'W', 'letters': /[\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72]/g }, { 'base': 'X', 'letters': /[\u0058\u24CD\uFF38\u1E8A\u1E8C]/g }, { 'base': 'Y', 'letters': /[\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE]/g }, { 'base': 'Z', 'letters': /[\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762]/g }, { 'base': 'a', 'letters': /[\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250]/g }, { 'base': 'aa', 'letters': /[\uA733]/g }, { 'base': 'ae', 'letters': /[\u00E6\u01FD\u01E3]/g }, { 'base': 'ao', 'letters': /[\uA735]/g }, { 'base': 'au', 'letters': /[\uA737]/g }, { 'base': 'av', 'letters': /[\uA739\uA73B]/g }, { 'base': 'ay', 'letters': /[\uA73D]/g }, { 'base': 'b', 'letters': /[\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253]/g }, { 'base': 'c', 'letters': /[\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184]/g }, { 'base': 'd', 'letters': /[\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A]/g }, { 'base': 'dz', 'letters': /[\u01F3\u01C6]/g }, { 'base': 'e', 'letters': /[\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD]/g }, { 'base': 'f', 'letters': /[\u0066\u24D5\uFF46\u1E1F\u0192\uA77C]/g }, { 'base': 'g', 'letters': /[\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F]/g }, { 'base': 'h', 'letters': /[\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265]/g }, { 'base': 'hv', 'letters': /[\u0195]/g }, { 'base': 'i', 'letters': /[\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131]/g }, { 'base': 'j', 'letters': /[\u006A\u24D9\uFF4A\u0135\u01F0\u0249]/g }, { 'base': 'k', 'letters': /[\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3]/g }, { 'base': 'l', 'letters': /[\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747]/g }, { 'base': 'lj', 'letters': /[\u01C9]/g }, { 'base': 'm', 'letters': /[\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F]/g }, { 'base': 'n', 'letters': /[\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5]/g }, { 'base': 'nj', 'letters': /[\u01CC]/g }, { 'base': 'o', 'letters': /[\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275]/g }, { 'base': 'oi', 'letters': /[\u01A3]/g }, { 'base': 'ou', 'letters': /[\u0223]/g }, { 'base': 'oo', 'letters': /[\uA74F]/g }, { 'base': 'p', 'letters': /[\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755]/g }, { 'base': 'q', 'letters': /[\u0071\u24E0\uFF51\u024B\uA757\uA759]/g }, { 'base': 'r', 'letters': /[\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783]/g }, { 'base': 's', 'letters': /[\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B]/g }, { 'base': 't', 'letters': /[\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787]/g }, { 'base': 'tz', 'letters': /[\uA729]/g }, { 'base': 'u', 'letters': /[\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289]/g }, { 'base': 'v', 'letters': /[\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C]/g }, { 'base': 'vy', 'letters': /[\uA761]/g }, { 'base': 'w', 'letters': /[\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73]/g }, { 'base': 'x', 'letters': /[\u0078\u24E7\uFF58\u1E8B\u1E8D]/g }, { 'base': 'y', 'letters': /[\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF]/g }, { 'base': 'z', 'letters': /[\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763]/g }]; module.exports = function stripDiacritics(str) { diff --git a/dist/react-select-plus.min.css b/dist/react-select-plus.min.css index c6ac40d3..e2dec2e1 100644 --- a/dist/react-select-plus.min.css +++ b/dist/react-select-plus.min.css @@ -1 +1 @@ -.Select,.Select-control{position:relative}.Select,.Select div,.Select input,.Select span{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.Select.is-disabled>.Select-control{background-color:#f9f9f9}.Select.is-disabled>.Select-control:hover{box-shadow:none}.Select.is-disabled .Select-arrow-zone{cursor:default;pointer-events:none}.Select-control{background-color:#fff;border-radius:4px;border:1px solid #ccc;color:#333;cursor:default;display:table;border-spacing:0;border-collapse:separate;height:36px;outline:0;overflow:hidden;width:100%}.is-searchable.is-focused:not(.is-open)>.Select-control,.is-searchable.is-open>.Select-control{cursor:text}.Select-control:hover{box-shadow:0 1px 0 rgba(0,0,0,.06)}.Select-control .Select-input:focus{outline:0}.is-open>.Select-control{border-bottom-right-radius:0;border-bottom-left-radius:0;background:#fff;border-color:#b3b3b3 #ccc #d9d9d9}.is-open>.Select-control>.Select-arrow{border-color:transparent transparent #999;border-width:0 5px 5px}.is-focused:not(.is-open)>.Select-control{border-color:#007eff;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 0 3px rgba(0,126,255,.1)}.Select--single>.Select-control .Select-value,.Select-placeholder{bottom:0;color:#aaa;left:0;line-height:34px;padding-left:10px;padding-right:10px;position:absolute;right:0;top:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.has-value.Select--single>.Select-control .Select-value .Select-value-label,.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value .Select-value-label{color:#333}.has-value.Select--single>.Select-control .Select-value a.Select-value-label,.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label{cursor:pointer;text-decoration:none}.has-value.Select--single>.Select-control .Select-value a.Select-value-label:focus,.has-value.Select--single>.Select-control .Select-value a.Select-value-label:hover,.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:focus,.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:hover{color:#007eff;outline:0;text-decoration:underline}.Select-input{height:34px;padding-left:10px;padding-right:10px;vertical-align:middle}.Select-input>input{width:100%;background:none;border:0;box-shadow:none;cursor:default;display:inline-block;font-family:inherit;font-size:inherit;margin:0;outline:0;line-height:14px;padding:8px 0 12px;-webkit-appearance:none}.is-focused .Select-input>input{cursor:text}.Select-arrow-zone,.Select-clear-zone,.Select-loading-zone{cursor:pointer;position:relative;text-align:center;vertical-align:middle}.has-value.is-pseudo-focused .Select-input{opacity:0}.Select-control:not(.is-searchable)>.Select-input{outline:0}.Select-loading-zone{display:table-cell;width:16px}.Select-loading{-webkit-animation:Select-animation-spin .4s infinite linear;-o-animation:Select-animation-spin .4s infinite linear;animation:Select-animation-spin .4s infinite linear;width:16px;height:16px;box-sizing:border-box;border-radius:50%;border:2px solid #ccc;border-right-color:#333;display:inline-block;position:relative;vertical-align:middle}.Select-clear-zone{-webkit-animation:Select-animation-fadeIn .2s;-o-animation:Select-animation-fadeIn .2s;animation:Select-animation-fadeIn .2s;color:#999;display:table-cell;width:17px}.Select-clear-zone:hover{color:#D0021B}.Select-clear{display:inline-block;font-size:18px;line-height:1}.Select--multi .Select-clear-zone{width:17px}.Select-arrow-zone{display:table-cell;width:25px;padding-right:5px}.Select--multi .Select-multi-value-wrapper,.Select-arrow{display:inline-block}.Select-arrow{border-color:#999 transparent transparent;border-style:solid;border-width:5px 5px 2.5px;height:0;width:0}.Select-arrow-zone:hover>.Select-arrow,.is-open .Select-arrow{border-top-color:#666}.Select .Select-aria-only{display:inline-block;height:1px;width:1px;margin:-1px;clip:rect(0,0,0,0);overflow:hidden}@-webkit-keyframes Select-animation-fadeIn{from{opacity:0}to{opacity:1}}@keyframes Select-animation-fadeIn{from{opacity:0}to{opacity:1}}.Select-menu-outer{border-bottom-right-radius:4px;border-bottom-left-radius:4px;background-color:#fff;border:1px solid #ccc;border-top-color:#e6e6e6;box-shadow:0 1px 0 rgba(0,0,0,.06);box-sizing:border-box;margin-top:-1px;max-height:200px;position:absolute;top:100%;width:100%;z-index:1;-webkit-overflow-scrolling:touch}.Select-menu{max-height:198px;overflow-y:auto}.Select-option-group-label{box-sizing:border-box;background-color:#fff;color:#666;font-weight:700;cursor:default;display:block;padding:8px 10px}.Select-option-group-label~.Select-option,.Select-option-group-label~.Select-option-group{padding-left:20px}.Select-noresults,.Select-option{box-sizing:border-box;display:block;padding:8px 10px}.Select-option{background-color:#fff;color:#666;cursor:pointer}.Select-option:last-child{border-bottom-right-radius:4px;border-bottom-left-radius:4px}.Select-option.is-selected{background-color:#f5faff;background-color:rgba(0,126,255,.04);color:#333}.Select-option.is-focused{background-color:#ebf5ff;background-color:rgba(0,126,255,.08);color:#333}.Select-option.is-disabled{color:#ccc;cursor:default}.Select-noresults{color:#999;cursor:default}.Select--multi .Select-input{vertical-align:middle;margin-left:10px;padding:0}.Select--multi.has-value .Select-input{margin-left:5px}.Select--multi .Select-value{background-color:#ebf5ff;background-color:rgba(0,126,255,.08);border-radius:2px;border:1px solid #c2e0ff;border:1px solid rgba(0,126,255,.24);color:#007eff;display:inline-block;font-size:.9em;line-height:1.4;margin-left:5px;margin-top:5px;vertical-align:top}.Select--multi .Select-value-icon,.Select--multi .Select-value-label{display:inline-block;vertical-align:middle}.Select--multi .Select-value-label{border-bottom-right-radius:2px;border-top-right-radius:2px;cursor:default;padding:2px 5px}.Select--multi a.Select-value-label{color:#007eff;cursor:pointer;text-decoration:none}.Select--multi a.Select-value-label:hover{text-decoration:underline}.Select--multi .Select-value-icon{cursor:pointer;border-bottom-left-radius:2px;border-top-left-radius:2px;border-right:1px solid #c2e0ff;border-right:1px solid rgba(0,126,255,.24);padding:1px 5px 3px}.Select--multi .Select-value-icon:focus,.Select--multi .Select-value-icon:hover{background-color:#d8eafd;background-color:rgba(0,113,230,.08);color:#0071e6}.Select--multi .Select-value-icon:active{background-color:#c2e0ff;background-color:rgba(0,126,255,.24)}.Select--multi.is-disabled .Select-value{background-color:#fcfcfc;border:1px solid #e3e3e3;color:#333}.Select--multi.is-disabled .Select-value-icon{cursor:not-allowed;border-right:1px solid #e3e3e3}.Select--multi.is-disabled .Select-value-icon:active,.Select--multi.is-disabled .Select-value-icon:focus,.Select--multi.is-disabled .Select-value-icon:hover{background-color:#fcfcfc}@keyframes Select-animation-spin{to{transform:rotate(1turn)}}@-webkit-keyframes Select-animation-spin{to{-webkit-transform:rotate(1turn)}} \ No newline at end of file +.Select,.Select-control{position:relative}.Select,.Select div,.Select input,.Select span{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.Select.is-disabled>.Select-control{background-color:#f9f9f9}.Select.is-disabled>.Select-control:hover{box-shadow:none}.Select.is-disabled .Select-arrow-zone{cursor:default;pointer-events:none;opacity:.35}.Select-control{background-color:#fff;border-radius:4px;border:1px solid #ccc;color:#333;cursor:default;display:table;border-spacing:0;border-collapse:separate;height:36px;outline:0;overflow:hidden;width:100%}.is-searchable.is-focused:not(.is-open)>.Select-control,.is-searchable.is-open>.Select-control{cursor:text}.Select-control:hover{box-shadow:0 1px 0 rgba(0,0,0,.06)}.Select-control .Select-input:focus{outline:0}.is-open>.Select-control{border-bottom-right-radius:0;border-bottom-left-radius:0;background:#fff;border-color:#b3b3b3 #ccc #d9d9d9}.is-open>.Select-control>.Select-arrow{border-color:transparent transparent #999;border-width:0 5px 5px}.is-focused:not(.is-open)>.Select-control{border-color:#007eff;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 0 3px rgba(0,126,255,.1)}.Select--single>.Select-control .Select-value,.Select-placeholder{bottom:0;color:#aaa;left:0;line-height:34px;padding-left:10px;padding-right:10px;position:absolute;right:0;top:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.has-value.Select--single>.Select-control .Select-value .Select-value-label,.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value .Select-value-label{color:#333}.has-value.Select--single>.Select-control .Select-value a.Select-value-label,.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label{cursor:pointer;text-decoration:none}.has-value.Select--single>.Select-control .Select-value a.Select-value-label:focus,.has-value.Select--single>.Select-control .Select-value a.Select-value-label:hover,.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:focus,.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:hover{color:#007eff;outline:0;text-decoration:underline}.Select-input{height:34px;padding-left:10px;padding-right:10px;vertical-align:middle}.Select-input>input{width:100%;background:none;border:0;box-shadow:none;cursor:default;display:inline-block;font-family:inherit;font-size:inherit;margin:0;outline:0;line-height:14px;padding:8px 0 12px;-webkit-appearance:none}.is-focused .Select-input>input{cursor:text}.Select-arrow-zone,.Select-clear-zone,.Select-loading-zone{cursor:pointer;position:relative;text-align:center;vertical-align:middle}.has-value.is-pseudo-focused .Select-input{opacity:0}.Select-control:not(.is-searchable)>.Select-input{outline:0}.Select-loading-zone{display:table-cell;width:16px}.Select-loading{-webkit-animation:Select-animation-spin .4s infinite linear;-o-animation:Select-animation-spin .4s infinite linear;animation:Select-animation-spin .4s infinite linear;width:16px;height:16px;box-sizing:border-box;border-radius:50%;border:2px solid #ccc;border-right-color:#333;display:inline-block;position:relative;vertical-align:middle}.Select-clear-zone{-webkit-animation:Select-animation-fadeIn .2s;-o-animation:Select-animation-fadeIn .2s;animation:Select-animation-fadeIn .2s;color:#999;display:table-cell;width:17px}.Select-clear-zone:hover{color:#D0021B}.Select-clear{display:inline-block;font-size:18px;line-height:1}.Select--multi .Select-clear-zone{width:17px}.Select-arrow-zone{display:table-cell;width:25px;padding-right:5px}.Select--multi .Select-multi-value-wrapper,.Select-arrow{display:inline-block}.Select-arrow{border-color:#999 transparent transparent;border-style:solid;border-width:5px 5px 2.5px;height:0;width:0}.Select-arrow-zone:hover>.Select-arrow,.is-open .Select-arrow{border-top-color:#666}.Select .Select-aria-only{display:inline-block;height:1px;width:1px;margin:-1px;clip:rect(0,0,0,0);overflow:hidden}@-webkit-keyframes Select-animation-fadeIn{from{opacity:0}to{opacity:1}}@keyframes Select-animation-fadeIn{from{opacity:0}to{opacity:1}}.Select-menu-outer{border-bottom-right-radius:4px;border-bottom-left-radius:4px;background-color:#fff;border:1px solid #ccc;border-top-color:#e6e6e6;box-shadow:0 1px 0 rgba(0,0,0,.06);box-sizing:border-box;margin-top:-1px;max-height:200px;position:absolute;top:100%;width:100%;z-index:1;-webkit-overflow-scrolling:touch}.Select-menu{max-height:198px;overflow-y:auto}.Select-option-group-label{box-sizing:border-box;background-color:#fff;color:#666;font-weight:700;cursor:default;display:block;padding:8px 10px}.Select-option-group-label~.Select-option,.Select-option-group-label~.Select-option-group{padding-left:20px}.Select-noresults,.Select-option{box-sizing:border-box;display:block;padding:8px 10px}.Select-option{background-color:#fff;color:#666;cursor:pointer}.Select-option:last-child{border-bottom-right-radius:4px;border-bottom-left-radius:4px}.Select-option.is-selected{background-color:#f5faff;background-color:rgba(0,126,255,.04);color:#333}.Select-option.is-focused{background-color:#ebf5ff;background-color:rgba(0,126,255,.08);color:#333}.Select-option.is-disabled{color:#ccc;cursor:default}.Select-noresults{color:#999;cursor:default}.Select--multi .Select-input{vertical-align:middle;margin-left:10px;padding:0}.Select--multi.has-value .Select-input{margin-left:5px}.Select--multi .Select-value{background-color:#ebf5ff;background-color:rgba(0,126,255,.08);border-radius:2px;border:1px solid #c2e0ff;border:1px solid rgba(0,126,255,.24);color:#007eff;display:inline-block;font-size:.9em;line-height:1.4;margin-left:5px;margin-top:5px;vertical-align:top}.Select--multi .Select-value-icon,.Select--multi .Select-value-label{display:inline-block;vertical-align:middle}.Select--multi .Select-value-label{border-bottom-right-radius:2px;border-top-right-radius:2px;cursor:default;padding:2px 5px}.Select--multi a.Select-value-label{color:#007eff;cursor:pointer;text-decoration:none}.Select--multi a.Select-value-label:hover{text-decoration:underline}.Select--multi .Select-value-icon{cursor:pointer;border-bottom-left-radius:2px;border-top-left-radius:2px;border-right:1px solid #c2e0ff;border-right:1px solid rgba(0,126,255,.24);padding:1px 5px 3px}.Select--multi .Select-value-icon:focus,.Select--multi .Select-value-icon:hover{background-color:#d8eafd;background-color:rgba(0,113,230,.08);color:#0071e6}.Select--multi .Select-value-icon:active{background-color:#c2e0ff;background-color:rgba(0,126,255,.24)}.Select--multi.is-disabled .Select-value{background-color:#fcfcfc;border:1px solid #e3e3e3;color:#333}.Select--multi.is-disabled .Select-value-icon{cursor:not-allowed;border-right:1px solid #e3e3e3}.Select--multi.is-disabled .Select-value-icon:active,.Select--multi.is-disabled .Select-value-icon:focus,.Select--multi.is-disabled .Select-value-icon:hover{background-color:#fcfcfc}@keyframes Select-animation-spin{to{transform:rotate(1turn)}}@-webkit-keyframes Select-animation-spin{to{-webkit-transform:rotate(1turn)}} \ No newline at end of file diff --git a/dist/react-select-plus.min.js b/dist/react-select-plus.min.js index 99cd7645..ef38da3c 100644 --- a/dist/react-select-plus.min.js +++ b/dist/react-select-plus.min.js @@ -1,2 +1,2 @@ -!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.Select=e()}}(function(){return function e(t,u,s){function o(i,a){if(!u[i]){if(!t[i]){var r="function"==typeof require&&require;if(!a&&r)return r(i,!0);if(n)return n(i,!0);var l=new Error("Cannot find module '"+i+"'");throw l.code="MODULE_NOT_FOUND",l}var p=u[i]={exports:{}};t[i][0].call(p.exports,function(e){var u=t[i][1][e];return o(u?u:e)},p,p.exports,e,t,u,s)}return u[i].exports}for(var n="function"==typeof require&&require,i=0;i=0;--u){var s=t.slice(0,u);if(e[s]&&(t===s||e[s].complete))return e[s]}}function a(e,t){if(e&&"function"==typeof e.then)return e.then(function(e){t(null,e)},function(e){t(e)})}var r=Object.assign||function(e){for(var t=1;tl.bottom||r.topt.offsetHeight&&!(t.scrollHeight-t.offsetHeight-t.scrollTop)&&this.props.onMenuScrollToBottom()}},handleRequired:function(e,t){return!e||(t?0===e.length:0===Object.keys(e).length)},getOptionLabel:function(e){return e[this.props.labelKey]},getValueArray:function(e,t){var u=this,s="object"==typeof t?t:this.props;if(s.multi){if("string"==typeof e&&(e=e.split(s.delimiter)),!Array.isArray(e)){if(null===e||void 0===e)return[];e=[e]}return e.map(function(e){return u.expandValue(e,s)}).filter(function(e){return e})}var o=this.expandValue(e,s);return o?[o]:[]},expandValue:function(e,t){if("string"!=typeof e&&"number"!=typeof e)return e;var u=this.props,s=u.labelKey,o=u.valueKey,i=u.renderInvalidValues,a=this._flatOptions;if(a&&""!==e){for(var r=0;r0?u-=1:u=t.length-1;else if("start"===e)u=0;else if("end"===e)u=t.length-1;else if("page_up"===e){var o=u-this.props.pageSize;u=o<0?0:o}else if("page_down"===e){var o=u+this.props.pageSize;u=o>t.length-1?t.length-1:o}u===-1&&(u=0),this.setState({focusedIndex:t[u].index,focusedOption:t[u].option})}},selectFocusedOption:function(){if(this._focusedOption)return this.selectValue(this._focusedOption)},renderLoading:function(){if(this.props.isLoading)return p["default"].createElement("span",{className:"Select-loading-zone","aria-hidden":"true"},p["default"].createElement("span",{className:"Select-loading"}))},renderValue:function(e,t){var u=this,s=this.props.valueRenderer||this.getOptionLabel,o=this.props.valueComponent;if(!e.length)return this.state.inputValue?null:p["default"].createElement("div",{className:"Select-placeholder"},this.props.placeholder);var n=this.props.onValueClick?this.handleValueClick:null;return this.props.multi?e.map(function(e,t){return p["default"].createElement(o,{id:u._instancePrefix+"-value-"+t,instancePrefix:u._instancePrefix,disabled:u.props.disabled||e.clearableValue===!1,key:"value-"+t+"-"+e[u.props.valueKey],onClick:n,onRemove:u.removeValue,value:e},s(e),p["default"].createElement("span",{className:"Select-aria-only"}," "))}):this.state.inputValue?void 0:(t&&(n=null),p["default"].createElement(o,{id:this._instancePrefix+"-value-item",disabled:this.props.disabled,instancePrefix:this._instancePrefix,onClick:n,value:e[0]},s(e[0])))},renderInput:function(e,t){if(this.props.inputRenderer)return this.props.inputRenderer();var u,s=(0,g["default"])("Select-input",this.props.inputProps.className),o=!!this.state.isOpen,i=(0,g["default"])((u={},n(u,this._instancePrefix+"-list",o),n(u,this._instancePrefix+"-backspace-remove-message",this.props.multi&&!this.props.disabled&&this.state.isFocused&&!this.state.inputValue),u)),a=r({},this.props.inputProps,{role:"combobox","aria-expanded":""+o,"aria-owns":i,"aria-haspopup":""+o,"aria-activedescendant":o?this._instancePrefix+"-option-"+t:this._instancePrefix+"-value","aria-labelledby":this.props["aria-labelledby"],"aria-label":this.props["aria-label"],className:s,tabIndex:this.props.tabIndex,onBlur:this.handleInputBlur,onChange:this.handleInputChange,onFocus:this.handleInputFocus,ref:"input",required:this.state.required,value:this.state.inputValue});if(this.props.disabled||!this.props.searchable){var l=(0,y["default"])(this.props.inputProps,"inputClassName");return p["default"].createElement("div",r({},l,{role:"combobox","aria-expanded":o,"aria-owns":o?this._instancePrefix+"-list":this._instancePrefix+"-value","aria-activedescendant":o?this._instancePrefix+"-option-"+t:this._instancePrefix+"-value",className:s,tabIndex:this.props.tabIndex||0,onBlur:this.handleInputBlur,onFocus:this.handleInputFocus,ref:"input","aria-readonly":""+!!this.props.disabled,style:{border:0,width:1,display:"inline-block"}}))}return this.props.autosize?p["default"].createElement(h["default"],r({},a,{minWidth:"5px"})):p["default"].createElement("div",{className:s},p["default"].createElement("input",a))},renderClear:function(){if(this.props.clearable&&this.props.value&&(!this.props.multi||this.props.value.length)&&!this.props.disabled&&!this.props.isLoading)return p["default"].createElement("span",{className:"Select-clear-zone",title:this.props.multi?this.props.clearAllText:this.props.clearValueText,"aria-label":this.props.multi?this.props.clearAllText:this.props.clearValueText,onMouseDown:this.clearValue,onTouchStart:this.handleTouchStart,onTouchMove:this.handleTouchMove,onTouchEnd:this.handleTouchEndClearValue},p["default"].createElement("span",{className:"Select-clear",dangerouslySetInnerHTML:{__html:"×"}}))},renderArrow:function(){return p["default"].createElement("span",{className:"Select-arrow-zone",onMouseDown:this.handleMouseDownOnArrow},p["default"].createElement("span",{className:"Select-arrow",onMouseDown:this.handleMouseDownOnArrow}))},filterOptions:function(e,t){var u=this,s=null,o=this.state.inputValue;if("function"==typeof this.props.filterOptions)return this.props.filterOptions.call(this,e,o,t);if(!this.props.filterOptions)return e;var n=function(){u.props.ignoreAccents&&(o=(0,v["default"])(o)),u.props.ignoreCase&&(o=o.toLowerCase()),t&&(s=t.map(function(e){return e[u.props.valueKey]}));var n=function(e){if(s&&s.indexOf(e[u.props.valueKey])>-1)return!1;if(u.props.filterOption)return u.props.filterOption.call(u,e,o);if(!o)return!0;var t=String(e[u.props.valueKey]),n=String(e[u.props.labelKey]);return u.props.ignoreAccents&&("label"!==u.props.matchProp&&(t=(0,v["default"])(t)),"value"!==u.props.matchProp&&(n=(0,v["default"])(n))),u.props.ignoreCase&&("label"!==u.props.matchProp&&(t=t.toLowerCase()),"value"!==u.props.matchProp&&(n=n.toLowerCase())),"start"===u.props.matchPos?"label"!==u.props.matchProp&&t.substr(0,o.length)===o||"value"!==u.props.matchProp&&n.substr(0,o.length)===o:"label"!==u.props.matchProp&&t.indexOf(o)>=0||"value"!==u.props.matchProp&&n.indexOf(o)>=0},a=[];return e.forEach(function(e){if(u.isGroup(e)){var s=i(e);s.options=u.filterOptions(e.options,t),s.options.length&&a.push(s)}else n(e)&&a.push(e)}),{v:a}}();return"object"==typeof n?n.v:void 0},isGroup:function(e){return e&&Array.isArray(e.options)},flattenOptions:function(e){if(e){for(var t=[],u=0;u-1,d=e===u,c=d?"focused":null,f=(0,g["default"])(s.props.optionClassName,{"Select-option":!0,"is-selected":l,"is-focused":d,"is-disabled":e.disabled});return p["default"].createElement(n,{instancePrefix:s._instancePrefix,optionIndex:a,className:f,isDisabled:e.disabled,isFocused:d,key:"option-"+a+"-"+e[s.props.valueKey],onSelect:s.selectValue,onFocus:s.focusOption,option:e,isSelected:l,ref:c},i(e))})}}();return"object"==typeof o?o.v:void 0},renderHiddenField:function(e){var t=this;if(this.props.name){if(this.props.joinValues){var u=e.map(function(e){return a(e[t.props.valueKey])}).join(this.props.delimiter);return p["default"].createElement("input",{type:"hidden",ref:"value",name:this.props.name,value:u,disabled:this.props.disabled})}return e.map(function(e,u){return p["default"].createElement("input",{key:"hidden."+u,type:"hidden",ref:"value"+u,name:t.props.name,value:a(e[t.props.valueKey]),disabled:t.props.disabled})})}},getFocusableOptionIndex:function(e){var t=this._visibleOptions;if(!t.length)return null;var u=this.state.focusedOption||e;if(u&&!u.disabled){var s=t.indexOf(u);if(s!==-1)return s}for(var o=0;o=0;--u){var n=t.slice(0,u);if(e[n]&&(t===n||e[n].complete))return e[n]}}function r(e,t){if(e&&"function"==typeof e.then)return e.then(function(e){t(null,e)},function(e){t(e)})}var a=Object.assign||function(e){for(var t=1;t=0||Object.prototype.hasOwnProperty.call(e,n)&&(u[n]=e[n]);return u}function o(e){var t=e.option,u=e.options,n=e.labelKey,s=e.valueKey;return 0===u.filter(function(e){return e[n]===t[n]||e[s]===t[s]}).length}function i(e){var t=e.label;return!!t}function r(e){var t=e.label,u=e.labelKey,n=e.valueKey,s={};return s[n]=t,s[u]=t,s.className="Select-create-option-placeholder",s}function a(e){return'Create option "'+e+'"'}function l(e){var t=e.keyCode;switch(t){case 9:case 13:case 188:return!0}return!1}var p=Object.assign||function(e){for(var t=1;t=0||Object.prototype.hasOwnProperty.call(e,n)&&(u[n]=e[n]);return u}function i(e,t,u){return t in e?Object.defineProperty(e,t,{value:u,enumerable:!0,configurable:!0,writable:!0}):e[t]=u,e}function r(e){var t={};for(var u in e)e.hasOwnProperty(u)&&(t[u]=e[u]);return t}function a(e){return e&&Array.isArray(e.options)}function l(e){return"string"==typeof e?e:"object"==typeof e?JSON.stringify(e):e||0===e?String(e):""}Object.defineProperty(u,"__esModule",{value:!0});var p=Object.assign||function(e){for(var t=1;tl.bottom||a.topt.offsetHeight&&!(t.scrollHeight-t.offsetHeight-t.scrollTop)&&this.props.onMenuScrollToBottom()}},handleRequired:function(e,t){return!e||(t?0===e.length:0===Object.keys(e).length)},getOptionLabel:function(e){return e[this.props.labelKey]},getValueArray:function(e,t){var u=this,n="object"==typeof t?t:this.props;if(n.multi){if("string"==typeof e&&(e=e.split(n.delimiter)),!Array.isArray(e)){if(null===e||void 0===e)return[];e=[e]}return e.map(function(e){return u.expandValue(e,n)}).filter(function(e){return e})}var s=this.expandValue(e,n);return s?[s]:[]},expandValue:function(e,t){if("string"!=typeof e&&"number"!=typeof e)return e;var u=this.props,n=u.labelKey,s=u.valueKey,o=u.renderInvalidValues,r=this._flatOptions;if(r&&""!==e){for(var a=0;a0?u-=1:u=t.length-1;else if("start"===e)u=0;else if("end"===e)u=t.length-1;else if("page_up"===e){var s=u-this.props.pageSize;u=s<0?0:s}else if("page_down"===e){var s=u+this.props.pageSize;u=s>t.length-1?t.length-1:s}u===-1&&(u=0),this.setState({focusedIndex:t[u].index,focusedOption:t[u].option})}},getFocusedOption:function(){return this._focusedOption},getInputValue:function(){return this.state.inputValue},selectFocusedOption:function(){if(this._focusedOption)return this.selectValue(this._focusedOption)},renderLoading:function(){if(this.props.isLoading)return c["default"].createElement("span",{className:"Select-loading-zone","aria-hidden":"true"},c["default"].createElement("span",{className:"Select-loading"}))},renderValue:function(e,t){var u=this,n=this.props.valueRenderer||this.getOptionLabel,s=this.props.valueComponent;if(!e.length)return this.state.inputValue?null:c["default"].createElement("div",{className:"Select-placeholder"},this.props.placeholder);var o=this.props.onValueClick?this.handleValueClick:null;return this.props.multi?e.map(function(e,t){return c["default"].createElement(s,{id:u._instancePrefix+"-value-"+t,instancePrefix:u._instancePrefix,disabled:u.props.disabled||e.clearableValue===!1,key:"value-"+t+"-"+e[u.props.valueKey],onClick:o,onRemove:u.removeValue,value:e},n(e,t),c["default"].createElement("span",{className:"Select-aria-only"}," "))}):this.state.inputValue?void 0:(t&&(o=null),c["default"].createElement(s,{id:this._instancePrefix+"-value-item",disabled:this.props.disabled,instancePrefix:this._instancePrefix,onClick:o,value:e[0]},n(e[0])))},renderInput:function(e,t){var u=this;if(this.props.inputRenderer)return this.props.inputRenderer();var n,s=(0,b["default"])("Select-input",this.props.inputProps.className),r=!!this.state.isOpen,a=(0,b["default"])((n={},i(n,this._instancePrefix+"-list",r),i(n,this._instancePrefix+"-backspace-remove-message",this.props.multi&&!this.props.disabled&&this.state.isFocused&&!this.state.inputValue),n)),l=p({},this.props.inputProps,{role:"combobox","aria-expanded":""+r,"aria-owns":a,"aria-haspopup":""+r,"aria-activedescendant":r?this._instancePrefix+"-option-"+t:this._instancePrefix+"-value","aria-labelledby":this.props["aria-labelledby"],"aria-label":this.props["aria-label"],className:s,tabIndex:this.props.tabIndex,onBlur:this.handleInputBlur,onChange:this.handleInputChange,onFocus:this.handleInputFocus,ref:function(e){return u.input=e},required:this.state.required,value:this.state.inputValue});if(this.props.disabled||!this.props.searchable){var d=this.props.inputProps,f=(d.inputClassName,o(d,["inputClassName"]));return c["default"].createElement("div",p({},f,{role:"combobox","aria-expanded":r,"aria-owns":r?this._instancePrefix+"-list":this._instancePrefix+"-value","aria-activedescendant":r?this._instancePrefix+"-option-"+t:this._instancePrefix+"-value",className:s,tabIndex:this.props.tabIndex||0,onBlur:this.handleInputBlur,onFocus:this.handleInputFocus,ref:function(e){return u.input=e},"aria-readonly":""+!!this.props.disabled,style:{border:0,width:1,display:"inline-block"}}))}return this.props.autosize?c["default"].createElement(g["default"],p({},l,{minWidth:"5px"})):c["default"].createElement("div",{className:s},c["default"].createElement("input",l))},renderClear:function(){if(this.props.clearable&&this.props.value&&0!==this.props.value&&(!this.props.multi||this.props.value.length)&&!this.props.disabled&&!this.props.isLoading)return c["default"].createElement("span",{className:"Select-clear-zone",title:this.props.multi?this.props.clearAllText:this.props.clearValueText,"aria-label":this.props.multi?this.props.clearAllText:this.props.clearValueText,onMouseDown:this.clearValue,onTouchStart:this.handleTouchStart,onTouchMove:this.handleTouchMove,onTouchEnd:this.handleTouchEndClearValue},c["default"].createElement("span",{className:"Select-clear",dangerouslySetInnerHTML:{__html:"×"}}))},renderArrow:function(){return c["default"].createElement("span",{className:"Select-arrow-zone",onMouseDown:this.handleMouseDownOnArrow},c["default"].createElement("span",{className:"Select-arrow",onMouseDown:this.handleMouseDownOnArrow}))},filterFlatOptions:function(e){var t=this.state.inputValue,u=this._flatOptions;if(this.props.filterOptions){var n="function"==typeof this.props.filterOptions?this.props.filterOptions:m["default"];return n(u,t,e,{filterOption:this.props.filterOption,ignoreAccents:this.props.ignoreAccents,ignoreCase:this.props.ignoreCase,labelKey:this.props.labelKey,matchPos:this.props.matchPos,matchProp:this.props.matchProp,valueKey:this.props.valueKey})}return u},flattenOptions:function(e,t){if(!e)return[];for(var u=[],n=0;n-1)return!1;if(n.filterOption)return n.filterOption.call(s,e,t);if(!t)return!0;var o=String(e[n.valueKey]),r=String(e[n.labelKey]);return n.ignoreAccents&&("label"!==n.matchProp&&(o=(0,i["default"])(o)),"value"!==n.matchProp&&(r=(0,i["default"])(r))),n.ignoreCase&&("label"!==n.matchProp&&(o=o.toLowerCase()),"value"!==n.matchProp&&(r=r.toLowerCase())),"start"===n.matchPos?"label"!==n.matchProp&&o.substr(0,t.length)===t||"value"!==n.matchProp&&r.substr(0,t.length)===t:"label"!==n.matchProp&&o.indexOf(t)>=0||"value"!==n.matchProp&&r.indexOf(t)>=0})}var o=e("./stripDiacritics"),i=n(o);t.exports=s},{"./stripDiacritics":10}],9:[function(e,t,u){(function(e){"use strict";function u(e){return e&&e.__esModule?e:{"default":e}}function n(e){return e&&Array.isArray(e.options)}function s(e){var t=e.focusedOption,u=e.instancePrefix,s=(e.labelKey,e.onFocus),o=e.onOptionRef,r=e.onSelect,l=e.optionClassName,p=e.optionComponent,d=e.optionGroupComponent,c=e.optionRenderer,f=e.options,h=e.valueArray,E=e.valueKey,g=d,y=p,b=c||this.getOptionLabel,v=function m(e){return e.map(function(e,p){if(n(e)){var d=(0,i["default"])({"Select-option-group":!0});return a["default"].createElement(g,{className:d,key:"option-group-"+p,label:b(e),option:e,optionIndex:p},m(e.options))}var c=function(){var n=h&&h.indexOf(e)>-1,d=e===t,c=(0,i["default"])(l,{"Select-option":!0,"is-selected":n,"is-focused":d,"is-disabled":e.disabled});return{v:a["default"].createElement(y,{className:c,instancePrefix:u,isDisabled:e.disabled,isFocused:d,isSelected:n,key:"option-"+p+"-"+e[E],onFocus:s,onSelect:r,option:e,optionIndex:p,ref:function(e){o(e,d)}},b(e,p))}}();if("object"==typeof c)return c.v})};return v(f)}var o="undefined"!=typeof window?window.classNames:"undefined"!=typeof e?e.classNames:null,i=u(o),r="undefined"!=typeof window?window.React:"undefined"!=typeof e?e.React:null,a=u(r);t.exports=s}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],10:[function(e,t,u){"use strict";var n=[{base:"A",letters:/[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g},{base:"AA",letters:/[\uA732]/g},{base:"AE",letters:/[\u00C6\u01FC\u01E2]/g},{base:"AO",letters:/[\uA734]/g},{base:"AU",letters:/[\uA736]/g},{base:"AV",letters:/[\uA738\uA73A]/g},{base:"AY",letters:/[\uA73C]/g},{base:"B",letters:/[\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181]/g},{base:"C",letters:/[\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E]/g},{base:"D",letters:/[\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779]/g},{base:"DZ",letters:/[\u01F1\u01C4]/g},{base:"Dz",letters:/[\u01F2\u01C5]/g},{base:"E",letters:/[\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E]/g},{base:"F",letters:/[\u0046\u24BB\uFF26\u1E1E\u0191\uA77B]/g},{base:"G",letters:/[\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E]/g},{base:"H",letters:/[\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D]/g},{base:"I",letters:/[\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197]/g},{base:"J",letters:/[\u004A\u24BF\uFF2A\u0134\u0248]/g},{base:"K",letters:/[\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2]/g},{base:"L",letters:/[\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780]/g},{base:"LJ",letters:/[\u01C7]/g},{base:"Lj",letters:/[\u01C8]/g},{base:"M",letters:/[\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C]/g},{base:"N",letters:/[\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4]/g},{base:"NJ",letters:/[\u01CA]/g},{base:"Nj",letters:/[\u01CB]/g},{base:"O",letters:/[\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C]/g},{base:"OI",letters:/[\u01A2]/g},{base:"OO",letters:/[\uA74E]/g},{base:"OU",letters:/[\u0222]/g},{base:"P",letters:/[\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754]/g},{base:"Q",letters:/[\u0051\u24C6\uFF31\uA756\uA758\u024A]/g},{base:"R",letters:/[\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782]/g},{base:"S",letters:/[\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784]/g},{base:"T",letters:/[\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786]/g},{base:"TZ",letters:/[\uA728]/g},{base:"U",letters:/[\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244]/g},{base:"V",letters:/[\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245]/g},{base:"VY",letters:/[\uA760]/g},{base:"W",letters:/[\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72]/g},{base:"X",letters:/[\u0058\u24CD\uFF38\u1E8A\u1E8C]/g},{base:"Y",letters:/[\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE]/g},{base:"Z",letters:/[\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762]/g},{base:"a",letters:/[\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250]/g},{base:"aa",letters:/[\uA733]/g},{base:"ae",letters:/[\u00E6\u01FD\u01E3]/g},{base:"ao",letters:/[\uA735]/g},{base:"au",letters:/[\uA737]/g},{base:"av",letters:/[\uA739\uA73B]/g},{base:"ay",letters:/[\uA73D]/g},{base:"b",letters:/[\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253]/g},{base:"c",letters:/[\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184]/g},{base:"d",letters:/[\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A]/g},{base:"dz",letters:/[\u01F3\u01C6]/g},{base:"e",letters:/[\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD]/g},{base:"f",letters:/[\u0066\u24D5\uFF46\u1E1F\u0192\uA77C]/g},{base:"g",letters:/[\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F]/g},{base:"h",letters:/[\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265]/g},{base:"hv",letters:/[\u0195]/g},{base:"i",letters:/[\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131]/g},{base:"j",letters:/[\u006A\u24D9\uFF4A\u0135\u01F0\u0249]/g},{base:"k",letters:/[\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3]/g},{base:"l",letters:/[\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747]/g},{base:"lj",letters:/[\u01C9]/g},{base:"m",letters:/[\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F]/g},{base:"n",letters:/[\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5]/g},{base:"nj",letters:/[\u01CC]/g},{base:"o",letters:/[\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275]/g},{base:"oi",letters:/[\u01A3]/g},{base:"ou",letters:/[\u0223]/g},{base:"oo",letters:/[\uA74F]/g},{base:"p",letters:/[\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755]/g},{base:"q",letters:/[\u0071\u24E0\uFF51\u024B\uA757\uA759]/g},{base:"r",letters:/[\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783]/g},{base:"s",letters:/[\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B]/g},{base:"t",letters:/[\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787]/g},{base:"tz",letters:/[\uA729]/g},{base:"u",letters:/[\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289]/g},{base:"v",letters:/[\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C]/g},{base:"vy",letters:/[\uA761]/g},{base:"w",letters:/[\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73]/g},{base:"x",letters:/[\u0078\u24E7\uFF58\u1E8B\u1E8D]/g},{base:"y",letters:/[\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF]/g},{base:"z",letters:/[\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763]/g}];t.exports=function(e){for(var t=0;t -*/ -},{"./components/Contributors":2,"./components/CustomComponents":3,"./components/CustomRender":4,"./components/GithubUsers":5,"./components/GroupedOptionsField":6,"./components/Multiselect":7,"./components/NumericSelect":8,"./components/States":9,"./components/Virtualized":10,"react":undefined,"react-dom":undefined,"react-select-plus":undefined}],2:[function(require,module,exports){ +},{"./components/Contributors":2,"./components/Creatable":3,"./components/CustomComponents":4,"./components/CustomRender":5,"./components/GithubUsers":6,"./components/GroupedOptionsField":7,"./components/Multiselect":8,"./components/NumericSelect":9,"./components/States":10,"./components/Virtualized":11,"react":undefined,"react-dom":undefined,"react-select-plus":undefined}],2:[function(require,module,exports){ 'use strict'; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } @@ -176,7 +181,115 @@ var Contributors = _react2['default'].createClass({ module.exports = Contributors; -},{"../data/contributors":12,"react":undefined,"react-select-plus":undefined}],3:[function(require,module,exports){ +},{"../data/contributors":13,"react":undefined,"react-select-plus":undefined}],3:[function(require,module,exports){ +'use strict'; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _reactSelectPlus = require('react-select-plus'); + +var _reactSelectPlus2 = _interopRequireDefault(_reactSelectPlus); + +var CreatableDemo = _react2['default'].createClass({ + displayName: 'CreatableDemo', + propTypes: { + hint: _react2['default'].PropTypes.string, + label: _react2['default'].PropTypes.string + }, + getInitialState: function getInitialState() { + return { + multi: true, + multiValue: [], + options: [{ value: 'R', label: 'Red' }, { value: 'G', label: 'Green' }, { value: 'B', label: 'Blue' }], + value: undefined + }; + }, + handleOnChange: function handleOnChange(value) { + var multi = this.state.multi; + + if (multi) { + this.setState({ multiValue: value }); + } else { + this.setState({ value: value }); + } + }, + render: function render() { + var _this = this; + + var _state = this.state; + var multi = _state.multi; + var multiValue = _state.multiValue; + var options = _state.options; + var value = _state.value; + + return _react2['default'].createElement( + 'div', + { className: 'section' }, + _react2['default'].createElement( + 'h3', + { className: 'section-heading' }, + this.props.label + ), + _react2['default'].createElement(_reactSelectPlus2['default'].Creatable, { + multi: multi, + options: options, + onChange: this.handleOnChange, + value: multi ? multiValue : value + }), + _react2['default'].createElement( + 'div', + { className: 'hint' }, + this.props.hint + ), + _react2['default'].createElement( + 'div', + { className: 'checkbox-list' }, + _react2['default'].createElement( + 'label', + { className: 'checkbox' }, + _react2['default'].createElement('input', { + type: 'radio', + className: 'checkbox-control', + checked: multi, + onChange: function () { + return _this.setState({ multi: true }); + } + }), + _react2['default'].createElement( + 'span', + { className: 'checkbox-label' }, + 'Multiselect' + ) + ), + _react2['default'].createElement( + 'label', + { className: 'checkbox' }, + _react2['default'].createElement('input', { + type: 'radio', + className: 'checkbox-control', + checked: !multi, + onChange: function () { + return _this.setState({ multi: false }); + } + }), + _react2['default'].createElement( + 'span', + { className: 'checkbox-label' }, + 'Single Value' + ) + ) + ) + ); + } +}); + +module.exports = CreatableDemo; + +},{"react":undefined,"react-select-plus":undefined}],4:[function(require,module,exports){ 'use strict'; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } @@ -320,7 +433,7 @@ var UsersField = _react2['default'].createClass({ module.exports = UsersField; -},{"../data/users":14,"react":undefined,"react-gravatar":20,"react-select-plus":undefined}],4:[function(require,module,exports){ +},{"../data/users":15,"react":undefined,"react-gravatar":20,"react-select-plus":undefined}],5:[function(require,module,exports){ 'use strict'; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } @@ -396,7 +509,7 @@ var DisabledUpsellOptions = _react2['default'].createClass({ }); module.exports = DisabledUpsellOptions; -},{"react":undefined,"react-select-plus":undefined}],5:[function(require,module,exports){ +},{"react":undefined,"react-select-plus":undefined}],6:[function(require,module,exports){ 'use strict'; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } @@ -495,7 +608,7 @@ var GithubUsers = _react2['default'].createClass({ module.exports = GithubUsers; -},{"isomorphic-fetch":16,"react":undefined,"react-select-plus":undefined}],6:[function(require,module,exports){ +},{"isomorphic-fetch":16,"react":undefined,"react-select-plus":undefined}],7:[function(require,module,exports){ 'use strict'; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } @@ -588,7 +701,7 @@ var GroupedOptionsField = _react2['default'].createClass({ module.exports = GroupedOptionsField; -},{"react":undefined,"react-select-plus":undefined}],7:[function(require,module,exports){ +},{"react":undefined,"react-select-plus":undefined}],8:[function(require,module,exports){ 'use strict'; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } @@ -672,7 +785,7 @@ var MultiSelectField = _react2['default'].createClass({ module.exports = MultiSelectField; -},{"react":undefined,"react-select-plus":undefined}],8:[function(require,module,exports){ +},{"react":undefined,"react-select-plus":undefined}],9:[function(require,module,exports){ 'use strict'; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } @@ -804,7 +917,7 @@ var ValuesAsNumbersField = _react2['default'].createClass({ module.exports = ValuesAsNumbersField; -},{"react":undefined,"react-select-plus":undefined}],9:[function(require,module,exports){ +},{"react":undefined,"react-select-plus":undefined}],10:[function(require,module,exports){ 'use strict'; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } @@ -942,7 +1055,7 @@ var StatesField = _react2['default'].createClass({ module.exports = StatesField; -},{"../data/states":13,"react":undefined,"react-select-plus":undefined}],10:[function(require,module,exports){ +},{"../data/states":14,"react":undefined,"react-select-plus":undefined}],11:[function(require,module,exports){ 'use strict'; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } @@ -1011,50 +1124,28 @@ var CitiesField = _react2['default'].createClass({ module.exports = CitiesField; -},{"../data/cities":11,"react":undefined,"react-virtualized-select":31}],11:[function(require,module,exports){ +},{"../data/cities":12,"react":undefined,"react-virtualized-select":31}],12:[function(require,module,exports){ 'use strict'; exports.CITIES = [{ name: 'Abilene' }, { name: 'Addison' }, { name: 'Akron' }, { name: 'Alameda' }, { name: 'Albany' }, { name: 'Albany' }, { name: 'Albany' }, { name: 'Albuquerque' }, { name: 'Alexandria' }, { name: 'Alexandria' }, { name: 'Alhambra' }, { name: 'Aliso Viejo' }, { name: 'Allen' }, { name: 'Allentown' }, { name: 'Alpharetta' }, { name: 'Altamonte Springs' }, { name: 'Altoona' }, { name: 'Amarillo' }, { name: 'Ames' }, { name: 'Anaheim' }, { name: 'Anchorage' }, { name: 'Anderson' }, { name: 'Ankeny' }, { name: 'Ann Arbor' }, { name: 'Annapolis' }, { name: 'Antioch' }, { name: 'Apache Junction' }, { name: 'Apex' }, { name: 'Apopka' }, { name: 'Apple Valley' }, { name: 'Apple Valley' }, { name: 'Appleton' }, { name: 'Arcadia' }, { name: 'Arlington' }, { name: 'Arlington Heights' }, { name: 'Arvada' }, { name: 'Asheville' }, { name: 'Athens-Clarke County' }, { name: 'Atlanta' }, { name: 'Atlantic City' }, { name: 'Attleboro' }, { name: 'Auburn' }, { name: 'Auburn' }, { name: 'Augusta-Richmond County' }, { name: 'Aurora' }, { name: 'Aurora' }, { name: 'Austin' }, { name: 'Aventura' }, { name: 'Avondale' }, { name: 'Azusa' }, { name: 'Bakersfield' }, { name: 'Baldwin Park' }, { name: 'Baltimore' }, { name: 'Barnstable Town' }, { name: 'Bartlett' }, { name: 'Bartlett' }, { name: 'Baton Rouge' }, { name: 'Battle Creek' }, { name: 'Bayonne' }, { name: 'Baytown' }, { name: 'Beaumont' }, { name: 'Beaumont' }, { name: 'Beavercreek' }, { name: 'Beaverton' }, { name: 'Bedford' }, { name: 'Bell Gardens' }, { name: 'Belleville' }, { name: 'Bellevue' }, { name: 'Bellevue' }, { name: 'Bellflower' }, { name: 'Bellingham' }, { name: 'Beloit' }, { name: 'Bend' }, { name: 'Bentonville' }, { name: 'Berkeley' }, { name: 'Berwyn' }, { name: 'Bethlehem' }, { name: 'Beverly' }, { name: 'Billings' }, { name: 'Biloxi' }, { name: 'Binghamton' }, { name: 'Birmingham' }, { name: 'Bismarck' }, { name: 'Blacksburg' }, { name: 'Blaine' }, { name: 'Bloomington' }, { name: 'Bloomington' }, { name: 'Bloomington' }, { name: 'Blue Springs' }, { name: 'Boca Raton' }, { name: 'Boise City' }, { name: 'Bolingbrook' }, { name: 'Bonita Springs' }, { name: 'Bossier City' }, { name: 'Boston' }, { name: 'Boulder' }, { name: 'Bountiful' }, { name: 'Bowie' }, { name: 'Bowling Green' }, { name: 'Boynton Beach' }, { name: 'Bozeman' }, { name: 'Bradenton' }, { name: 'Brea' }, { name: 'Bremerton' }, { name: 'Brentwood' }, { name: 'Brentwood' }, { name: 'Bridgeport' }, { name: 'Bristol' }, { name: 'Brockton' }, { name: 'Broken Arrow' }, { name: 'Brookfield' }, { name: 'Brookhaven' }, { name: 'Brooklyn Park' }, { name: 'Broomfield' }, { name: 'Brownsville' }, { name: 'Bryan' }, { name: 'Buckeye' }, { name: 'Buena Park' }, { name: 'Buffalo' }, { name: 'Buffalo Grove' }, { name: 'Bullhead City' }, { name: 'Burbank' }, { name: 'Burien' }, { name: 'Burleson' }, { name: 'Burlington' }, { name: 'Burlington' }, { name: 'Burnsville' }, { name: 'Caldwell' }, { name: 'Calexico' }, { name: 'Calumet City' }, { name: 'Camarillo' }, { name: 'Cambridge' }, { name: 'Camden' }, { name: 'Campbell' }, { name: 'Canton' }, { name: 'Cape Coral' }, { name: 'Cape Girardeau' }, { name: 'Carlsbad' }, { name: 'Carmel' }, { name: 'Carol Stream' }, { name: 'Carpentersville' }, { name: 'Carrollton' }, { name: 'Carson' }, { name: 'Carson City' }, { name: 'Cary' }, { name: 'Casa Grande' }, { name: 'Casper' }, { name: 'Castle Rock' }, { name: 'Cathedral City' }, { name: 'Cedar Falls' }, { name: 'Cedar Hill' }, { name: 'Cedar Park' }, { name: 'Cedar Rapids' }, { name: 'Centennial' }, { name: 'Ceres' }, { name: 'Cerritos' }, { name: 'Champaign' }, { name: 'Chandler' }, { name: 'Chapel Hill' }, { name: 'Charleston' }, { name: 'Charleston' }, { name: 'Charlotte' }, { name: 'Charlottesville' }, { name: 'Chattanooga' }, { name: 'Chelsea' }, { name: 'Chesapeake' }, { name: 'Chesterfield' }, { name: 'Cheyenne' }, { name: 'Chicago' }, { name: 'Chico' }, { name: 'Chicopee' }, { name: 'Chino' }, { name: 'Chino Hills' }, { name: 'Chula Vista' }, { name: 'Cicero' }, { name: 'Cincinnati' }, { name: 'Citrus Heights' }, { name: 'Clarksville' }, { name: 'Clearwater' }, { name: 'Cleveland' }, { name: 'Cleveland' }, { name: 'Cleveland Heights' }, { name: 'Clifton' }, { name: 'Clovis' }, { name: 'Clovis' }, { name: 'Coachella' }, { name: 'Coconut Creek' }, { name: 'Coeur d\'Alene' }, { name: 'College Station' }, { name: 'Collierville' }, { name: 'Colorado Springs' }, { name: 'Colton' }, { name: 'Columbia' }, { name: 'Columbia' }, { name: 'Columbus' }, { name: 'Columbus' }, { name: 'Columbus' }, { name: 'Commerce City' }, { name: 'Compton' }, { name: 'Concord' }, { name: 'Concord' }, { name: 'Concord' }, { name: 'Conroe' }, { name: 'Conway' }, { name: 'Coon Rapids' }, { name: 'Coppell' }, { name: 'Coral Gables' }, { name: 'Coral Springs' }, { name: 'Corona' }, { name: 'Corpus Christi' }, { name: 'Corvallis' }, { name: 'Costa Mesa' }, { name: 'Council Bluffs' }, { name: 'Covina' }, { name: 'Covington' }, { name: 'Cranston' }, { name: 'Crystal Lake' }, { name: 'Culver City' }, { name: 'Cupertino' }, { name: 'Cutler Bay' }, { name: 'Cuyahoga Falls' }, { name: 'Cypress' }, { name: 'Dallas' }, { name: 'Daly City' }, { name: 'Danbury' }, { name: 'Danville' }, { name: 'Danville' }, { name: 'Davenport' }, { name: 'Davie' }, { name: 'Davis' }, { name: 'Dayton' }, { name: 'Daytona Beach' }, { name: 'DeKalb' }, { name: 'DeSoto' }, { name: 'Dearborn' }, { name: 'Dearborn Heights' }, { name: 'Decatur' }, { name: 'Decatur' }, { name: 'Deerfield Beach' }, { name: 'Delano' }, { name: 'Delray Beach' }, { name: 'Deltona' }, { name: 'Denton' }, { name: 'Denver' }, { name: 'Des Moines' }, { name: 'Des Plaines' }, { name: 'Detroit' }, { name: 'Diamond Bar' }, { name: 'Doral' }, { name: 'Dothan' }, { name: 'Dover' }, { name: 'Downers Grove' }, { name: 'Downey' }, { name: 'Draper' }, { name: 'Dublin' }, { name: 'Dublin' }, { name: 'Dubuque' }, { name: 'Duluth' }, { name: 'Duncanville' }, { name: 'Dunwoody' }, { name: 'Durham' }, { name: 'Eagan' }, { name: 'East Lansing' }, { name: 'East Orange' }, { name: 'East Providence' }, { name: 'Eastvale' }, { name: 'Eau Claire' }, { name: 'Eden Prairie' }, { name: 'Edina' }, { name: 'Edinburg' }, { name: 'Edmond' }, { name: 'Edmonds' }, { name: 'El Cajon' }, { name: 'El Centro' }, { name: 'El Monte' }, { name: 'El Paso' }, { name: 'Elgin' }, { name: 'Elizabeth' }, { name: 'Elk Grove' }, { name: 'Elkhart' }, { name: 'Elmhurst' }, { name: 'Elyria' }, { name: 'Encinitas' }, { name: 'Enid' }, { name: 'Erie' }, { name: 'Escondido' }, { name: 'Euclid' }, { name: 'Eugene' }, { name: 'Euless' }, { name: 'Evanston' }, { name: 'Evansville' }, { name: 'Everett' }, { name: 'Everett' }, { name: 'Fairfield' }, { name: 'Fairfield' }, { name: 'Fall River' }, { name: 'Fargo' }, { name: 'Farmington' }, { name: 'Farmington Hills' }, { name: 'Fayetteville' }, { name: 'Fayetteville' }, { name: 'Federal Way' }, { name: 'Findlay' }, { name: 'Fishers' }, { name: 'Fitchburg' }, { name: 'Flagstaff' }, { name: 'Flint' }, { name: 'Florence' }, { name: 'Florence' }, { name: 'Florissant' }, { name: 'Flower Mound' }, { name: 'Folsom' }, { name: 'Fond du Lac' }, { name: 'Fontana' }, { name: 'Fort Collins' }, { name: 'Fort Lauderdale' }, { name: 'Fort Myers' }, { name: 'Fort Pierce' }, { name: 'Fort Smith' }, { name: 'Fort Wayne' }, { name: 'Fort Worth' }, { name: 'Fountain Valley' }, { name: 'Franklin' }, { name: 'Frederick' }, { name: 'Freeport' }, { name: 'Fremont' }, { name: 'Fresno' }, { name: 'Friendswood' }, { name: 'Frisco' }, { name: 'Fullerton' }, { name: 'Gainesville' }, { name: 'Gaithersburg' }, { name: 'Galveston' }, { name: 'Garden Grove' }, { name: 'Gardena' }, { name: 'Garland' }, { name: 'Gary' }, { name: 'Gastonia' }, { name: 'Georgetown' }, { name: 'Germantown' }, { name: 'Gilbert' }, { name: 'Gilroy' }, { name: 'Glendale' }, { name: 'Glendale' }, { name: 'Glendora' }, { name: 'Glenview' }, { name: 'Goodyear' }, { name: 'Goose Creek' }, { name: 'Grand Forks' }, { name: 'Grand Island' }, { name: 'Grand Junction' }, { name: 'Grand Prairie' }, { name: 'Grand Rapids' }, { name: 'Grapevine' }, { name: 'Great Falls' }, { name: 'Greeley' }, { name: 'Green Bay' }, { name: 'Greenacres' }, { name: 'Greenfield' }, { name: 'Greensboro' }, { name: 'Greenville' }, { name: 'Greenville' }, { name: 'Greenwood' }, { name: 'Gresham' }, { name: 'Grove City' }, { name: 'Gulfport' }, { name: 'Hackensack' }, { name: 'Hagerstown' }, { name: 'Hallandale Beach' }, { name: 'Haltom City' }, { name: 'Hamilton' }, { name: 'Hammond' }, { name: 'Hampton' }, { name: 'Hanford' }, { name: 'Hanover Park' }, { name: 'Harlingen' }, { name: 'Harrisburg' }, { name: 'Harrisonburg' }, { name: 'Hartford' }, { name: 'Hattiesburg' }, { name: 'Haverhill' }, { name: 'Hawthorne' }, { name: 'Hayward' }, { name: 'Hemet' }, { name: 'Hempstead' }, { name: 'Henderson' }, { name: 'Hendersonville' }, { name: 'Hesperia' }, { name: 'Hialeah' }, { name: 'Hickory' }, { name: 'High Point' }, { name: 'Highland' }, { name: 'Hillsboro' }, { name: 'Hilton Head Island' }, { name: 'Hoboken' }, { name: 'Hoffman Estates' }, { name: 'Hollywood' }, { name: 'Holyoke' }, { name: 'Homestead' }, { name: 'Honolulu' }, { name: 'Hoover' }, { name: 'Houston' }, { name: 'Huber Heights' }, { name: 'Huntersville' }, { name: 'Huntington' }, { name: 'Huntington Beach' }, { name: 'Huntington Park' }, { name: 'Huntsville' }, { name: 'Huntsville' }, { name: 'Hurst' }, { name: 'Hutchinson' }, { name: 'Idaho Falls' }, { name: 'Independence' }, { name: 'Indianapolis' }, { name: 'Indio' }, { name: 'Inglewood' }, { name: 'Iowa City' }, { name: 'Irvine' }, { name: 'Irving' }, { name: 'Jackson' }, { name: 'Jackson' }, { name: 'Jacksonville' }, { name: 'Jacksonville' }, { name: 'Janesville' }, { name: 'Jefferson City' }, { name: 'Jeffersonville' }, { name: 'Jersey City' }, { name: 'Johns Creek' }, { name: 'Johnson City' }, { name: 'Joliet' }, { name: 'Jonesboro' }, { name: 'Joplin' }, { name: 'Jupiter' }, { name: 'Jurupa Valley' }, { name: 'Kalamazoo' }, { name: 'Kannapolis' }, { name: 'Kansas City' }, { name: 'Kansas City' }, { name: 'Kearny' }, { name: 'Keizer' }, { name: 'Keller' }, { name: 'Kenner' }, { name: 'Kennewick' }, { name: 'Kenosha' }, { name: 'Kent' }, { name: 'Kentwood' }, { name: 'Kettering' }, { name: 'Killeen' }, { name: 'Kingsport' }, { name: 'Kirkland' }, { name: 'Kissimmee' }, { name: 'Knoxville' }, { name: 'Kokomo' }, { name: 'La Crosse' }, { name: 'La Habra' }, { name: 'La Mesa' }, { name: 'La Mirada' }, { name: 'La Puente' }, { name: 'La Quinta' }, { name: 'Lacey' }, { name: 'Lafayette' }, { name: 'Lafayette' }, { name: 'Laguna Niguel' }, { name: 'Lake Charles' }, { name: 'Lake Elsinore' }, { name: 'Lake Forest' }, { name: 'Lake Havasu City' }, { name: 'Lake Oswego' }, { name: 'Lakeland' }, { name: 'Lakeville' }, { name: 'Lakewood' }, { name: 'Lakewood' }, { name: 'Lakewood' }, { name: 'Lakewood' }, { name: 'Lancaster' }, { name: 'Lancaster' }, { name: 'Lancaster' }, { name: 'Lancaster' }, { name: 'Lansing' }, { name: 'Laredo' }, { name: 'Largo' }, { name: 'Las Cruces' }, { name: 'Las Vegas' }, { name: 'Lauderhill' }, { name: 'Lawrence' }, { name: 'Lawrence' }, { name: 'Lawrence' }, { name: 'Lawton' }, { name: 'Layton' }, { name: 'League City' }, { name: 'Lee\'s Summit' }, { name: 'Leesburg' }, { name: 'Lehi' }, { name: 'Lenexa' }, { name: 'Leominster' }, { name: 'Lewisville' }, { name: 'Lexington-Fayette' }, { name: 'Lima' }, { name: 'Lincoln' }, { name: 'Lincoln' }, { name: 'Lincoln Park' }, { name: 'Linden' }, { name: 'Little Rock' }, { name: 'Littleton' }, { name: 'Livermore' }, { name: 'Livonia' }, { name: 'Lodi' }, { name: 'Logan' }, { name: 'Lombard' }, { name: 'Lompoc' }, { name: 'Long Beach' }, { name: 'Longmont' }, { name: 'Longview' }, { name: 'Lorain' }, { name: 'Los Angeles' }, { name: 'Louisville/Jefferson County' }, { name: 'Loveland' }, { name: 'Lowell' }, { name: 'Lubbock' }, { name: 'Lynchburg' }, { name: 'Lynn' }, { name: 'Lynwood' }, { name: 'Macon' }, { name: 'Madera' }, { name: 'Madison' }, { name: 'Madison' }, { name: 'Malden' }, { name: 'Manassas' }, { name: 'Manchester' }, { name: 'Manhattan' }, { name: 'Mankato' }, { name: 'Mansfield' }, { name: 'Mansfield' }, { name: 'Manteca' }, { name: 'Maple Grove' }, { name: 'Maplewood' }, { name: 'Marana' }, { name: 'Margate' }, { name: 'Maricopa' }, { name: 'Marietta' }, { name: 'Marlborough' }, { name: 'Martinez' }, { name: 'Marysville' }, { name: 'McAllen' }, { name: 'McKinney' }, { name: 'Medford' }, { name: 'Medford' }, { name: 'Melbourne' }, { name: 'Memphis' }, { name: 'Menifee' }, { name: 'Mentor' }, { name: 'Merced' }, { name: 'Meriden' }, { name: 'Meridian' }, { name: 'Meridian' }, { name: 'Mesa' }, { name: 'Mesquite' }, { name: 'Methuen' }, { name: 'Miami' }, { name: 'Miami Beach' }, { name: 'Miami Gardens' }, { name: 'Middletown' }, { name: 'Middletown' }, { name: 'Midland' }, { name: 'Midland' }, { name: 'Midwest City' }, { name: 'Milford' }, { name: 'Milpitas' }, { name: 'Milwaukee' }, { name: 'Minneapolis' }, { name: 'Minnetonka' }, { name: 'Minot' }, { name: 'Miramar' }, { name: 'Mishawaka' }, { name: 'Mission' }, { name: 'Mission Viejo' }, { name: 'Missoula' }, { name: 'Missouri City' }, { name: 'Mobile' }, { name: 'Modesto' }, { name: 'Moline' }, { name: 'Monroe' }, { name: 'Monrovia' }, { name: 'Montclair' }, { name: 'Montebello' }, { name: 'Monterey Park' }, { name: 'Montgomery' }, { name: 'Moore' }, { name: 'Moorhead' }, { name: 'Moreno Valley' }, { name: 'Morgan Hill' }, { name: 'Mount Pleasant' }, { name: 'Mount Prospect' }, { name: 'Mount Vernon' }, { name: 'Mountain View' }, { name: 'Muncie' }, { name: 'Murfreesboro' }, { name: 'Murray' }, { name: 'Murrieta' }, { name: 'Muskegon' }, { name: 'Muskogee' }, { name: 'Nampa' }, { name: 'Napa' }, { name: 'Naperville' }, { name: 'Nashua' }, { name: 'Nashville-Davidson' }, { name: 'National City' }, { name: 'New Bedford' }, { name: 'New Berlin' }, { name: 'New Braunfels' }, { name: 'New Britain' }, { name: 'New Brunswick' }, { name: 'New Haven' }, { name: 'New Orleans' }, { name: 'New Rochelle' }, { name: 'New York' }, { name: 'Newark' }, { name: 'Newark' }, { name: 'Newark' }, { name: 'Newport Beach' }, { name: 'Newport News' }, { name: 'Newton' }, { name: 'Niagara Falls' }, { name: 'Noblesville' }, { name: 'Norfolk' }, { name: 'Normal' }, { name: 'Norman' }, { name: 'North Charleston' }, { name: 'North Las Vegas' }, { name: 'North Lauderdale' }, { name: 'North Little Rock' }, { name: 'North Miami' }, { name: 'North Miami Beach' }, { name: 'North Port' }, { name: 'North Richland Hills' }, { name: 'Northglenn' }, { name: 'Norwalk' }, { name: 'Norwalk' }, { name: 'Norwich' }, { name: 'Novato' }, { name: 'Novi' }, { name: 'O\'Fallon' }, { name: 'Oak Lawn' }, { name: 'Oak Park' }, { name: 'Oakland' }, { name: 'Oakland Park' }, { name: 'Oakley' }, { name: 'Ocala' }, { name: 'Oceanside' }, { name: 'Ocoee' }, { name: 'Odessa' }, { name: 'Ogden' }, { name: 'Oklahoma City' }, { name: 'Olathe' }, { name: 'Olympia' }, { name: 'Omaha' }, { name: 'Ontario' }, { name: 'Orange' }, { name: 'Orem' }, { name: 'Orland Park' }, { name: 'Orlando' }, { name: 'Ormond Beach' }, { name: 'Oro Valley' }, { name: 'Oshkosh' }, { name: 'Overland Park' }, { name: 'Owensboro' }, { name: 'Oxnard' }, { name: 'Pacifica' }, { name: 'Palatine' }, { name: 'Palm Bay' }, { name: 'Palm Beach Gardens' }, { name: 'Palm Coast' }, { name: 'Palm Desert' }, { name: 'Palm Springs' }, { name: 'Palmdale' }, { name: 'Palo Alto' }, { name: 'Panama City' }, { name: 'Paramount' }, { name: 'Park Ridge' }, { name: 'Parker' }, { name: 'Parma' }, { name: 'Pasadena' }, { name: 'Pasadena' }, { name: 'Pasco' }, { name: 'Passaic' }, { name: 'Paterson' }, { name: 'Pawtucket' }, { name: 'Peabody' }, { name: 'Peachtree Corners' }, { name: 'Pearland' }, { name: 'Pembroke Pines' }, { name: 'Pensacola' }, { name: 'Peoria' }, { name: 'Peoria' }, { name: 'Perris' }, { name: 'Perth Amboy' }, { name: 'Petaluma' }, { name: 'Pflugerville' }, { name: 'Pharr' }, { name: 'Phenix City' }, { name: 'Philadelphia' }, { name: 'Phoenix' }, { name: 'Pico Rivera' }, { name: 'Pine Bluff' }, { name: 'Pinellas Park' }, { name: 'Pittsburg' }, { name: 'Pittsburgh' }, { name: 'Pittsfield' }, { name: 'Placentia' }, { name: 'Plainfield' }, { name: 'Plainfield' }, { name: 'Plano' }, { name: 'Plantation' }, { name: 'Pleasanton' }, { name: 'Plymouth' }, { name: 'Pocatello' }, { name: 'Pomona' }, { name: 'Pompano Beach' }, { name: 'Pontiac' }, { name: 'Port Arthur' }, { name: 'Port Orange' }, { name: 'Port St. Lucie' }, { name: 'Portage' }, { name: 'Porterville' }, { name: 'Portland' }, { name: 'Portland' }, { name: 'Portsmouth' }, { name: 'Poway' }, { name: 'Prescott' }, { name: 'Prescott Valley' }, { name: 'Providence' }, { name: 'Provo' }, { name: 'Pueblo' }, { name: 'Puyallup' }, { name: 'Quincy' }, { name: 'Quincy' }, { name: 'Racine' }, { name: 'Raleigh' }, { name: 'Rancho Cordova' }, { name: 'Rancho Cucamonga' }, { name: 'Rancho Palos Verdes' }, { name: 'Rancho Santa Margarita' }, { name: 'Rapid City' }, { name: 'Reading' }, { name: 'Redding' }, { name: 'Redlands' }, { name: 'Redmond' }, { name: 'Redondo Beach' }, { name: 'Redwood City' }, { name: 'Reno' }, { name: 'Renton' }, { name: 'Revere' }, { name: 'Rialto' }, { name: 'Richardson' }, { name: 'Richland' }, { name: 'Richmond' }, { name: 'Richmond' }, { name: 'Rio Rancho' }, { name: 'Riverside' }, { name: 'Riverton' }, { name: 'Roanoke' }, { name: 'Rochester' }, { name: 'Rochester' }, { name: 'Rochester Hills' }, { name: 'Rock Hill' }, { name: 'Rock Island' }, { name: 'Rockford' }, { name: 'Rocklin' }, { name: 'Rockville' }, { name: 'Rockwall' }, { name: 'Rocky Mount' }, { name: 'Rogers' }, { name: 'Rohnert Park' }, { name: 'Romeoville' }, { name: 'Rosemead' }, { name: 'Roseville' }, { name: 'Roseville' }, { name: 'Roswell' }, { name: 'Roswell' }, { name: 'Round Rock' }, { name: 'Rowlett' }, { name: 'Roy' }, { name: 'Royal Oak' }, { name: 'Sacramento' }, { name: 'Saginaw' }, { name: 'Salem' }, { name: 'Salem' }, { name: 'Salina' }, { name: 'Salinas' }, { name: 'Salt Lake City' }, { name: 'Sammamish' }, { name: 'San Angelo' }, { name: 'San Antonio' }, { name: 'San Bernardino' }, { name: 'San Bruno' }, { name: 'San Buenaventura (Ventura)' }, { name: 'San Clemente' }, { name: 'San Diego' }, { name: 'San Francisco' }, { name: 'San Gabriel' }, { name: 'San Jacinto' }, { name: 'San Jose' }, { name: 'San Leandro' }, { name: 'San Luis Obispo' }, { name: 'San Marcos' }, { name: 'San Marcos' }, { name: 'San Mateo' }, { name: 'San Rafael' }, { name: 'San Ramon' }, { name: 'Sandy' }, { name: 'Sandy Springs' }, { name: 'Sanford' }, { name: 'Santa Ana' }, { name: 'Santa Barbara' }, { name: 'Santa Clara' }, { name: 'Santa Clarita' }, { name: 'Santa Cruz' }, { name: 'Santa Fe' }, { name: 'Santa Maria' }, { name: 'Santa Monica' }, { name: 'Santa Rosa' }, { name: 'Santee' }, { name: 'Sarasota' }, { name: 'Savannah' }, { name: 'Sayreville' }, { name: 'Schaumburg' }, { name: 'Schenectady' }, { name: 'Scottsdale' }, { name: 'Scranton' }, { name: 'Seattle' }, { name: 'Shakopee' }, { name: 'Shawnee' }, { name: 'Sheboygan' }, { name: 'Shelton' }, { name: 'Sherman' }, { name: 'Shoreline' }, { name: 'Shreveport' }, { name: 'Sierra Vista' }, { name: 'Simi Valley' }, { name: 'Sioux City' }, { name: 'Sioux Falls' }, { name: 'Skokie' }, { name: 'Smyrna' }, { name: 'Smyrna' }, { name: 'Somerville' }, { name: 'South Bend' }, { name: 'South Gate' }, { name: 'South Jordan' }, { name: 'South San Francisco' }, { name: 'Southaven' }, { name: 'Southfield' }, { name: 'Spanish Fork' }, { name: 'Sparks' }, { name: 'Spartanburg' }, { name: 'Spokane' }, { name: 'Spokane Valley' }, { name: 'Springdale' }, { name: 'Springfield' }, { name: 'Springfield' }, { name: 'Springfield' }, { name: 'Springfield' }, { name: 'Springfield' }, { name: 'St. Charles' }, { name: 'St. Clair Shores' }, { name: 'St. Cloud' }, { name: 'St. Cloud' }, { name: 'St. George' }, { name: 'St. Joseph' }, { name: 'St. Louis' }, { name: 'St. Louis Park' }, { name: 'St. Paul' }, { name: 'St. Peters' }, { name: 'St. Petersburg' }, { name: 'Stamford' }, { name: 'Stanton' }, { name: 'State College' }, { name: 'Sterling Heights' }, { name: 'Stillwater' }, { name: 'Stockton' }, { name: 'Streamwood' }, { name: 'Strongsville' }, { name: 'Suffolk' }, { name: 'Sugar Land' }, { name: 'Summerville' }, { name: 'Sumter' }, { name: 'Sunnyvale' }, { name: 'Sunrise' }, { name: 'Surprise' }, { name: 'Syracuse' }, { name: 'Tacoma' }, { name: 'Tallahassee' }, { name: 'Tamarac' }, { name: 'Tampa' }, { name: 'Taunton' }, { name: 'Taylor' }, { name: 'Taylorsville' }, { name: 'Temecula' }, { name: 'Tempe' }, { name: 'Temple' }, { name: 'Terre Haute' }, { name: 'Texarkana' }, { name: 'Texas City' }, { name: 'The Colony' }, { name: 'Thornton' }, { name: 'Thousand Oaks' }, { name: 'Tigard' }, { name: 'Tinley Park' }, { name: 'Titusville' }, { name: 'Toledo' }, { name: 'Topeka' }, { name: 'Torrance' }, { name: 'Tracy' }, { name: 'Trenton' }, { name: 'Troy' }, { name: 'Troy' }, { name: 'Tucson' }, { name: 'Tulare' }, { name: 'Tulsa' }, { name: 'Turlock' }, { name: 'Tuscaloosa' }, { name: 'Tustin' }, { name: 'Twin Falls' }, { name: 'Tyler' }, { name: 'Union City' }, { name: 'Union City' }, { name: 'Upland' }, { name: 'Urbana' }, { name: 'Urbandale' }, { name: 'Utica' }, { name: 'Vacaville' }, { name: 'Valdosta' }, { name: 'Vallejo' }, { name: 'Valley Stream' }, { name: 'Vancouver' }, { name: 'Victoria' }, { name: 'Victorville' }, { name: 'Vineland' }, { name: 'Virginia Beach' }, { name: 'Visalia' }, { name: 'Vista' }, { name: 'Waco' }, { name: 'Walnut Creek' }, { name: 'Waltham' }, { name: 'Warner Robins' }, { name: 'Warren' }, { name: 'Warren' }, { name: 'Warwick' }, { name: 'Washington' }, { name: 'Waterbury' }, { name: 'Waterloo' }, { name: 'Watsonville' }, { name: 'Waukegan' }, { name: 'Waukesha' }, { name: 'Wausau' }, { name: 'Wauwatosa' }, { name: 'Wellington' }, { name: 'Weslaco' }, { name: 'West Allis' }, { name: 'West Covina' }, { name: 'West Des Moines' }, { name: 'West Haven' }, { name: 'West Jordan' }, { name: 'West New York' }, { name: 'West Palm Beach' }, { name: 'West Sacramento' }, { name: 'West Valley City' }, { name: 'Westerville' }, { name: 'Westfield' }, { name: 'Westland' }, { name: 'Westminster' }, { name: 'Westminster' }, { name: 'Weston' }, { name: 'Weymouth Town' }, { name: 'Wheaton' }, { name: 'Wheeling' }, { name: 'White Plains' }, { name: 'Whittier' }, { name: 'Wichita' }, { name: 'Wichita Falls' }, { name: 'Wilkes-Barre' }, { name: 'Wilmington' }, { name: 'Wilmington' }, { name: 'Wilson' }, { name: 'Winston-Salem' }, { name: 'Winter Garden' }, { name: 'Woburn' }, { name: 'Woodbury' }, { name: 'Woodland' }, { name: 'Woonsocket' }, { name: 'Worcester' }, { name: 'Wylie' }, { name: 'Wyoming' }, { name: 'Yakima' }, { name: 'Yonkers' }, { name: 'Yorba Linda' }, { name: 'York' }, { name: 'Youngstown' }, { name: 'Yuba City' }, { name: 'Yucaipa' }, { name: 'Yuma' }]; -},{}],12:[function(require,module,exports){ +},{}],13:[function(require,module,exports){ 'use strict'; module.exports = [{ github: 'jedwatson', name: 'Jed Watson' }, { github: 'bruderstein', name: 'Dave Brotherstone' }, { github: 'jossmac', name: 'Joss Mackison' }, { github: 'jniechcial', name: 'Jakub NiechciaÅ‚' }, { github: 'craigdallimore', name: 'Craig Dallimore' }, { github: 'julen', name: 'Julen Ruiz Aizpuru' }, { github: 'dcousens', name: 'Daniel Cousens' }, { github: 'jgautsch', name: 'Jon Gautsch' }, { github: 'dmitry-smirnov', name: 'Dmitry Smirnov' }, { github: 'trevorburnham', name: 'Trevor Burnham' }]; -},{}],13:[function(require,module,exports){ +},{}],14:[function(require,module,exports){ 'use strict'; exports.AU = [{ value: 'australian-capital-territory', label: 'Australian Capital Territory', className: 'State-ACT' }, { value: 'new-south-wales', label: 'New South Wales', className: 'State-NSW' }, { value: 'victoria', label: 'Victoria', className: 'State-Vic' }, { value: 'queensland', label: 'Queensland', className: 'State-Qld' }, { value: 'western-australia', label: 'Western Australia', className: 'State-WA' }, { value: 'south-australia', label: 'South Australia', className: 'State-SA' }, { value: 'tasmania', label: 'Tasmania', className: 'State-Tas' }, { value: 'northern-territory', label: 'Northern Territory', className: 'State-NT' }]; exports.US = [{ value: 'AL', label: 'Alabama', disabled: true }, { value: 'AK', label: 'Alaska' }, { value: 'AS', label: 'American Samoa' }, { value: 'AZ', label: 'Arizona' }, { value: 'AR', label: 'Arkansas' }, { value: 'CA', label: 'California' }, { value: 'CO', label: 'Colorado' }, { value: 'CT', label: 'Connecticut' }, { value: 'DE', label: 'Delaware' }, { value: 'DC', label: 'District Of Columbia' }, { value: 'FM', label: 'Federated States Of Micronesia' }, { value: 'FL', label: 'Florida' }, { value: 'GA', label: 'Georgia' }, { value: 'GU', label: 'Guam' }, { value: 'HI', label: 'Hawaii' }, { value: 'ID', label: 'Idaho' }, { value: 'IL', label: 'Illinois' }, { value: 'IN', label: 'Indiana' }, { value: 'IA', label: 'Iowa' }, { value: 'KS', label: 'Kansas' }, { value: 'KY', label: 'Kentucky' }, { value: 'LA', label: 'Louisiana' }, { value: 'ME', label: 'Maine' }, { value: 'MH', label: 'Marshall Islands' }, { value: 'MD', label: 'Maryland' }, { value: 'MA', label: 'Massachusetts' }, { value: 'MI', label: 'Michigan' }, { value: 'MN', label: 'Minnesota' }, { value: 'MS', label: 'Mississippi' }, { value: 'MO', label: 'Missouri' }, { value: 'MT', label: 'Montana' }, { value: 'NE', label: 'Nebraska' }, { value: 'NV', label: 'Nevada' }, { value: 'NH', label: 'New Hampshire' }, { value: 'NJ', label: 'New Jersey' }, { value: 'NM', label: 'New Mexico' }, { value: 'NY', label: 'New York' }, { value: 'NC', label: 'North Carolina' }, { value: 'ND', label: 'North Dakota' }, { value: 'MP', label: 'Northern Mariana Islands' }, { value: 'OH', label: 'Ohio' }, { value: 'OK', label: 'Oklahoma' }, { value: 'OR', label: 'Oregon' }, { value: 'PW', label: 'Palau' }, { value: 'PA', label: 'Pennsylvania' }, { value: 'PR', label: 'Puerto Rico' }, { value: 'RI', label: 'Rhode Island' }, { value: 'SC', label: 'South Carolina' }, { value: 'SD', label: 'South Dakota' }, { value: 'TN', label: 'Tennessee' }, { value: 'TX', label: 'Texas' }, { value: 'UT', label: 'Utah' }, { value: 'VT', label: 'Vermont' }, { value: 'VI', label: 'Virgin Islands' }, { value: 'VA', label: 'Virginia' }, { value: 'WA', label: 'Washington' }, { value: 'WV', label: 'West Virginia' }, { value: 'WI', label: 'Wisconsin' }, { value: 'WY', label: 'Wyoming' }]; -},{}],14:[function(require,module,exports){ +},{}],15:[function(require,module,exports){ 'use strict'; module.exports = [{ value: 'John Smith', label: 'John Smith', email: 'john@smith.com' }, { value: 'Merry Jane', label: 'Merry Jane', email: 'merry@jane.com' }, { value: 'Stan Hoper', label: 'Stan Hoper', email: 'stan@hoper.com' }]; -},{}],15:[function(require,module,exports){ -module.exports = function blacklist (src) { - var copy = {} - var filter = arguments[1] - - if (typeof filter === 'string') { - filter = {} - for (var i = 1; i < arguments.length; i++) { - filter[arguments[i]] = true - } - } - - for (var key in src) { - // blacklist? - if (filter[key]) continue - - copy[key] = src[key] - } - - return copy -} - },{}],16:[function(require,module,exports){ // the whatwg-fetch polyfill installs the fetch() function // on the global object (window or self) @@ -1500,9 +1591,8 @@ module.exports = self.fetch.bind(self); },{}],18:[function(require,module,exports){ module.exports = require('react/lib/shallowCompare'); -},{"react/lib/shallowCompare":76}],19:[function(require,module,exports){ +},{"react/lib/shallowCompare":91}],19:[function(require,module,exports){ // shim for using process in browser - var process = module.exports = {}; // cached from whatever global is present so that test runners that stub it @@ -1513,22 +1603,84 @@ var process = module.exports = {}; var cachedSetTimeout; var cachedClearTimeout; +function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); +} +function defaultClearTimeout () { + throw new Error('clearTimeout has not been defined'); +} (function () { - try { - cachedSetTimeout = setTimeout; - } catch (e) { - cachedSetTimeout = function () { - throw new Error('setTimeout is not defined'); + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; } - } - try { - cachedClearTimeout = clearTimeout; - } catch (e) { - cachedClearTimeout = function () { - throw new Error('clearTimeout is not defined'); + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; } - } } ()) +function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + + +} +function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + + + +} var queue = []; var draining = false; var currentQueue; @@ -1553,7 +1705,7 @@ function drainQueue() { if (draining) { return; } - var timeout = cachedSetTimeout(cleanUpNextTick); + var timeout = runTimeout(cleanUpNextTick); draining = true; var len = queue.length; @@ -1570,7 +1722,7 @@ function drainQueue() { } currentQueue = null; draining = false; - cachedClearTimeout(timeout); + runClearTimeout(timeout); } process.nextTick = function (fun) { @@ -1582,7 +1734,7 @@ process.nextTick = function (fun) { } queue.push(new Item(fun, args)); if (queue.length === 1 && !draining) { - cachedSetTimeout(drainQueue, 0); + runTimeout(drainQueue); } }; @@ -1666,12 +1818,7 @@ var Gravatar = function (_React$Component) { _createClass(Gravatar, [{ key: 'render', value: function render() { - var base = void 0; - if (this.props.https) { - base = 'https://secure.gravatar.com/avatar/'; - } else { - base = 'http://www.gravatar.com/avatar/'; - } + var base = '//www.gravatar.com/avatar/'; var query = _queryString2.default.stringify({ s: this.props.size, @@ -1685,11 +1832,14 @@ var Gravatar = function (_React$Component) { d: this.props.default }); + // Gravatar service currently trims and lowercases all registered emails + var formattedEmail = ('' + this.props.email).trim().toLowerCase(); + var hash = void 0; if (this.props.md5) { hash = this.props.md5; - } else if (this.props.email) { - hash = (0, _md2.default)(this.props.email); + } else if (typeof this.props.email === 'string') { + hash = (0, _md2.default)(formattedEmail); } else { console.warn('Gravatar image can not be fetched. Either the "email" or "md5" prop must be specified.'); return _react2.default.createElement('script', null); @@ -1715,7 +1865,6 @@ var Gravatar = function (_React$Component) { var rest = _objectWithoutProperties(this.props, []); - delete rest.https; delete rest.md5; delete rest.email; delete rest.rating; @@ -1725,7 +1874,7 @@ var Gravatar = function (_React$Component) { delete rest.default; if (!modernBrowser && (0, _isRetina2.default)()) { return _react2.default.createElement('img', _extends({ - alt: 'Gravatar for ' + this.props.email, + alt: 'Gravatar for ' + formattedEmail, style: this.props.style, src: retinaSrc, height: this.props.size, @@ -1735,7 +1884,7 @@ var Gravatar = function (_React$Component) { })); } return _react2.default.createElement('img', _extends({ - alt: 'Gravatar for ' + this.props.email, + alt: 'Gravatar for ' + formattedEmail, style: this.props.style, src: src, srcSet: retinaSrc + ' 2x', @@ -1756,7 +1905,6 @@ Gravatar.propTypes = { md5: _react2.default.PropTypes.string, size: _react2.default.PropTypes.number, rating: _react2.default.PropTypes.string, - https: _react2.default.PropTypes.bool, default: _react2.default.PropTypes.string, className: _react2.default.PropTypes.string, style: _react2.default.PropTypes.object @@ -1764,7 +1912,6 @@ Gravatar.propTypes = { Gravatar.defaultProps = { size: 50, rating: 'g', - https: false, default: 'retro' }; @@ -1936,8 +2083,8 @@ module.exports = function() { md5._digestsize = 16; module.exports = function (message, options) { - if(typeof message == 'undefined') - return; + if (message === undefined || message === null) + throw new Error('Illegal argument ' + message); var digestbytes = crypt.wordsToBytes(md5(message, options)); return options && options.asBytes ? digestbytes : @@ -2081,22 +2228,26 @@ module.exports = charenc; })(); },{}],25:[function(require,module,exports){ -/** - * Determine if an object is Buffer +/*! + * Determine if an object is a Buffer * - * Author: Feross Aboukhadijeh - * License: MIT - * - * `npm install is-buffer` + * @author Feross Aboukhadijeh + * @license MIT */ +// The _isBuffer check is for Safari 5-7 support, because it's missing +// Object.prototype.constructor. Remove this eventually module.exports = function (obj) { - return !!(obj != null && - (obj._isBuffer || // For Safari 5-7 (missing Object.prototype.constructor) - (obj.constructor && - typeof obj.constructor.isBuffer === 'function' && - obj.constructor.isBuffer(obj)) - )) + return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer) +} + +function isBuffer (obj) { + return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj) +} + +// For Node v0.10 support. Remove this eventually. +function isSlowBuffer (obj) { + return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0)) } },{}],26:[function(require,module,exports){ @@ -2334,10 +2485,24 @@ var VirtualizedSelect = function (_Component) { return _this; } + /** See VirtualScroll#recomputeRowHeights */ + + _createClass(VirtualizedSelect, [{ + key: 'recomputeOptionHeights', + value: function recomputeOptionHeights() { + var index = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0]; + + if (this._virtualScroll) { + this._virtualScroll.recomputeRowHeights(index); + } + } + }, { key: 'render', value: function render() { - return _react2.default.createElement(_reactSelect2.default, _extends({}, this.props, { + var SelectComponent = this._getSelectComponent(); + + return _react2.default.createElement(SelectComponent, _extends({}, this.props, { menuRenderer: this._renderMenu, menuStyle: { overflow: 'hidden' } })); @@ -2348,38 +2513,57 @@ var VirtualizedSelect = function (_Component) { }, { key: '_renderMenu', value: function _renderMenu(_ref) { + var _this2 = this; + var focusedOption = _ref.focusedOption; var focusOption = _ref.focusOption; var labelKey = _ref.labelKey; var options = _ref.options; var selectValue = _ref.selectValue; var valueArray = _ref.valueArray; - var _props = this.props; - var maxHeight = _props.maxHeight; - var optionHeight = _props.optionHeight; - var optionRenderer = _props.optionRenderer; + var optionRenderer = this.props.optionRenderer; var focusedOptionIndex = options.indexOf(focusedOption); - var height = Math.min(maxHeight, options.length * optionHeight); + var height = this._calculateVirtualScrollHeight({ options: options }); var innerRowRenderer = optionRenderer || this._optionRenderer; - function wrappedRowRenderer(index) { + function wrappedRowRenderer(_ref2) { + var index = _ref2.index; + var option = options[index]; - return innerRowRenderer({ focusedOption: focusedOption, focusedOptionIndex: focusedOptionIndex, focusOption: focusOption, labelKey: labelKey, option: option, options: options, selectValue: selectValue, valueArray: valueArray }); + return innerRowRenderer({ + focusedOption: focusedOption, + focusedOptionIndex: focusedOptionIndex, + focusOption: focusOption, + labelKey: labelKey, + option: option, + optionIndex: index, + options: options, + selectValue: selectValue, + valueArray: valueArray + }); } return _react2.default.createElement( _reactVirtualized.AutoSizer, { disableHeight: true }, - function (_ref2) { - var width = _ref2.width; + function (_ref3) { + var width = _ref3.width; return _react2.default.createElement(_reactVirtualized.VirtualScroll, { className: 'VirtualSelectGrid', height: height, - rowHeight: optionHeight, + ref: function ref(_ref5) { + return _this2._virtualScroll = _ref5; + }, + rowCount: options.length, + rowHeight: function rowHeight(_ref4) { + var index = _ref4.index; + return _this2._getOptionHeight({ + option: options[index] + }); + }, rowRenderer: wrappedRowRenderer, - rowsCount: options.length, scrollToIndex: focusedOptionIndex, width: width }); @@ -2387,32 +2571,87 @@ var VirtualizedSelect = function (_Component) { ); } }, { - key: '_optionRenderer', - value: function _optionRenderer(_ref3) { - var focusedOption = _ref3.focusedOption; - var focusOption = _ref3.focusOption; - var labelKey = _ref3.labelKey; - var option = _ref3.option; - var selectValue = _ref3.selectValue; + key: '_calculateVirtualScrollHeight', + value: function _calculateVirtualScrollHeight(_ref6) { + var options = _ref6.options; + var maxHeight = this.props.maxHeight; + + + var height = 0; + + for (var optionIndex = 0; optionIndex < options.length; optionIndex++) { + var option = options[optionIndex]; + + height += this._getOptionHeight({ option: option }); + + if (height > maxHeight) { + return maxHeight; + } + } + + return height; + } + }, { + key: '_getOptionHeight', + value: function _getOptionHeight(_ref7) { + var option = _ref7.option; var optionHeight = this.props.optionHeight; - var className = option === focusedOption ? 'VirtualizedSelectOption VirtualizedSelectFocusedOption' : 'VirtualizedSelectOption'; + return optionHeight instanceof Function ? optionHeight({ option: option }) : optionHeight; + } + }, { + key: '_getSelectComponent', + value: function _getSelectComponent() { + var _props = this.props; + var async = _props.async; + var selectComponent = _props.selectComponent; + + + if (selectComponent) { + return selectComponent; + } else if (async) { + return _reactSelect2.default.Async; + } else { + return _reactSelect2.default; + } + } + }, { + key: '_optionRenderer', + value: function _optionRenderer(_ref8) { + var focusedOption = _ref8.focusedOption; + var focusOption = _ref8.focusOption; + var labelKey = _ref8.labelKey; + var option = _ref8.option; + var selectValue = _ref8.selectValue; + + var height = this._getOptionHeight({ option: option }); + + var className = ['VirtualizedSelectOption']; + + if (option === focusedOption) { + className.push('VirtualizedSelectFocusedOption'); + } + + if (option.disabled) { + className.push('VirtualizedSelectDisabledOption'); + } + + var events = option.disabled ? {} : { + onClick: function onClick() { + return selectValue(option); + }, + onMouseOver: function onMouseOver() { + return focusOption(option); + } + }; return _react2.default.createElement( 'div', - { - className: className, - onClick: function onClick() { - return selectValue(option); - }, - onMouseOver: function onMouseOver() { - return focusOption(option); - }, - style: { - height: optionHeight - } - }, + _extends({ + className: className.join(' '), + style: { height: height } + }, events), option[labelKey] ); } @@ -2422,16 +2661,19 @@ var VirtualizedSelect = function (_Component) { }(_react.Component); VirtualizedSelect.propTypes = { + async: _react.PropTypes.bool, maxHeight: _react.PropTypes.number.isRequired, - optionHeight: _react.PropTypes.number.isRequired, - optionRenderer: _react.PropTypes.func + optionHeight: _react.PropTypes.oneOfType([_react.PropTypes.number, _react.PropTypes.func]).isRequired, + optionRenderer: _react.PropTypes.func, + selectComponent: _react.PropTypes.func }; VirtualizedSelect.defaultProps = { + async: false, maxHeight: 200, optionHeight: 35 }; exports.default = VirtualizedSelect; -},{"react":undefined,"react-select":34,"react-virtualized":67}],30:[function(require,module,exports){ +},{"react":undefined,"react-select":36,"react-virtualized":83}],30:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { @@ -2451,10 +2693,24 @@ arguments[4][30][0].apply(exports,arguments) },{"./VirtualizedSelect":30,"dup":30}],32:[function(require,module,exports){ 'use strict'; +Object.defineProperty(exports, '__esModule', { + value: true +}); + var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + var _react = require('react'); var _react2 = _interopRequireDefault(_react); @@ -2467,181 +2723,517 @@ var _utilsStripDiacritics = require('./utils/stripDiacritics'); var _utilsStripDiacritics2 = _interopRequireDefault(_utilsStripDiacritics); -var requestId = 0; - -function initCache(cache) { - if (cache && typeof cache !== 'object') { - cache = {}; - } - return cache ? cache : null; -} - -function updateCache(cache, input, data) { - if (!cache) return; - cache[input] = data; -} +var propTypes = { + autoload: _react2['default'].PropTypes.bool.isRequired, // automatically call the `loadOptions` prop on-mount; defaults to true + cache: _react2['default'].PropTypes.any, // object to use to cache results; set to null/false to disable caching + children: _react2['default'].PropTypes.func.isRequired, // Child function responsible for creating the inner Select component; (props: Object): PropTypes.element + ignoreAccents: _react2['default'].PropTypes.bool, // strip diacritics when filtering; defaults to true + ignoreCase: _react2['default'].PropTypes.bool, // perform case-insensitive filtering; defaults to true + loadingPlaceholder: _react.PropTypes.string.isRequired, // replaces the placeholder while options are loading + loadOptions: _react2['default'].PropTypes.func.isRequired, // callback to load options asynchronously; (inputValue: string, callback: Function): ?Promise + options: _react.PropTypes.array.isRequired, // array of options + placeholder: _react2['default'].PropTypes.oneOfType([// field placeholder, displayed when there's no value (shared with Select) + _react2['default'].PropTypes.string, _react2['default'].PropTypes.node]), + searchPromptText: _react2['default'].PropTypes.oneOfType([// label to prompt for search input + _react2['default'].PropTypes.string, _react2['default'].PropTypes.node]) +}; -function getFromCache(cache, input) { - if (!cache) return; - for (var i = input.length; i >= 0; --i) { - var cacheKey = input.slice(0, i); - if (cache[cacheKey] && (input === cacheKey || cache[cacheKey].complete)) { - return cache[cacheKey]; - } - } -} +var defaultProps = { + autoload: true, + cache: {}, + children: defaultChildren, + ignoreAccents: true, + ignoreCase: true, + loadingPlaceholder: 'Loading...', + options: [], + searchPromptText: 'Type to search' +}; -function thenPromise(promise, callback) { - if (!promise || typeof promise.then !== 'function') return; - return promise.then(function (data) { - callback(null, data); - }, function (err) { - callback(err); - }); -} +var Async = (function (_Component) { + _inherits(Async, _Component); -var stringOrNode = _react2['default'].PropTypes.oneOfType([_react2['default'].PropTypes.string, _react2['default'].PropTypes.node]); + function Async(props, context) { + _classCallCheck(this, Async); -var Async = _react2['default'].createClass({ - displayName: 'Async', + _get(Object.getPrototypeOf(Async.prototype), 'constructor', this).call(this, props, context); - propTypes: { - cache: _react2['default'].PropTypes.any, // object to use to cache results, can be null to disable cache - ignoreAccents: _react2['default'].PropTypes.bool, // whether to strip diacritics when filtering (shared with Select) - ignoreCase: _react2['default'].PropTypes.bool, // whether to perform case-insensitive filtering (shared with Select) - isLoading: _react2['default'].PropTypes.bool, // overrides the isLoading state when set to true - loadOptions: _react2['default'].PropTypes.func.isRequired, // function to call to load options asynchronously - loadingPlaceholder: _react2['default'].PropTypes.string, // replaces the placeholder while options are loading - minimumInput: _react2['default'].PropTypes.number, // the minimum number of characters that trigger loadOptions - noResultsText: stringOrNode, // placeholder displayed when there are no matching search results (shared with Select) - onInputChange: _react2['default'].PropTypes.func, // onInputChange handler: function (inputValue) {} - placeholder: stringOrNode, // field placeholder, displayed when there's no value (shared with Select) - searchPromptText: stringOrNode, // label to prompt for search input - searchingText: _react2['default'].PropTypes.string }, - // message to display while options are loading - getDefaultProps: function getDefaultProps() { - return { - cache: true, - ignoreAccents: true, - ignoreCase: true, - loadingPlaceholder: 'Loading...', - minimumInput: 0, - searchingText: 'Searching...', - searchPromptText: 'Type to search' - }; - }, - getInitialState: function getInitialState() { - return { - cache: initCache(this.props.cache), + this.state = { isLoading: false, - options: [] + options: props.options }; - }, - componentWillMount: function componentWillMount() { - this._lastInput = ''; - }, - componentDidMount: function componentDidMount() { - this.loadOptions(''); - }, - componentWillReceiveProps: function componentWillReceiveProps(nextProps) { - if (nextProps.cache !== this.props.cache) { - this.setState({ - cache: initCache(nextProps.cache) - }); - } - }, - focus: function focus() { - this.refs.select.focus(); - }, - resetState: function resetState() { - this._currentRequestId = -1; - this.setState({ - isLoading: false, - options: [] - }); - }, - getResponseHandler: function getResponseHandler(input) { - var _this = this; - var _requestId = this._currentRequestId = requestId++; - return function (err, data) { - if (err) throw err; - if (!_this.isMounted()) return; - updateCache(_this.state.cache, input, data); - if (_requestId !== _this._currentRequestId) return; - _this.setState({ - isLoading: false, - options: data && data.options || [] - }); - }; - }, - loadOptions: function loadOptions(input) { - if (this.props.onInputChange) { - var nextState = this.props.onInputChange(input); - // Note: != used deliberately here to catch undefined and null - if (nextState != null) { - input = '' + nextState; - } - } - if (this.props.ignoreAccents) input = (0, _utilsStripDiacritics2['default'])(input); - if (this.props.ignoreCase) input = input.toLowerCase(); + this._onInputChange = this._onInputChange.bind(this); + } + + _createClass(Async, [{ + key: 'componentDidMount', + value: function componentDidMount() { + var autoload = this.props.autoload; - this._lastInput = input; - if (input.length < this.props.minimumInput) { - return this.resetState(); + if (autoload) { + this.loadOptions(''); + } } - var cacheResult = getFromCache(this.state.cache, input); - if (cacheResult) { - return this.setState({ - options: cacheResult.options + }, { + key: 'componentWillUpdate', + value: function componentWillUpdate(nextProps, nextState) { + var _this = this; + + var propertiesToSync = ['options']; + propertiesToSync.forEach(function (prop) { + if (_this.props[prop] !== nextProps[prop]) { + _this.setState(_defineProperty({}, prop, nextProps[prop])); + } }); } - this.setState({ - isLoading: true - }); - var responseHandler = this.getResponseHandler(input); - var inputPromise = thenPromise(this.props.loadOptions(input, responseHandler), responseHandler); - return inputPromise ? inputPromise.then(function () { - return input; - }) : input; - }, - render: function render() { - var noResultsText = this.props.noResultsText; - var _state = this.state; - var isLoading = _state.isLoading; - var options = _state.options; + }, { + key: 'loadOptions', + value: function loadOptions(inputValue) { + var _this2 = this; - if (this.props.isLoading) isLoading = true; - var placeholder = isLoading ? this.props.loadingPlaceholder : this.props.placeholder; - if (isLoading) { - noResultsText = this.props.searchingText; - } else if (!options.length && this._lastInput.length < this.props.minimumInput) { - noResultsText = this.props.searchPromptText; - } - return _react2['default'].createElement(_Select2['default'], _extends({}, this.props, { - ref: 'select', - isLoading: isLoading, - noResultsText: noResultsText, - onInputChange: this.loadOptions, - options: options, - placeholder: placeholder - })); - } -}); + var _props = this.props; + var cache = _props.cache; + var loadOptions = _props.loadOptions; -module.exports = Async; -},{"./Select":34,"./utils/stripDiacritics":36,"react":undefined}],33:[function(require,module,exports){ -'use strict'; + if (cache && cache.hasOwnProperty(inputValue)) { + this.setState({ + options: cache[inputValue] + }); -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + return; + } -var _react = require('react'); + var callback = function callback(error, data) { + if (callback === _this2._callback) { + _this2._callback = null; -var _react2 = _interopRequireDefault(_react); + var options = data && data.options || []; -var _classnames = require('classnames'); + if (cache) { + cache[inputValue] = options; + } -var _classnames2 = _interopRequireDefault(_classnames); + _this2.setState({ + isLoading: false, + options: options + }); + } + }; + + // Ignore all but the most recent request + this._callback = callback; + + var promise = loadOptions(inputValue, callback); + if (promise) { + promise.then(function (data) { + return callback(null, data); + }, function (error) { + return callback(error); + }); + } + + if (this._callback && !this.state.isLoading) { + this.setState({ + isLoading: true + }); + } + + return inputValue; + } + }, { + key: '_onInputChange', + value: function _onInputChange(inputValue) { + var _props2 = this.props; + var ignoreAccents = _props2.ignoreAccents; + var ignoreCase = _props2.ignoreCase; + + if (ignoreAccents) { + inputValue = (0, _utilsStripDiacritics2['default'])(inputValue); + } + + if (ignoreCase) { + inputValue = inputValue.toLowerCase(); + } + + return this.loadOptions(inputValue); + } + }, { + key: 'render', + value: function render() { + var _props3 = this.props; + var children = _props3.children; + var loadingPlaceholder = _props3.loadingPlaceholder; + var placeholder = _props3.placeholder; + var searchPromptText = _props3.searchPromptText; + var _state = this.state; + var isLoading = _state.isLoading; + var options = _state.options; + + var props = { + noResultsText: isLoading ? loadingPlaceholder : searchPromptText, + placeholder: isLoading ? loadingPlaceholder : placeholder, + options: isLoading ? [] : options + }; + + return children(_extends({}, this.props, props, { + isLoading: isLoading, + onInputChange: this._onInputChange + })); + } + }]); + + return Async; +})(_react.Component); + +exports['default'] = Async; + +Async.propTypes = propTypes; +Async.defaultProps = defaultProps; + +function defaultChildren(props) { + return _react2['default'].createElement(_Select2['default'], props); +}; +module.exports = exports['default']; +},{"./Select":36,"./utils/stripDiacritics":41,"react":undefined}],33:[function(require,module,exports){ +'use strict'; + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _Select = require('./Select'); + +var _Select2 = _interopRequireDefault(_Select); + +var AsyncCreatable = _react2['default'].createClass({ + displayName: 'AsyncCreatableSelect', + + render: function render() { + var _this = this; + + return _react2['default'].createElement( + _Select2['default'].Async, + this.props, + function (asyncProps) { + return _react2['default'].createElement( + _Select2['default'].Creatable, + _this.props, + function (creatableProps) { + return _react2['default'].createElement(_Select2['default'], _extends({}, asyncProps, creatableProps, { + onInputChange: function (input) { + creatableProps.onInputChange(input); + return asyncProps.onInputChange(input); + } + })); + } + ); + } + ); + } +}); + +module.exports = AsyncCreatable; +},{"./Select":36,"react":undefined}],34:[function(require,module,exports){ +'use strict'; + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _Select = require('./Select'); + +var _Select2 = _interopRequireDefault(_Select); + +var _utilsDefaultFilterOptions = require('./utils/defaultFilterOptions'); + +var _utilsDefaultFilterOptions2 = _interopRequireDefault(_utilsDefaultFilterOptions); + +var _utilsDefaultMenuRenderer = require('./utils/defaultMenuRenderer'); + +var _utilsDefaultMenuRenderer2 = _interopRequireDefault(_utilsDefaultMenuRenderer); + +var Creatable = _react2['default'].createClass({ + displayName: 'CreatableSelect', + + propTypes: { + // Child function responsible for creating the inner Select component + // This component can be used to compose HOCs (eg Creatable and Async) + // (props: Object): PropTypes.element + children: _react2['default'].PropTypes.func, + + // See Select.propTypes.filterOptions + filterOptions: _react2['default'].PropTypes.any, + + // Searches for any matching option within the set of options. + // This function prevents duplicate options from being created. + // ({ option: Object, options: Array, labelKey: string, valueKey: string }): boolean + isOptionUnique: _react2['default'].PropTypes.func, + + // Determines if the current input text represents a valid option. + // ({ label: string }): boolean + isValidNewOption: _react2['default'].PropTypes.func, + + // See Select.propTypes.menuRenderer + menuRenderer: _react2['default'].PropTypes.any, + + // Factory to create new option. + // ({ label: string, labelKey: string, valueKey: string }): Object + newOptionCreator: _react2['default'].PropTypes.func, + + // See Select.propTypes.options + options: _react2['default'].PropTypes.array, + + // Creates prompt/placeholder option text. + // (filterText: string): string + promptTextCreator: _react2['default'].PropTypes.func, + + // Decides if a keyDown event (eg its `keyCode`) should result in the creation of a new option. + shouldKeyDownEventCreateNewOption: _react2['default'].PropTypes.func + }, + + // Default prop methods + statics: { + isOptionUnique: isOptionUnique, + isValidNewOption: isValidNewOption, + newOptionCreator: newOptionCreator, + promptTextCreator: promptTextCreator, + shouldKeyDownEventCreateNewOption: shouldKeyDownEventCreateNewOption + }, + + getDefaultProps: function getDefaultProps() { + return { + filterOptions: _utilsDefaultFilterOptions2['default'], + isOptionUnique: isOptionUnique, + isValidNewOption: isValidNewOption, + menuRenderer: _utilsDefaultMenuRenderer2['default'], + newOptionCreator: newOptionCreator, + promptTextCreator: promptTextCreator, + shouldKeyDownEventCreateNewOption: shouldKeyDownEventCreateNewOption + }; + }, + + createNewOption: function createNewOption() { + var _props = this.props; + var isValidNewOption = _props.isValidNewOption; + var newOptionCreator = _props.newOptionCreator; + var _props$options = _props.options; + var options = _props$options === undefined ? [] : _props$options; + var shouldKeyDownEventCreateNewOption = _props.shouldKeyDownEventCreateNewOption; + + if (isValidNewOption({ label: this.inputValue })) { + var option = newOptionCreator({ label: this.inputValue, labelKey: this.labelKey, valueKey: this.valueKey }); + var _isOptionUnique = this.isOptionUnique({ option: option }); + + // Don't add the same option twice. + if (_isOptionUnique) { + options.unshift(option); + + this.select.selectValue(option); + } + } + }, + + filterOptions: function filterOptions() { + var _props2 = this.props; + var filterOptions = _props2.filterOptions; + var isValidNewOption = _props2.isValidNewOption; + var options = _props2.options; + var promptTextCreator = _props2.promptTextCreator; + + // TRICKY Check currently selected options as well. + // Don't display a create-prompt for a value that's selected. + // This covers async edge-cases where a newly-created Option isn't yet in the async-loaded array. + var excludeOptions = arguments[2] || []; + + var filteredOptions = filterOptions.apply(undefined, arguments) || []; + + if (isValidNewOption({ label: this.inputValue })) { + var _newOptionCreator = this.props.newOptionCreator; + + var option = _newOptionCreator({ + label: this.inputValue, + labelKey: this.labelKey, + valueKey: this.valueKey + }); + + // TRICKY Compare to all options (not just filtered options) in case option has already been selected). + // For multi-selects, this would remove it from the filtered list. + var _isOptionUnique2 = this.isOptionUnique({ + option: option, + options: excludeOptions.concat(filteredOptions) + }); + + if (_isOptionUnique2) { + var _prompt = promptTextCreator(this.inputValue); + + this._createPlaceholderOption = _newOptionCreator({ + label: _prompt, + labelKey: this.labelKey, + valueKey: this.valueKey + }); + + filteredOptions.unshift(this._createPlaceholderOption); + } + } + + return filteredOptions; + }, + + isOptionUnique: function isOptionUnique(_ref2) { + var option = _ref2.option; + var options = _ref2.options; + var isOptionUnique = this.props.isOptionUnique; + + options = options || this.select.filterOptions(); + + return isOptionUnique({ + labelKey: this.labelKey, + option: option, + options: options, + valueKey: this.valueKey + }); + }, + + menuRenderer: function menuRenderer(params) { + var menuRenderer = this.props.menuRenderer; + + return menuRenderer(_extends({}, params, { + onSelect: this.onOptionSelect + })); + }, + + onInputChange: function onInputChange(input) { + // This value may be needed in between Select mounts (when this.select is null) + this.inputValue = input; + }, + + onInputKeyDown: function onInputKeyDown(event) { + var shouldKeyDownEventCreateNewOption = this.props.shouldKeyDownEventCreateNewOption; + + var focusedOption = this.select.getFocusedOption(); + + if (focusedOption && focusedOption === this._createPlaceholderOption && shouldKeyDownEventCreateNewOption({ keyCode: event.keyCode })) { + this.createNewOption(); + + // Prevent decorated Select from doing anything additional with this keyDown event + event.preventDefault(); + } + }, + + onOptionSelect: function onOptionSelect(option, event) { + if (option === this._createPlaceholderOption) { + this.createNewOption(); + } else { + this.select.selectValue(option); + } + }, + + render: function render() { + var _this = this; + + var _props3 = this.props; + var _props3$children = _props3.children; + var children = _props3$children === undefined ? defaultChildren : _props3$children; + var newOptionCreator = _props3.newOptionCreator; + var shouldKeyDownEventCreateNewOption = _props3.shouldKeyDownEventCreateNewOption; + + var restProps = _objectWithoutProperties(_props3, ['children', 'newOptionCreator', 'shouldKeyDownEventCreateNewOption']); + + var props = _extends({}, restProps, { + allowCreate: true, + filterOptions: this.filterOptions, + menuRenderer: this.menuRenderer, + onInputChange: this.onInputChange, + onInputKeyDown: this.onInputKeyDown, + ref: function ref(_ref) { + _this.select = _ref; + + // These values may be needed in between Select mounts (when this.select is null) + if (_ref) { + _this.labelKey = _ref.props.labelKey; + _this.valueKey = _ref.props.valueKey; + } + } + }); + + return children(props); + } +}); + +function defaultChildren(props) { + return _react2['default'].createElement(_Select2['default'], props); +}; + +function isOptionUnique(_ref3) { + var option = _ref3.option; + var options = _ref3.options; + var labelKey = _ref3.labelKey; + var valueKey = _ref3.valueKey; + + return options.filter(function (existingOption) { + return existingOption[labelKey] === option[labelKey] || existingOption[valueKey] === option[valueKey]; + }).length === 0; +}; + +function isValidNewOption(_ref4) { + var label = _ref4.label; + + return !!label; +}; + +function newOptionCreator(_ref5) { + var label = _ref5.label; + var labelKey = _ref5.labelKey; + var valueKey = _ref5.valueKey; + + var option = {}; + option[valueKey] = label; + option[labelKey] = label; + option.className = 'Select-create-option-placeholder'; + return option; +}; + +function promptTextCreator(label) { + return 'Create option "' + label + '"'; +} + +function shouldKeyDownEventCreateNewOption(_ref6) { + var keyCode = _ref6.keyCode; + + switch (keyCode) { + case 9: // TAB + case 13: // ENTER + case 188: + // COMMA + return true; + } + + return false; +}; + +module.exports = Creatable; +},{"./Select":36,"./utils/defaultFilterOptions":39,"./utils/defaultMenuRenderer":40,"react":undefined}],35:[function(require,module,exports){ +'use strict'; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _classnames = require('classnames'); + +var _classnames2 = _interopRequireDefault(_classnames); var Option = _react2['default'].createClass({ displayName: 'Option', @@ -2742,7 +3334,13 @@ var Option = _react2['default'].createClass({ }); module.exports = Option; -},{"classnames":undefined,"react":undefined}],34:[function(require,module,exports){ +},{"classnames":undefined,"react":undefined}],36:[function(require,module,exports){ +/*! + Copyright (c) 2016 Jed Watson. + Licensed under the MIT License (MIT), see + http://jedwatson.github.io/react-select +*/ + 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -2753,6 +3351,8 @@ var _extends = Object.assign || function (target) { for (var i = 1; i < argument function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } +function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } + function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } var _react = require('react'); @@ -2771,18 +3371,30 @@ var _classnames = require('classnames'); var _classnames2 = _interopRequireDefault(_classnames); -var _blacklist = require('blacklist'); +var _utilsDefaultArrowRenderer = require('./utils/defaultArrowRenderer'); -var _blacklist2 = _interopRequireDefault(_blacklist); +var _utilsDefaultArrowRenderer2 = _interopRequireDefault(_utilsDefaultArrowRenderer); -var _utilsStripDiacritics = require('./utils/stripDiacritics'); +var _utilsDefaultFilterOptions = require('./utils/defaultFilterOptions'); -var _utilsStripDiacritics2 = _interopRequireDefault(_utilsStripDiacritics); +var _utilsDefaultFilterOptions2 = _interopRequireDefault(_utilsDefaultFilterOptions); + +var _utilsDefaultMenuRenderer = require('./utils/defaultMenuRenderer'); + +var _utilsDefaultMenuRenderer2 = _interopRequireDefault(_utilsDefaultMenuRenderer); var _Async = require('./Async'); var _Async2 = _interopRequireDefault(_Async); +var _AsyncCreatable = require('./AsyncCreatable'); + +var _AsyncCreatable2 = _interopRequireDefault(_AsyncCreatable); + +var _Creatable = require('./Creatable'); + +var _Creatable2 = _interopRequireDefault(_Creatable); + var _Option = require('./Option'); var _Option2 = _interopRequireDefault(_Option); @@ -2792,10 +3404,15 @@ var _Value = require('./Value'); var _Value2 = _interopRequireDefault(_Value); function stringifyValue(value) { - if (typeof value === 'object') { + var valueType = typeof value; + if (valueType === 'string') { + return value; + } else if (valueType === 'object') { return JSON.stringify(value); + } else if (valueType === 'number' || valueType === 'boolean') { + return String(value); } else { - return value; + return ''; } } @@ -2809,15 +3426,14 @@ var Select = _react2['default'].createClass({ propTypes: { addLabelText: _react2['default'].PropTypes.string, // placeholder displayed when you want to add a label on a multi-value input - allowCreate: _react2['default'].PropTypes.bool, // whether to allow creation of new entries 'aria-label': _react2['default'].PropTypes.string, // Aria label (for assistive tech) 'aria-labelledby': _react2['default'].PropTypes.string, // HTML ID of an element that should be used as the label (for assistive tech) + arrowRenderer: _react2['default'].PropTypes.func, // Create drop-down caret element autoBlur: _react2['default'].PropTypes.bool, // automatically blur the component when an option is selected autofocus: _react2['default'].PropTypes.bool, // autofocus the component on mount autosize: _react2['default'].PropTypes.bool, // whether to enable autosizing or not backspaceRemoves: _react2['default'].PropTypes.bool, // whether backspace removes an item if there is no text input - backspaceToRemoveMessage: _react2['default'].PropTypes.string, // Message to use for screenreaders to press backspace to remove the current item - - // {label} is replaced with the item label + backspaceToRemoveMessage: _react2['default'].PropTypes.string, // Message to use for screenreaders to press backspace to remove the current item - {label} is replaced with the item label className: _react2['default'].PropTypes.string, // className for the outer element clearAllText: stringOrNode, // title for the "clear" control when multi: true clearValueText: stringOrNode, // title for the "clear" control @@ -2831,6 +3447,7 @@ var Select = _react2['default'].createClass({ ignoreCase: _react2['default'].PropTypes.bool, // whether to perform case-insensitive filtering inputProps: _react2['default'].PropTypes.object, // custom attributes for the Input inputRenderer: _react2['default'].PropTypes.func, // returns a custom input component + instanceId: _react2['default'].PropTypes.string, // set the components instanceId isLoading: _react2['default'].PropTypes.bool, // whether the Select is loading externally or not (such as options being loaded) joinValues: _react2['default'].PropTypes.bool, // joins multiple values into a single form field with the delimiter (legacy mode) labelKey: _react2['default'].PropTypes.string, // path of the label value in option objects @@ -2842,14 +3459,15 @@ var Select = _react2['default'].createClass({ menuStyle: _react2['default'].PropTypes.object, // optional style to apply to the menu multi: _react2['default'].PropTypes.bool, // multi-value input name: _react2['default'].PropTypes.string, // generates a hidden tag with this field name for html forms - newOptionCreator: _react2['default'].PropTypes.func, // factory to create new options when allowCreate set noResultsText: stringOrNode, // placeholder displayed when there are no matching search results onBlur: _react2['default'].PropTypes.func, // onBlur handler: function (event) {} onBlurResetsInput: _react2['default'].PropTypes.bool, // whether input is cleared on blur onChange: _react2['default'].PropTypes.func, // onChange handler: function (newValue) {} onClose: _react2['default'].PropTypes.func, // fires when the menu is closed + onCloseResetsInput: _react2['default'].PropTypes.bool, // whether input is cleared when menu is closed through the arrow onFocus: _react2['default'].PropTypes.func, // onFocus handler: function (event) {} onInputChange: _react2['default'].PropTypes.func, // onInputChange handler: function (inputValue) {} + onInputKeyDown: _react2['default'].PropTypes.func, // input keyDown handler: function (event) {} onMenuScrollToBottom: _react2['default'].PropTypes.func, // fires when the menu is scrolled to the bottom; can be used to paginate options onOpen: _react2['default'].PropTypes.func, // fires when the menu is opened onValueClick: _react2['default'].PropTypes.func, // onClick handler for value labels: function (value, event) {} @@ -2876,13 +3494,13 @@ var Select = _react2['default'].createClass({ wrapperStyle: _react2['default'].PropTypes.object }, // optional style to apply to the component wrapper - statics: { Async: _Async2['default'] }, + statics: { Async: _Async2['default'], AsyncCreatable: _AsyncCreatable2['default'], Creatable: _Creatable2['default'] }, getDefaultProps: function getDefaultProps() { return { addLabelText: 'Add "{label}"?', + arrowRenderer: _utilsDefaultArrowRenderer2['default'], autosize: true, - allowCreate: false, backspaceRemoves: true, backspaceToRemoveMessage: 'Press backspace to remove {label}', clearable: true, @@ -2891,7 +3509,7 @@ var Select = _react2['default'].createClass({ delimiter: ',', disabled: false, escapeClearsValue: true, - filterOptions: true, + filterOptions: _utilsDefaultFilterOptions2['default'], ignoreAccents: true, ignoreCase: true, inputProps: {}, @@ -2901,15 +3519,16 @@ var Select = _react2['default'].createClass({ matchPos: 'any', matchProp: 'any', menuBuffer: 0, + menuRenderer: _utilsDefaultMenuRenderer2['default'], multi: false, noResultsText: 'No results found', onBlurResetsInput: true, + onCloseResetsInput: true, openAfterFocus: false, optionComponent: _Option2['default'], pageSize: 5, placeholder: 'Select...', required: false, - resetValue: null, scrollMenuIntoView: true, searchable: true, simpleValue: false, @@ -2923,7 +3542,6 @@ var Select = _react2['default'].createClass({ return { inputValue: '', isFocused: false, - isLoading: false, isOpen: false, isPseudoFocused: false, required: false @@ -2931,7 +3549,7 @@ var Select = _react2['default'].createClass({ }, componentWillMount: function componentWillMount() { - this._instancePrefix = 'react-select-' + ++instanceId + '-'; + this._instancePrefix = 'react-select-' + (this.props.instanceId || ++instanceId) + '-'; var valueArray = this.getValueArray(this.props.value); if (this.props.required) { @@ -2959,6 +3577,7 @@ var Select = _react2['default'].createClass({ componentWillUpdate: function componentWillUpdate(nextProps, nextState) { if (nextState.isOpen !== this.state.isOpen) { + this.toggleTouchOutsideEvent(nextState.isOpen); var handler = nextState.isOpen ? nextProps.onOpen : nextProps.onClose; handler && handler(); } @@ -2966,27 +3585,27 @@ var Select = _react2['default'].createClass({ componentDidUpdate: function componentDidUpdate(prevProps, prevState) { // focus to the selected option - if (this.refs.menu && this.refs.focused && this.state.isOpen && !this.hasScrolledToOption) { - var focusedOptionNode = _reactDom2['default'].findDOMNode(this.refs.focused); - var menuNode = _reactDom2['default'].findDOMNode(this.refs.menu); + if (this.menu && this.focused && this.state.isOpen && !this.hasScrolledToOption) { + var focusedOptionNode = _reactDom2['default'].findDOMNode(this.focused); + var menuNode = _reactDom2['default'].findDOMNode(this.menu); menuNode.scrollTop = focusedOptionNode.offsetTop; this.hasScrolledToOption = true; } else if (!this.state.isOpen) { this.hasScrolledToOption = false; } - if (this._scrollToFocusedOptionOnUpdate && this.refs.focused && this.refs.menu) { + if (this._scrollToFocusedOptionOnUpdate && this.focused && this.menu) { this._scrollToFocusedOptionOnUpdate = false; - var focusedDOM = _reactDom2['default'].findDOMNode(this.refs.focused); - var menuDOM = _reactDom2['default'].findDOMNode(this.refs.menu); + var focusedDOM = _reactDom2['default'].findDOMNode(this.focused); + var menuDOM = _reactDom2['default'].findDOMNode(this.menu); var focusedRect = focusedDOM.getBoundingClientRect(); var menuRect = menuDOM.getBoundingClientRect(); if (focusedRect.bottom > menuRect.bottom || focusedRect.top < menuRect.top) { menuDOM.scrollTop = focusedDOM.offsetTop + focusedDOM.clientHeight - menuDOM.offsetHeight; } } - if (this.props.scrollMenuIntoView && this.refs.menuContainer) { - var menuContainerRect = this.refs.menuContainer.getBoundingClientRect(); + if (this.props.scrollMenuIntoView && this.menuContainer) { + var menuContainerRect = this.menuContainer.getBoundingClientRect(); if (window.innerHeight < menuContainerRect.bottom + this.props.menuBuffer) { window.scrollBy(0, menuContainerRect.bottom + this.props.menuBuffer - window.innerHeight); } @@ -2997,9 +3616,28 @@ var Select = _react2['default'].createClass({ } }, + componentWillUnmount: function componentWillUnmount() { + document.removeEventListener('touchstart', this.handleTouchOutside); + }, + + toggleTouchOutsideEvent: function toggleTouchOutsideEvent(enabled) { + if (enabled) { + document.addEventListener('touchstart', this.handleTouchOutside); + } else { + document.removeEventListener('touchstart', this.handleTouchOutside); + } + }, + + handleTouchOutside: function handleTouchOutside(event) { + // handle touch outside on ios to dismiss menu + if (this.wrapper && !this.wrapper.contains(event.target)) { + this.closeMenu(); + } + }, + focus: function focus() { - if (!this.refs.input) return; - this.refs.input.focus(); + if (!this.input) return; + this.input.focus(); if (this.props.openAfterFocus) { this.setState({ @@ -3009,8 +3647,8 @@ var Select = _react2['default'].createClass({ }, blurInput: function blurInput() { - if (!this.refs.input) return; - this.refs.input.blur(); + if (!this.input) return; + this.input.blur(); }, handleTouchMove: function handleTouchMove(event) { @@ -3070,9 +3708,9 @@ var Select = _react2['default'].createClass({ // Call focus() again here to be safe. this.focus(); - var input = this.refs.input; + var input = this.input; if (typeof input.getInput === 'function') { - // Get the actual DOM input if the ref is an component + // Get the actual DOM input if the ref is an component input = input.getInput(); } @@ -3122,15 +3760,24 @@ var Select = _react2['default'].createClass({ }, closeMenu: function closeMenu() { - this.setState({ - isOpen: false, - isPseudoFocused: this.state.isFocused && !this.props.multi, - inputValue: '' - }); + if (this.props.onCloseResetsInput) { + this.setState({ + isOpen: false, + isPseudoFocused: this.state.isFocused && !this.props.multi, + inputValue: '' + }); + } else { + this.setState({ + isOpen: false, + isPseudoFocused: this.state.isFocused && !this.props.multi, + inputValue: this.state.inputValue + }); + } this.hasScrolledToOption = false; }, handleInputFocus: function handleInputFocus(event) { + if (this.props.disabled) return; var isOpen = this.state.isOpen || this._openAfterFocus || this.props.openOnFocus; if (this.props.onFocus) { this.props.onFocus(event); @@ -3144,7 +3791,7 @@ var Select = _react2['default'].createClass({ handleInputBlur: function handleInputBlur(event) { // The check for menu.contains(activeElement) is necessary to prevent IE11's scrollbar from closing the menu in certain contexts. - if (this.refs.menu && (this.refs.menu === document.activeElement || this.refs.menu.contains(document.activeElement))) { + if (this.menu && (this.menu === document.activeElement || this.menu.contains(document.activeElement))) { this.focus(); return; } @@ -3165,6 +3812,7 @@ var Select = _react2['default'].createClass({ handleInputChange: function handleInputChange(event) { var newInputValue = event.target.value; + if (this.state.inputValue !== event.target.value && this.props.onInputChange) { var nextState = this.props.onInputChange(newInputValue); // Note: != used deliberately here to catch undefined and null @@ -3172,6 +3820,7 @@ var Select = _react2['default'].createClass({ newInputValue = '' + nextState; } } + this.setState({ isOpen: true, isPseudoFocused: false, @@ -3181,6 +3830,14 @@ var Select = _react2['default'].createClass({ handleKeyDown: function handleKeyDown(event) { if (this.props.disabled) return; + + if (typeof this.props.onInputKeyDown === 'function') { + this.props.onInputKeyDown(event); + if (event.defaultPrevented) { + return; + } + } + switch (event.keyCode) { case 8: // backspace @@ -3230,21 +3887,18 @@ var Select = _react2['default'].createClass({ break; case 35: // end key + if (event.shiftKey) { + return; + } this.focusEndOption(); break; case 36: // home key + if (event.shiftKey) { + return; + } this.focusStartOption(); break; - // case 188: // , - // if (this.props.allowCreate && this.props.multi) { - // event.preventDefault(); - // event.stopPropagation(); - // this.selectFocusedOption(); - // } else { - // return; - // } - // break; default: return; } @@ -3307,7 +3961,8 @@ var Select = _react2['default'].createClass({ * @param {Object} props - the Select component's props (or nextProps) */ expandValue: function expandValue(value, props) { - if (typeof value !== 'string' && typeof value !== 'number') return value; + var valueType = typeof value; + if (valueType !== 'string' && valueType !== 'number' && valueType !== 'boolean') return value; var options = props.options; var valueKey = props.valueKey; @@ -3339,7 +3994,7 @@ var Select = _react2['default'].createClass({ selectValue: function selectValue(value) { var _this3 = this; - //NOTE: update value in the callback to make sure the input value is empty so that there are no sttyling issues (Chrome had issue otherwise) + //NOTE: update value in the callback to make sure the input value is empty so that there are no styling issues (Chrome had issue otherwise) this.hasScrolledToOption = false; if (this.props.multi) { this.setState({ @@ -3387,13 +4042,23 @@ var Select = _react2['default'].createClass({ } event.stopPropagation(); event.preventDefault(); - this.setValue(this.props.resetValue); + this.setValue(this.getResetValue()); this.setState({ isOpen: false, inputValue: '' }, this.focus); }, + getResetValue: function getResetValue() { + if (this.props.resetValue !== undefined) { + return this.props.resetValue; + } else if (this.props.multi) { + return []; + } else { + return null; + } + }, + focusOption: function focusOption(option) { this.setState({ focusedOption: option @@ -3485,10 +4150,15 @@ var Select = _react2['default'].createClass({ }); }, + getFocusedOption: function getFocusedOption() { + return this._focusedOption; + }, + + getInputValue: function getInputValue() { + return this.state.inputValue; + }, + selectFocusedOption: function selectFocusedOption() { - // if (this.props.allowCreate && !this.state.focusedOption) { - // return this.selectValue(this.state.inputValue); - // } if (this._focusedOption) { return this.selectValue(this._focusedOption); } @@ -3529,7 +4199,7 @@ var Select = _react2['default'].createClass({ onRemove: _this4.removeValue, value: value }, - renderLabel(value), + renderLabel(value, i), _react2['default'].createElement( 'span', { className: 'Select-aria-only' }, @@ -3554,6 +4224,8 @@ var Select = _react2['default'].createClass({ }, renderInput: function renderInput(valueArray, focusedOptionIndex) { + var _this5 = this; + if (this.props.inputRenderer) { return this.props.inputRenderer(); } else { @@ -3578,13 +4250,19 @@ var Select = _react2['default'].createClass({ onBlur: this.handleInputBlur, onChange: this.handleInputChange, onFocus: this.handleInputFocus, - ref: 'input', + ref: function ref(_ref) { + return _this5.input = _ref; + }, required: this.state.required, value: this.state.inputValue }); if (this.props.disabled || !this.props.searchable) { - var divProps = (0, _blacklist2['default'])(this.props.inputProps, 'inputClassName'); + var _props$inputProps = this.props.inputProps; + var inputClassName = _props$inputProps.inputClassName; + + var divProps = _objectWithoutProperties(_props$inputProps, ['inputClassName']); + return _react2['default'].createElement('div', _extends({}, divProps, { role: 'combobox', 'aria-expanded': isOpen, @@ -3594,7 +4272,9 @@ var Select = _react2['default'].createClass({ tabIndex: this.props.tabIndex || 0, onBlur: this.handleInputBlur, onFocus: this.handleInputFocus, - ref: 'input', + ref: function (ref) { + return _this5.input = ref; + }, 'aria-readonly': '' + !!this.props.disabled, style: { border: 0, width: 1, display: 'inline-block' } })); } @@ -3611,7 +4291,7 @@ var Select = _react2['default'].createClass({ }, renderClear: function renderClear() { - if (!this.props.clearable || !this.props.value || this.props.multi && !this.props.value.length || this.props.disabled || this.props.isLoading) return; + if (!this.props.clearable || !this.props.value || this.props.value === 0 || this.props.multi && !this.props.value.length || this.props.disabled || this.props.isLoading) return; return _react2['default'].createElement( 'span', { className: 'Select-clear-zone', title: this.props.multi ? this.props.clearAllText : this.props.clearValueText, @@ -3626,105 +4306,65 @@ var Select = _react2['default'].createClass({ }, renderArrow: function renderArrow() { + var onMouseDown = this.handleMouseDownOnArrow; + var arrow = this.props.arrowRenderer({ onMouseDown: onMouseDown }); + return _react2['default'].createElement( 'span', - { className: 'Select-arrow-zone', onMouseDown: this.handleMouseDownOnArrow }, - _react2['default'].createElement('span', { className: 'Select-arrow', onMouseDown: this.handleMouseDownOnArrow }) + { + className: 'Select-arrow-zone', + onMouseDown: onMouseDown + }, + arrow ); }, filterOptions: function filterOptions(excludeOptions) { - var _this5 = this; - var filterValue = this.state.inputValue; var options = this.props.options || []; - if (typeof this.props.filterOptions === 'function') { - return this.props.filterOptions.call(this, options, filterValue, excludeOptions); - } else if (this.props.filterOptions) { - if (this.props.ignoreAccents) { - filterValue = (0, _utilsStripDiacritics2['default'])(filterValue); - } - if (this.props.ignoreCase) { - filterValue = filterValue.toLowerCase(); - } - if (excludeOptions) excludeOptions = excludeOptions.map(function (i) { - return i[_this5.props.valueKey]; - }); - return options.filter(function (option) { - if (excludeOptions && excludeOptions.indexOf(option[_this5.props.valueKey]) > -1) return false; - if (_this5.props.filterOption) return _this5.props.filterOption.call(_this5, option, filterValue); - if (!filterValue) return true; - var valueTest = String(option[_this5.props.valueKey]); - var labelTest = String(option[_this5.props.labelKey]); - if (_this5.props.ignoreAccents) { - if (_this5.props.matchProp !== 'label') valueTest = (0, _utilsStripDiacritics2['default'])(valueTest); - if (_this5.props.matchProp !== 'value') labelTest = (0, _utilsStripDiacritics2['default'])(labelTest); - } - if (_this5.props.ignoreCase) { - if (_this5.props.matchProp !== 'label') valueTest = valueTest.toLowerCase(); - if (_this5.props.matchProp !== 'value') labelTest = labelTest.toLowerCase(); - } - return _this5.props.matchPos === 'start' ? _this5.props.matchProp !== 'label' && valueTest.substr(0, filterValue.length) === filterValue || _this5.props.matchProp !== 'value' && labelTest.substr(0, filterValue.length) === filterValue : _this5.props.matchProp !== 'label' && valueTest.indexOf(filterValue) >= 0 || _this5.props.matchProp !== 'value' && labelTest.indexOf(filterValue) >= 0; + if (this.props.filterOptions) { + // Maintain backwards compatibility with boolean attribute + var filterOptions = typeof this.props.filterOptions === 'function' ? this.props.filterOptions : _utilsDefaultFilterOptions2['default']; + + return filterOptions(options, filterValue, excludeOptions, { + filterOption: this.props.filterOption, + ignoreAccents: this.props.ignoreAccents, + ignoreCase: this.props.ignoreCase, + labelKey: this.props.labelKey, + matchPos: this.props.matchPos, + matchProp: this.props.matchProp, + valueKey: this.props.valueKey }); } else { return options; } }, - renderMenu: function renderMenu(options, valueArray, focusedOption) { - var _this6 = this; + onOptionRef: function onOptionRef(ref, isFocused) { + if (isFocused) { + this.focused = ref; + } + }, + renderMenu: function renderMenu(options, valueArray, focusedOption) { if (options && options.length) { - if (this.props.menuRenderer) { - return this.props.menuRenderer({ - focusedOption: focusedOption, - focusOption: this.focusOption, - labelKey: this.props.labelKey, - options: options, - selectValue: this.selectValue, - valueArray: valueArray - }); - } else { - var _ret = (function () { - var Option = _this6.props.optionComponent; - var renderLabel = _this6.props.optionRenderer || _this6.getOptionLabel; - - return { - v: options.map(function (option, i) { - var isSelected = valueArray && valueArray.indexOf(option) > -1; - var isFocused = option === focusedOption; - var optionRef = isFocused ? 'focused' : null; - var optionClass = (0, _classnames2['default'])(_this6.props.optionClassName, { - 'Select-option': true, - 'is-selected': isSelected, - 'is-focused': isFocused, - 'is-disabled': option.disabled - }); - - return _react2['default'].createElement( - Option, - { - instancePrefix: _this6._instancePrefix, - optionIndex: i, - className: optionClass, - isDisabled: option.disabled, - isFocused: isFocused, - key: 'option-' + i + '-' + option[_this6.props.valueKey], - onSelect: _this6.selectValue, - onFocus: _this6.focusOption, - option: option, - isSelected: isSelected, - ref: optionRef - }, - renderLabel(option) - ); - }) - }; - })(); - - if (typeof _ret === 'object') return _ret.v; - } - } else if (this.props.noResultsText) { + return this.props.menuRenderer({ + focusedOption: focusedOption, + focusOption: this.focusOption, + instancePrefix: this._instancePrefix, + labelKey: this.props.labelKey, + onFocus: this.focusOption, + onSelect: this.selectValue, + optionClassName: this.props.optionClassName, + optionComponent: this.props.optionComponent, + optionRenderer: this.props.optionRenderer || this.getOptionLabel, + options: options, + selectValue: this.selectValue, + valueArray: valueArray, + valueKey: this.props.valueKey, + onOptionRef: this.onOptionRef + }); + } else if (this.props.noResultsText) { return _react2['default'].createElement( 'div', { className: 'Select-noresults' }, @@ -3736,16 +4376,18 @@ var Select = _react2['default'].createClass({ }, renderHiddenField: function renderHiddenField(valueArray) { - var _this7 = this; + var _this6 = this; if (!this.props.name) return; if (this.props.joinValues) { var value = valueArray.map(function (i) { - return stringifyValue(i[_this7.props.valueKey]); + return stringifyValue(i[_this6.props.valueKey]); }).join(this.props.delimiter); return _react2['default'].createElement('input', { type: 'hidden', - ref: 'value', + ref: function (ref) { + return _this6.value = ref; + }, name: this.props.name, value: value, disabled: this.props.disabled }); @@ -3754,9 +4396,9 @@ var Select = _react2['default'].createClass({ return _react2['default'].createElement('input', { key: 'hidden.' + index, type: 'hidden', ref: 'value' + index, - name: _this7.props.name, - value: stringifyValue(item[_this7.props.valueKey]), - disabled: _this7.props.disabled }); + name: _this6.props.name, + value: stringifyValue(item[_this6.props.valueKey]), + disabled: _this6.props.disabled }); }); }, @@ -3779,6 +4421,8 @@ var Select = _react2['default'].createClass({ }, renderOuter: function renderOuter(options, valueArray, focusedOption) { + var _this7 = this; + var menu = this.renderMenu(options, valueArray, focusedOption); if (!menu) { return null; @@ -3786,10 +4430,14 @@ var Select = _react2['default'].createClass({ return _react2['default'].createElement( 'div', - { ref: 'menuContainer', className: 'Select-menu-outer', style: this.props.menuContainerStyle }, + { ref: function (ref) { + return _this7.menuContainer = ref; + }, className: 'Select-menu-outer', style: this.props.menuContainerStyle }, _react2['default'].createElement( 'div', - { ref: 'menu', role: 'listbox', className: 'Select-menu', id: this._instancePrefix + '-list', + { ref: function (ref) { + return _this7.menu = ref; + }, role: 'listbox', className: 'Select-menu', id: this._instancePrefix + '-list', style: this.props.menuStyle, onScroll: this.handleMenuScroll, onMouseDown: this.handleMouseDownOnMenu }, @@ -3799,15 +4447,17 @@ var Select = _react2['default'].createClass({ }, render: function render() { + var _this8 = this; + var valueArray = this.getValueArray(this.props.value); - var options = this._visibleOptions = this.filterOptions(this.props.multi ? valueArray : null); + var options = this._visibleOptions = this.filterOptions(this.props.multi ? this.getValueArray(this.props.value) : null); var isOpen = this.state.isOpen; if (this.props.multi && !options.length && valueArray.length && !this.state.inputValue) isOpen = false; var focusedOptionIndex = this.getFocusableOptionIndex(valueArray[0]); var focusedOption = null; if (focusedOptionIndex !== null) { - focusedOption = this._focusedOption = this._visibleOptions[focusedOptionIndex]; + focusedOption = this._focusedOption = options[focusedOptionIndex]; } else { focusedOption = this._focusedOption = null; } @@ -3834,13 +4484,17 @@ var Select = _react2['default'].createClass({ return _react2['default'].createElement( 'div', - { ref: 'wrapper', + { ref: function (ref) { + return _this8.wrapper = ref; + }, className: className, style: this.props.wrapperStyle }, this.renderHiddenField(valueArray), _react2['default'].createElement( 'div', - { ref: 'control', + { ref: function (ref) { + return _this8.control = ref; + }, className: 'Select-control', style: this.props.style, onKeyDown: this.handleKeyDown, @@ -3868,7 +4522,7 @@ var Select = _react2['default'].createClass({ exports['default'] = Select; module.exports = exports['default']; -},{"./Async":32,"./Option":33,"./Value":35,"./utils/stripDiacritics":36,"blacklist":15,"classnames":undefined,"react":undefined,"react-dom":undefined,"react-input-autosize":undefined}],35:[function(require,module,exports){ +},{"./Async":32,"./AsyncCreatable":33,"./Creatable":34,"./Option":35,"./Value":37,"./utils/defaultArrowRenderer":38,"./utils/defaultFilterOptions":39,"./utils/defaultMenuRenderer":40,"classnames":undefined,"react":undefined,"react-dom":undefined,"react-input-autosize":undefined}],37:[function(require,module,exports){ 'use strict'; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } @@ -3975,7 +4629,137 @@ var Value = _react2['default'].createClass({ }); module.exports = Value; -},{"classnames":undefined,"react":undefined}],36:[function(require,module,exports){ +},{"classnames":undefined,"react":undefined}],38:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports["default"] = arrowRenderer; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +function arrowRenderer(_ref) { + var onMouseDown = _ref.onMouseDown; + + return _react2["default"].createElement("span", { + className: "Select-arrow", + onMouseDown: onMouseDown + }); +} + +; +module.exports = exports["default"]; +},{"react":undefined}],39:[function(require,module,exports){ +'use strict'; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _stripDiacritics = require('./stripDiacritics'); + +var _stripDiacritics2 = _interopRequireDefault(_stripDiacritics); + +function filterOptions(options, filterValue, excludeOptions, props) { + var _this = this; + + if (props.ignoreAccents) { + filterValue = (0, _stripDiacritics2['default'])(filterValue); + } + + if (props.ignoreCase) { + filterValue = filterValue.toLowerCase(); + } + + if (excludeOptions) excludeOptions = excludeOptions.map(function (i) { + return i[props.valueKey]; + }); + + return options.filter(function (option) { + if (excludeOptions && excludeOptions.indexOf(option[props.valueKey]) > -1) return false; + if (props.filterOption) return props.filterOption.call(_this, option, filterValue); + if (!filterValue) return true; + var valueTest = String(option[props.valueKey]); + var labelTest = String(option[props.labelKey]); + if (props.ignoreAccents) { + if (props.matchProp !== 'label') valueTest = (0, _stripDiacritics2['default'])(valueTest); + if (props.matchProp !== 'value') labelTest = (0, _stripDiacritics2['default'])(labelTest); + } + if (props.ignoreCase) { + if (props.matchProp !== 'label') valueTest = valueTest.toLowerCase(); + if (props.matchProp !== 'value') labelTest = labelTest.toLowerCase(); + } + return props.matchPos === 'start' ? props.matchProp !== 'label' && valueTest.substr(0, filterValue.length) === filterValue || props.matchProp !== 'value' && labelTest.substr(0, filterValue.length) === filterValue : props.matchProp !== 'label' && valueTest.indexOf(filterValue) >= 0 || props.matchProp !== 'value' && labelTest.indexOf(filterValue) >= 0; + }); +} + +module.exports = filterOptions; +},{"./stripDiacritics":41}],40:[function(require,module,exports){ +'use strict'; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _classnames = require('classnames'); + +var _classnames2 = _interopRequireDefault(_classnames); + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +function menuRenderer(_ref) { + var focusedOption = _ref.focusedOption; + var instancePrefix = _ref.instancePrefix; + var labelKey = _ref.labelKey; + var onFocus = _ref.onFocus; + var onSelect = _ref.onSelect; + var optionClassName = _ref.optionClassName; + var optionComponent = _ref.optionComponent; + var optionRenderer = _ref.optionRenderer; + var options = _ref.options; + var valueArray = _ref.valueArray; + var valueKey = _ref.valueKey; + var onOptionRef = _ref.onOptionRef; + + var Option = optionComponent; + + return options.map(function (option, i) { + var isSelected = valueArray && valueArray.indexOf(option) > -1; + var isFocused = option === focusedOption; + var optionClass = (0, _classnames2['default'])(optionClassName, { + 'Select-option': true, + 'is-selected': isSelected, + 'is-focused': isFocused, + 'is-disabled': option.disabled + }); + + return _react2['default'].createElement( + Option, + { + className: optionClass, + instancePrefix: instancePrefix, + isDisabled: option.disabled, + isFocused: isFocused, + isSelected: isSelected, + key: 'option-' + i + '-' + option[valueKey], + onFocus: onFocus, + onSelect: onSelect, + option: option, + optionIndex: i, + ref: function (ref) { + onOptionRef(ref, isFocused); + } + }, + optionRenderer(option, i) + ); + }); +} + +module.exports = menuRenderer; +},{"classnames":undefined,"react":undefined}],41:[function(require,module,exports){ 'use strict'; var map = [{ 'base': 'A', 'letters': /[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g }, { 'base': 'AA', 'letters': /[\uA732]/g }, { 'base': 'AE', 'letters': /[\u00C6\u01FC\u01E2]/g }, { 'base': 'AO', 'letters': /[\uA734]/g }, { 'base': 'AU', 'letters': /[\uA736]/g }, { 'base': 'AV', 'letters': /[\uA738\uA73A]/g }, { 'base': 'AY', 'letters': /[\uA73C]/g }, { 'base': 'B', 'letters': /[\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181]/g }, { 'base': 'C', 'letters': /[\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E]/g }, { 'base': 'D', 'letters': /[\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779]/g }, { 'base': 'DZ', 'letters': /[\u01F1\u01C4]/g }, { 'base': 'Dz', 'letters': /[\u01F2\u01C5]/g }, { 'base': 'E', 'letters': /[\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E]/g }, { 'base': 'F', 'letters': /[\u0046\u24BB\uFF26\u1E1E\u0191\uA77B]/g }, { 'base': 'G', 'letters': /[\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E]/g }, { 'base': 'H', 'letters': /[\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D]/g }, { 'base': 'I', 'letters': /[\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197]/g }, { 'base': 'J', 'letters': /[\u004A\u24BF\uFF2A\u0134\u0248]/g }, { 'base': 'K', 'letters': /[\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2]/g }, { 'base': 'L', 'letters': /[\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780]/g }, { 'base': 'LJ', 'letters': /[\u01C7]/g }, { 'base': 'Lj', 'letters': /[\u01C8]/g }, { 'base': 'M', 'letters': /[\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C]/g }, { 'base': 'N', 'letters': /[\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4]/g }, { 'base': 'NJ', 'letters': /[\u01CA]/g }, { 'base': 'Nj', 'letters': /[\u01CB]/g }, { 'base': 'O', 'letters': /[\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C]/g }, { 'base': 'OI', 'letters': /[\u01A2]/g }, { 'base': 'OO', 'letters': /[\uA74E]/g }, { 'base': 'OU', 'letters': /[\u0222]/g }, { 'base': 'P', 'letters': /[\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754]/g }, { 'base': 'Q', 'letters': /[\u0051\u24C6\uFF31\uA756\uA758\u024A]/g }, { 'base': 'R', 'letters': /[\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782]/g }, { 'base': 'S', 'letters': /[\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784]/g }, { 'base': 'T', 'letters': /[\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786]/g }, { 'base': 'TZ', 'letters': /[\uA728]/g }, { 'base': 'U', 'letters': /[\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244]/g }, { 'base': 'V', 'letters': /[\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245]/g }, { 'base': 'VY', 'letters': /[\uA760]/g }, { 'base': 'W', 'letters': /[\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72]/g }, { 'base': 'X', 'letters': /[\u0058\u24CD\uFF38\u1E8A\u1E8C]/g }, { 'base': 'Y', 'letters': /[\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE]/g }, { 'base': 'Z', 'letters': /[\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762]/g }, { 'base': 'a', 'letters': /[\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250]/g }, { 'base': 'aa', 'letters': /[\uA733]/g }, { 'base': 'ae', 'letters': /[\u00E6\u01FD\u01E3]/g }, { 'base': 'ao', 'letters': /[\uA735]/g }, { 'base': 'au', 'letters': /[\uA737]/g }, { 'base': 'av', 'letters': /[\uA739\uA73B]/g }, { 'base': 'ay', 'letters': /[\uA73D]/g }, { 'base': 'b', 'letters': /[\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253]/g }, { 'base': 'c', 'letters': /[\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184]/g }, { 'base': 'd', 'letters': /[\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A]/g }, { 'base': 'dz', 'letters': /[\u01F3\u01C6]/g }, { 'base': 'e', 'letters': /[\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD]/g }, { 'base': 'f', 'letters': /[\u0066\u24D5\uFF46\u1E1F\u0192\uA77C]/g }, { 'base': 'g', 'letters': /[\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F]/g }, { 'base': 'h', 'letters': /[\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265]/g }, { 'base': 'hv', 'letters': /[\u0195]/g }, { 'base': 'i', 'letters': /[\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131]/g }, { 'base': 'j', 'letters': /[\u006A\u24D9\uFF4A\u0135\u01F0\u0249]/g }, { 'base': 'k', 'letters': /[\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3]/g }, { 'base': 'l', 'letters': /[\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747]/g }, { 'base': 'lj', 'letters': /[\u01C9]/g }, { 'base': 'm', 'letters': /[\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F]/g }, { 'base': 'n', 'letters': /[\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5]/g }, { 'base': 'nj', 'letters': /[\u01CC]/g }, { 'base': 'o', 'letters': /[\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275]/g }, { 'base': 'oi', 'letters': /[\u01A3]/g }, { 'base': 'ou', 'letters': /[\u0223]/g }, { 'base': 'oo', 'letters': /[\uA74F]/g }, { 'base': 'p', 'letters': /[\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755]/g }, { 'base': 'q', 'letters': /[\u0071\u24E0\uFF51\u024B\uA757\uA759]/g }, { 'base': 'r', 'letters': /[\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783]/g }, { 'base': 's', 'letters': /[\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B]/g }, { 'base': 't', 'letters': /[\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787]/g }, { 'base': 'tz', 'letters': /[\uA729]/g }, { 'base': 'u', 'letters': /[\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289]/g }, { 'base': 'v', 'letters': /[\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C]/g }, { 'base': 'vy', 'letters': /[\uA761]/g }, { 'base': 'w', 'letters': /[\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73]/g }, { 'base': 'x', 'letters': /[\u0078\u24E7\uFF58\u1E8B\u1E8D]/g }, { 'base': 'y', 'letters': /[\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF]/g }, { 'base': 'z', 'letters': /[\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763]/g }]; @@ -3986,7 +4770,7 @@ module.exports = function stripDiacritics(str) { } return str; }; -},{}],37:[function(require,module,exports){ +},{}],42:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { @@ -4014,14 +4798,13 @@ function _inherits(subClass, superClass) { if (typeof superClass !== "function" /** * This HOC decorates a virtualized component and responds to arrow-key events by scrolling one row or column at a time. */ - var ArrowKeyStepper = function (_Component) { _inherits(ArrowKeyStepper, _Component); function ArrowKeyStepper(props, context) { _classCallCheck(this, ArrowKeyStepper); - var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(ArrowKeyStepper).call(this, props, context)); + var _this = _possibleConstructorReturn(this, (ArrowKeyStepper.__proto__ || Object.getPrototypeOf(ArrowKeyStepper)).call(this, props, context)); _this.state = { scrollToColumn: 0, @@ -4071,8 +4854,8 @@ var ArrowKeyStepper = function (_Component) { key: '_onKeyDown', value: function _onKeyDown(event) { var _props2 = this.props; - var columnsCount = _props2.columnsCount; - var rowsCount = _props2.rowsCount; + var columnCount = _props2.columnCount; + var rowCount = _props2.rowCount; // The above cases all prevent default event event behavior. // This is to keep the grid from scrolling after the snap-to update. @@ -4081,7 +4864,7 @@ var ArrowKeyStepper = function (_Component) { case 'ArrowDown': event.preventDefault(); this.setState({ - scrollToRow: Math.min(this._rowStopIndex + 1, rowsCount - 1) + scrollToRow: Math.min(this._rowStopIndex + 1, rowCount - 1) }); break; case 'ArrowLeft': @@ -4093,7 +4876,7 @@ var ArrowKeyStepper = function (_Component) { case 'ArrowRight': event.preventDefault(); this.setState({ - scrollToColumn: Math.min(this._columnStopIndex + 1, columnsCount - 1) + scrollToColumn: Math.min(this._columnStopIndex + 1, columnCount - 1) }); break; case 'ArrowUp': @@ -4125,11 +4908,11 @@ var ArrowKeyStepper = function (_Component) { ArrowKeyStepper.propTypes = { children: _react.PropTypes.func.isRequired, className: _react.PropTypes.string, - columnsCount: _react.PropTypes.number.isRequired, - rowsCount: _react.PropTypes.number.isRequired + columnCount: _react.PropTypes.number.isRequired, + rowCount: _react.PropTypes.number.isRequired }; exports.default = ArrowKeyStepper; -},{"react":undefined,"react-addons-shallow-compare":18}],38:[function(require,module,exports){ +},{"react":undefined,"react-addons-shallow-compare":18}],43:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { @@ -4145,7 +4928,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de exports.default = _ArrowKeyStepper3.default; exports.ArrowKeyStepper = _ArrowKeyStepper3.default; -},{"./ArrowKeyStepper":37}],39:[function(require,module,exports){ +},{"./ArrowKeyStepper":42}],44:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { @@ -4175,14 +4958,13 @@ function _inherits(subClass, superClass) { if (typeof superClass !== "function" * Child component should not be declared as a child but should rather be specified by a `ChildComponent` property. * All other properties will be passed through to the child component. */ - var AutoSizer = function (_Component) { _inherits(AutoSizer, _Component); function AutoSizer(props) { _classCallCheck(this, AutoSizer); - var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(AutoSizer).call(this, props)); + var _this = _possibleConstructorReturn(this, (AutoSizer.__proto__ || Object.getPrototypeOf(AutoSizer)).call(this, props)); _this.state = { height: 0, @@ -4198,6 +4980,11 @@ var AutoSizer = function (_Component) { _createClass(AutoSizer, [{ key: 'componentDidMount', value: function componentDidMount() { + // Delay access of parentNode until mount. + // This handles edge-cases where the component has already been unmounted before its ref has been set, + // As well as libraries like react-lite which have a slightly different lifecycle. + this._parentNode = this._autoSizer.parentNode; + // Defer requiring resize handler in order to support server-side rendering. // See issue #41 this._detectElementResize = require('../vendor/detectElementResize'); @@ -4287,8 +5074,7 @@ var AutoSizer = function (_Component) { }, { key: '_setRef', value: function _setRef(autoSizer) { - // In case the component has been unmounted - this._parentNode = autoSizer && autoSizer.parentNode; + this._autoSizer = autoSizer; } }]); @@ -4316,7 +5102,7 @@ AutoSizer.defaultProps = { onResize: function onResize() {} }; exports.default = AutoSizer; -},{"../vendor/detectElementResize":71,"react":undefined,"react-addons-shallow-compare":18}],40:[function(require,module,exports){ +},{"../vendor/detectElementResize":86,"react":undefined,"react-addons-shallow-compare":18}],45:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { @@ -4332,41 +5118,33 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de exports.default = _AutoSizer3.default; exports.AutoSizer = _AutoSizer3.default; -},{"./AutoSizer":39}],41:[function(require,module,exports){ +},{"./AutoSizer":44}],46:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); -var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; - var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _react = require('react'); var _react2 = _interopRequireDefault(_react); -var _CollectionView = require('./CollectionView'); - -var _CollectionView2 = _interopRequireDefault(_CollectionView); - -var _calculateSizeAndPositionData2 = require('./utils/calculateSizeAndPositionData'); +var _reactAddonsShallowCompare = require('react-addons-shallow-compare'); -var _calculateSizeAndPositionData3 = _interopRequireDefault(_calculateSizeAndPositionData2); +var _reactAddonsShallowCompare2 = _interopRequireDefault(_reactAddonsShallowCompare); -var _getUpdatedOffsetForIndex = require('../utils/getUpdatedOffsetForIndex'); +var _reactDom = require('react-dom'); -var _getUpdatedOffsetForIndex2 = _interopRequireDefault(_getUpdatedOffsetForIndex); +var _reactDom2 = _interopRequireDefault(_reactDom); -var _reactAddonsShallowCompare = require('react-addons-shallow-compare'); +var _defaultCellSizeCache = require('./defaultCellSizeCache'); -var _reactAddonsShallowCompare2 = _interopRequireDefault(_reactAddonsShallowCompare); +var _defaultCellSizeCache2 = _interopRequireDefault(_defaultCellSizeCache); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } @@ -4374,259 +5152,431 @@ function _possibleConstructorReturn(self, call) { if (!self) { throw new Referen function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** - * Renders scattered or non-linear data. - * Unlike Grid, which renders checkerboard data, Collection can render arbitrarily positioned- even overlapping- data. + * Measures a Grid cell's contents by rendering them in a way that is not visible to the user. + * Either a fixed width or height may be provided if it is desirable to measure only in one direction. */ +var CellMeasurer = function (_Component) { + _inherits(CellMeasurer, _Component); -var Collection = function (_Component) { - _inherits(Collection, _Component); + function CellMeasurer(props, state) { + _classCallCheck(this, CellMeasurer); - function Collection(props, context) { - _classCallCheck(this, Collection); + var _this = _possibleConstructorReturn(this, (CellMeasurer.__proto__ || Object.getPrototypeOf(CellMeasurer)).call(this, props, state)); - var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(Collection).call(this, props, context)); + _this._cellSizeCache = props.cellSizeCache || new _defaultCellSizeCache2.default(); - _this._cellMetadata = []; - _this._lastRenderedCellIndices = []; + _this.getColumnWidth = _this.getColumnWidth.bind(_this); + _this.getRowHeight = _this.getRowHeight.bind(_this); + _this.resetMeasurements = _this.resetMeasurements.bind(_this); + _this.resetMeasurementForColumn = _this.resetMeasurementForColumn.bind(_this); + _this.resetMeasurementForRow = _this.resetMeasurementForRow.bind(_this); return _this; } - /** React lifecycle methods */ + _createClass(CellMeasurer, [{ + key: 'getColumnWidth', + value: function getColumnWidth(_ref) { + var index = _ref.index; - _createClass(Collection, [{ - key: 'render', - value: function render() { - var props = _objectWithoutProperties(this.props, []); + if (this._cellSizeCache.hasColumnWidth(index)) { + return this._cellSizeCache.getColumnWidth(index); + } - return _react2.default.createElement(_CollectionView2.default, _extends({ - cellLayoutManager: this, - ref: 'CollectionView' - }, props)); + var rowCount = this.props.rowCount; + + + var maxWidth = 0; + + for (var rowIndex = 0; rowIndex < rowCount; rowIndex++) { + var _measureCell2 = this._measureCell({ + clientWidth: true, + columnIndex: index, + rowIndex: rowIndex + }); + + var width = _measureCell2.width; + + + maxWidth = Math.max(maxWidth, width); + } + + this._cellSizeCache.setColumnWidth(index, maxWidth); + + return maxWidth; } }, { - key: 'shouldComponentUpdate', - value: function shouldComponentUpdate(nextProps, nextState) { - return (0, _reactAddonsShallowCompare2.default)(this, nextProps, nextState); - } + key: 'getRowHeight', + value: function getRowHeight(_ref2) { + var index = _ref2.index; - /** CellLayoutManager interface */ + if (this._cellSizeCache.hasRowHeight(index)) { + return this._cellSizeCache.getRowHeight(index); + } - }, { - key: 'calculateSizeAndPositionData', - value: function calculateSizeAndPositionData() { - var _props = this.props; - var cellCount = _props.cellCount; - var cellSizeAndPositionGetter = _props.cellSizeAndPositionGetter; - var sectionSize = _props.sectionSize; + var columnCount = this.props.columnCount; - var data = (0, _calculateSizeAndPositionData3.default)({ - cellCount: cellCount, - cellSizeAndPositionGetter: cellSizeAndPositionGetter, - sectionSize: sectionSize - }); + var maxHeight = 0; - this._cellMetadata = data.cellMetadata; - this._sectionManager = data.sectionManager; - this._height = data.height; - this._width = data.width; - } + for (var columnIndex = 0; columnIndex < columnCount; columnIndex++) { + var _measureCell3 = this._measureCell({ + clientHeight: true, + columnIndex: columnIndex, + rowIndex: index + }); - /** - * Returns the most recently rendered set of cell indices. - */ + var height = _measureCell3.height; + + + maxHeight = Math.max(maxHeight, height); + } + + this._cellSizeCache.setRowHeight(index, maxHeight); + return maxHeight; + } }, { - key: 'getLastRenderedIndices', - value: function getLastRenderedIndices() { - return this._lastRenderedCellIndices; + key: 'resetMeasurementForColumn', + value: function resetMeasurementForColumn(columnIndex) { + this._cellSizeCache.clearColumnWidth(columnIndex); + } + }, { + key: 'resetMeasurementForRow', + value: function resetMeasurementForRow(rowIndex) { + this._cellSizeCache.clearRowHeight(rowIndex); + } + }, { + key: 'resetMeasurements', + value: function resetMeasurements() { + this._cellSizeCache.clearAllColumnWidths(); + this._cellSizeCache.clearAllRowHeights(); } + }, { + key: 'componentDidMount', + value: function componentDidMount() { + this._renderAndMount(); + } + }, { + key: 'componentWillReceiveProps', + value: function componentWillReceiveProps(nextProps) { + var cellSizeCache = this.props.cellSizeCache; - /** - * Calculates the minimum amount of change from the current scroll position to ensure the specified cell is (fully) visible. - */ + if (cellSizeCache !== nextProps.cellSizeCache) { + this._cellSizeCache = nextProps.cellSizeCache; + } + + this._updateDivDimensions(nextProps); + } }, { - key: 'getScrollPositionForCell', - value: function getScrollPositionForCell(_ref) { - var cellIndex = _ref.cellIndex; - var height = _ref.height; - var scrollLeft = _ref.scrollLeft; - var scrollTop = _ref.scrollTop; - var width = _ref.width; - var cellCount = this.props.cellCount; + key: 'componentWillUnmount', + value: function componentWillUnmount() { + this._unmountContainer(); + } + }, { + key: 'render', + value: function render() { + var children = this.props.children; - if (cellIndex >= 0 && cellIndex < cellCount) { - var cellMetadata = this._cellMetadata[cellIndex]; + return children({ + getColumnWidth: this.getColumnWidth, + getRowHeight: this.getRowHeight, + resetMeasurements: this.resetMeasurements, + resetMeasurementForColumn: this.resetMeasurementForColumn, + resetMeasurementForRow: this.resetMeasurementForRow + }); + } + }, { + key: 'shouldComponentUpdate', + value: function shouldComponentUpdate(nextProps, nextState) { + return (0, _reactAddonsShallowCompare2.default)(this, nextProps, nextState); + } + }, { + key: '_getContainerNode', + value: function _getContainerNode(props) { + var container = props.container; - scrollLeft = (0, _getUpdatedOffsetForIndex2.default)({ - cellOffset: cellMetadata.x, - cellSize: cellMetadata.width, - containerSize: width, - currentOffset: scrollLeft, - targetIndex: cellIndex - }); - scrollTop = (0, _getUpdatedOffsetForIndex2.default)({ - cellOffset: cellMetadata.y, - cellSize: cellMetadata.height, - containerSize: height, - currentOffset: scrollTop, - targetIndex: cellIndex - }); + if (container) { + return _reactDom2.default.findDOMNode(typeof container === 'function' ? container() : container); + } else { + return document.body; } + } + }, { + key: '_measureCell', + value: function _measureCell(_ref3) { + var _ref3$clientHeight = _ref3.clientHeight; + var clientHeight = _ref3$clientHeight === undefined ? false : _ref3$clientHeight; + var _ref3$clientWidth = _ref3.clientWidth; + var clientWidth = _ref3$clientWidth === undefined ? true : _ref3$clientWidth; + var columnIndex = _ref3.columnIndex; + var rowIndex = _ref3.rowIndex; + var cellRenderer = this.props.cellRenderer; + + + var rendered = cellRenderer({ + columnIndex: columnIndex, + rowIndex: rowIndex + }); - return { - scrollLeft: scrollLeft, - scrollTop: scrollTop + // Handle edge case where this method is called before the CellMeasurer has completed its initial render (and mounted). + this._renderAndMount(); + + // @TODO Keep an eye on this for future React updates as the interface may change: + // https://twitter.com/soprano/status/737316379712331776 + _reactDom2.default.unstable_renderSubtreeIntoContainer(this, rendered, this._div); + + var measurements = { + height: clientHeight && this._div.clientHeight, + width: clientWidth && this._div.clientWidth }; + + _reactDom2.default.unmountComponentAtNode(this._div); + + return measurements; } }, { - key: 'getTotalSize', - value: function getTotalSize() { - return { - height: this._height, - width: this._width - }; + key: '_renderAndMount', + value: function _renderAndMount() { + if (!this._div) { + this._div = document.createElement('div'); + this._div.style.display = 'inline-block'; + this._div.style.position = 'absolute'; + this._div.style.visibility = 'hidden'; + this._div.style.zIndex = -1; + + this._updateDivDimensions(this.props); + + this._containerNode = this._getContainerNode(this.props); + this._containerNode.appendChild(this._div); + } } }, { - key: 'renderCells', - value: function renderCells(_ref2) { - var _this2 = this; + key: '_unmountContainer', + value: function _unmountContainer() { + if (this._div) { + this._containerNode.removeChild(this._div); - var height = _ref2.height; - var isScrolling = _ref2.isScrolling; - var width = _ref2.width; - var x = _ref2.x; - var y = _ref2.y; - var _props2 = this.props; - var cellGroupRenderer = _props2.cellGroupRenderer; - var cellRenderer = _props2.cellRenderer; + this._div = null; + } - // Store for later calls to getLastRenderedIndices() + this._containerNode = null; + } + }, { + key: '_updateDivDimensions', + value: function _updateDivDimensions(props) { + var height = props.height; + var width = props.width; - this._lastRenderedCellIndices = this._sectionManager.getCellIndices({ - height: height, - width: width, - x: x, - y: y - }); - return cellGroupRenderer({ - cellRenderer: cellRenderer, - cellSizeAndPositionGetter: function cellSizeAndPositionGetter(index) { - return _this2._sectionManager.getCellMetadata(index); - }, - indices: this._lastRenderedCellIndices - }); + if (height && height !== this._divHeight) { + this._divHeight = height; + this._div.style.height = height + 'px'; + } + + if (width && width !== this._divWidth) { + this._divWidth = width; + this._div.style.width = width + 'px'; + } } }]); - return Collection; + return CellMeasurer; }(_react.Component); -Collection.propTypes = { - 'aria-label': _react.PropTypes.string, +CellMeasurer.propTypes = { + /** + * Renders a cell given its indices. + * Should implement the following interface: ({ columnIndex: number, rowIndex: number }): PropTypes.node + */ + cellRenderer: _react.PropTypes.func.isRequired, /** - * Number of cells in Collection. + * Optional, custom caching strategy for cell sizes. */ - cellCount: _react.PropTypes.number.isRequired, + cellSizeCache: _react.PropTypes.object, /** - * Responsible for rendering a group of cells given their indices. - * Should implement the following interface: ({ - * cellSizeAndPositionGetter:Function, - * indices: Array, - * cellRenderer: Function - * }): Array + * Function respondible for rendering a virtualized component. + * This function should implement the following signature: + * ({ getColumnWidth, getRowHeight, resetMeasurements }) => PropTypes.element */ - cellGroupRenderer: _react.PropTypes.func.isRequired, + children: _react.PropTypes.func.isRequired, /** - * Responsible for rendering a cell given an row and column index. - * Should implement the following interface: (index: number): PropTypes.node + * Number of columns in grid. */ - cellRenderer: _react.PropTypes.func.isRequired, + columnCount: _react.PropTypes.number.isRequired, /** - * Callback responsible for returning size and offset/position information for a given cell (index). - * (index): { height: number, width: number, x: number, y: number } + * A Node, Component instance, or function that returns either. + * If this property is not specified the document body will be used. */ - cellSizeAndPositionGetter: _react.PropTypes.func.isRequired, + container: _react2.default.PropTypes.oneOfType([_react2.default.PropTypes.func, _react2.default.PropTypes.node]), /** - * Optionally override the size of the sections a Collection's cells are split into. + * Assign a fixed :height in order to measure dynamic text :width only. */ - sectionSize: _react.PropTypes.number -}; -Collection.defaultProps = { - 'aria-label': 'grid', - cellGroupRenderer: defaultCellGroupRenderer + height: _react.PropTypes.number, + + /** + * Number of rows in grid. + */ + rowCount: _react.PropTypes.number.isRequired, + + /** + * Assign a fixed :width in order to measure dynamic text :height only. + */ + width: _react.PropTypes.number }; -exports.default = Collection; +exports.default = CellMeasurer; +},{"./defaultCellSizeCache":47,"react":undefined,"react-addons-shallow-compare":18,"react-dom":undefined}],47:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { + value: true +}); -function defaultCellGroupRenderer(_ref3) { - var cellRenderer = _ref3.cellRenderer; - var cellSizeAndPositionGetter = _ref3.cellSizeAndPositionGetter; - var indices = _ref3.indices; +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - return indices.map(function (index) { - var cellMetadata = cellSizeAndPositionGetter(index); - var renderedCell = cellRenderer(index); +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - if (renderedCell == null || renderedCell === false) { - return null; +/** + * Default CellMeasurer `cellSizeCache` implementation. + * Permanently caches all cell sizes (identified by column and row index) unless explicitly cleared. + * Can be configured to handle uniform cell widths and/or heights as a way of optimizing certain use cases. + */ +var CellSizeCache = function () { + function CellSizeCache() { + var _ref = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; + + var _ref$uniformRowHeight = _ref.uniformRowHeight; + var uniformRowHeight = _ref$uniformRowHeight === undefined ? false : _ref$uniformRowHeight; + var _ref$uniformColumnWid = _ref.uniformColumnWidth; + var uniformColumnWidth = _ref$uniformColumnWid === undefined ? false : _ref$uniformColumnWid; + + _classCallCheck(this, CellSizeCache); + + this._uniformRowHeight = uniformRowHeight; + this._uniformColumnWidth = uniformColumnWidth; + + this._cachedColumnWidths = {}; + this._cachedRowHeights = {}; + } + + _createClass(CellSizeCache, [{ + key: "clearAllColumnWidths", + value: function clearAllColumnWidths() { + this._cachedColumnWidth = undefined; + this._cachedColumnWidths = {}; + } + }, { + key: "clearAllRowHeights", + value: function clearAllRowHeights() { + this._cachedRowHeight = undefined; + this._cachedRowHeights = {}; } + }, { + key: "clearColumnWidth", + value: function clearColumnWidth(index) { + this._cachedColumnWidth = undefined; - return _react2.default.createElement( - 'div', - { - className: 'Collection__cell', - key: index, - style: { - height: cellMetadata.height, - left: cellMetadata.x, - top: cellMetadata.y, - width: cellMetadata.width - } - }, - renderedCell - ); - }).filter(function (renderedCell) { - return !!renderedCell; - }); -} -},{"../utils/getUpdatedOffsetForIndex":69,"./CollectionView":42,"./utils/calculateSizeAndPositionData":46,"react":undefined,"react-addons-shallow-compare":18}],42:[function(require,module,exports){ + delete this._cachedColumnWidths[index]; + } + }, { + key: "clearRowHeight", + value: function clearRowHeight(index) { + this._cachedRowHeight = undefined; + + delete this._cachedRowHeights[index]; + } + }, { + key: "getColumnWidth", + value: function getColumnWidth(index) { + return this._uniformColumnWidth ? this._cachedColumnWidth : this._cachedColumnWidths[index]; + } + }, { + key: "getRowHeight", + value: function getRowHeight(index) { + return this._uniformRowHeight ? this._cachedRowHeight : this._cachedRowHeights[index]; + } + }, { + key: "hasColumnWidth", + value: function hasColumnWidth(index) { + return this._uniformColumnWidth ? !!this._cachedColumnWidth : !!this._cachedColumnWidths[index]; + } + }, { + key: "hasRowHeight", + value: function hasRowHeight(index) { + return this._uniformRowHeight ? !!this._cachedRowHeight : !!this._cachedRowHeights[index]; + } + }, { + key: "setColumnWidth", + value: function setColumnWidth(index, width) { + this._cachedColumnWidth = width; + this._cachedColumnWidths[index] = width; + } + }, { + key: "setRowHeight", + value: function setRowHeight(index, height) { + this._cachedRowHeight = height; + this._cachedRowHeights[index] = height; + } + }]); + + return CellSizeCache; +}(); + +exports.default = CellSizeCache; +},{}],48:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.defaultCellSizeCache = exports.CellMeasurer = exports.default = undefined; + +var _CellMeasurer2 = require('./CellMeasurer'); + +var _CellMeasurer3 = _interopRequireDefault(_CellMeasurer2); + +var _defaultCellSizeCache2 = require('./defaultCellSizeCache'); + +var _defaultCellSizeCache3 = _interopRequireDefault(_defaultCellSizeCache2); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +exports.default = _CellMeasurer3.default; +exports.CellMeasurer = _CellMeasurer3.default; +exports.defaultCellSizeCache = _defaultCellSizeCache3.default; +},{"./CellMeasurer":46,"./defaultCellSizeCache":47}],49:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _react = require('react'); var _react2 = _interopRequireDefault(_react); -var _classnames = require('classnames'); - -var _classnames2 = _interopRequireDefault(_classnames); - -var _createCallbackMemoizer = require('../utils/createCallbackMemoizer'); +var _CollectionView = require('./CollectionView'); -var _createCallbackMemoizer2 = _interopRequireDefault(_createCallbackMemoizer); +var _CollectionView2 = _interopRequireDefault(_CollectionView); -var _scrollbarSize = require('dom-helpers/util/scrollbarSize'); +var _calculateSizeAndPositionData2 = require('./utils/calculateSizeAndPositionData'); -var _scrollbarSize2 = _interopRequireDefault(_scrollbarSize); +var _calculateSizeAndPositionData3 = _interopRequireDefault(_calculateSizeAndPositionData2); -var _raf = require('raf'); +var _getUpdatedOffsetForIndex = require('../utils/getUpdatedOffsetForIndex'); -var _raf2 = _interopRequireDefault(_raf); +var _getUpdatedOffsetForIndex2 = _interopRequireDefault(_getUpdatedOffsetForIndex); var _reactAddonsShallowCompare = require('react-addons-shallow-compare'); @@ -4634,1192 +5584,1272 @@ var _reactAddonsShallowCompare2 = _interopRequireDefault(_reactAddonsShallowComp function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } -// @TODO It would be nice to refactor Grid to use this code as well. - -/** - * Specifies the number of miliseconds during which to disable pointer events while a scroll is in progress. - * This improves performance and makes scrolling smoother. - */ -var IS_SCROLLING_TIMEOUT = 150; - -/** - * Controls whether the Grid updates the DOM element's scrollLeft/scrollTop based on the current state or just observes it. - * This prevents Grid from interrupting mouse-wheel animations (see issue #2). - */ -var SCROLL_POSITION_CHANGE_REASONS = { - OBSERVED: 'observed', - REQUESTED: 'requested' -}; - /** - * Monitors changes in properties (eg. cellCount) and state (eg. scroll offsets) to determine when rendering needs to occur. - * This component does not render any visible content itself; it defers to the specified :cellLayoutManager. + * Renders scattered or non-linear data. + * Unlike Grid, which renders checkerboard data, Collection can render arbitrarily positioned- even overlapping- data. */ +var Collection = function (_Component) { + _inherits(Collection, _Component); -var CollectionView = function (_Component) { - _inherits(CollectionView, _Component); - - function CollectionView(props, context) { - _classCallCheck(this, CollectionView); + function Collection(props, context) { + _classCallCheck(this, Collection); - var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(CollectionView).call(this, props, context)); + var _this = _possibleConstructorReturn(this, (Collection.__proto__ || Object.getPrototypeOf(Collection)).call(this, props, context)); - _this.state = { - calculateSizeAndPositionDataOnNextUpdate: false, - isScrolling: false, - scrollLeft: 0, - scrollTop: 0 - }; + _this._cellMetadata = []; + _this._lastRenderedCellIndices = []; - // Invokes callbacks only when their values have changed. - _this._onSectionRenderedMemoizer = (0, _createCallbackMemoizer2.default)(); - _this._onScrollMemoizer = (0, _createCallbackMemoizer2.default)(false); + // Cell cache during scroll (for perforamnce) + _this._cellCache = []; - // Bind functions to instance so they don't lose context when passed around. - _this._invokeOnSectionRenderedHelper = _this._invokeOnSectionRenderedHelper.bind(_this); - _this._onScroll = _this._onScroll.bind(_this); - _this._updateScrollPositionForScrollToCell = _this._updateScrollPositionForScrollToCell.bind(_this); + _this._isScrollingChange = _this._isScrollingChange.bind(_this); return _this; } - /** - * Forced recompute of cell sizes and positions. - * This function should be called if cell sizes have changed but nothing else has. - * Since cell positions are calculated by callbacks, the collection view has no way of detecting when the underlying data has changed. - */ + /** See Collection#recomputeCellSizesAndPositions */ - _createClass(CollectionView, [{ + _createClass(Collection, [{ key: 'recomputeCellSizesAndPositions', value: function recomputeCellSizesAndPositions() { - this.setState({ - calculateSizeAndPositionDataOnNextUpdate: true - }); + this._cellCache = []; + this._collectionView.recomputeCellSizesAndPositions(); } - /* ---------------------------- Component lifecycle methods ---------------------------- */ + /** React lifecycle methods */ }, { - key: 'componentDidMount', - value: function componentDidMount() { - var _props = this.props; - var cellLayoutManager = _props.cellLayoutManager; - var scrollLeft = _props.scrollLeft; - var scrollToCell = _props.scrollToCell; - var scrollTop = _props.scrollTop; - - - this._scrollbarSize = (0, _scrollbarSize2.default)(); + key: 'render', + value: function render() { + var _this2 = this; - if (scrollToCell >= 0) { - this._updateScrollPositionForScrollToCell(); - } else if (scrollLeft >= 0 || scrollTop >= 0) { - this._setScrollPosition({ scrollLeft: scrollLeft, scrollTop: scrollTop }); - } + var props = _objectWithoutProperties(this.props, []); - // Update onSectionRendered callback. - this._invokeOnSectionRenderedHelper(); + return _react2.default.createElement(_CollectionView2.default, _extends({ + cellLayoutManager: this, + isScrollingChange: this._isScrollingChange, + ref: function ref(_ref) { + _this2._collectionView = _ref; + } + }, props)); + } + }, { + key: 'shouldComponentUpdate', + value: function shouldComponentUpdate(nextProps, nextState) { + return (0, _reactAddonsShallowCompare2.default)(this, nextProps, nextState); + } - var _cellLayoutManager$ge = cellLayoutManager.getTotalSize(); + /** CellLayoutManager interface */ - var totalHeight = _cellLayoutManager$ge.height; - var totalWidth = _cellLayoutManager$ge.width; + }, { + key: 'calculateSizeAndPositionData', + value: function calculateSizeAndPositionData() { + var _props = this.props; + var cellCount = _props.cellCount; + var cellSizeAndPositionGetter = _props.cellSizeAndPositionGetter; + var sectionSize = _props.sectionSize; - // Initialize onScroll callback. - this._invokeOnScrollMemoizer({ - scrollLeft: scrollLeft || 0, - scrollTop: scrollTop || 0, - totalHeight: totalHeight, - totalWidth: totalWidth + var data = (0, _calculateSizeAndPositionData3.default)({ + cellCount: cellCount, + cellSizeAndPositionGetter: cellSizeAndPositionGetter, + sectionSize: sectionSize }); + + this._cellMetadata = data.cellMetadata; + this._sectionManager = data.sectionManager; + this._height = data.height; + this._width = data.width; } - }, { - key: 'componentDidUpdate', - value: function componentDidUpdate(prevProps, prevState) { - var _props2 = this.props; - var height = _props2.height; - var scrollToCell = _props2.scrollToCell; - var width = _props2.width; - var _state = this.state; - var scrollLeft = _state.scrollLeft; - var scrollPositionChangeReason = _state.scrollPositionChangeReason; - var scrollTop = _state.scrollTop; - // Make sure requested changes to :scrollLeft or :scrollTop get applied. - // Assigning to scrollLeft/scrollTop tells the browser to interrupt any running scroll animations, - // And to discard any pending async changes to the scroll position that may have happened in the meantime (e.g. on a separate scrolling thread). - // So we only set these when we require an adjustment of the scroll position. - // See issue #2 for more information. + /** + * Returns the most recently rendered set of cell indices. + */ - if (scrollPositionChangeReason === SCROLL_POSITION_CHANGE_REASONS.REQUESTED) { - if (scrollLeft >= 0 && scrollLeft !== prevState.scrollLeft && scrollLeft !== this.refs.scrollingContainer.scrollLeft) { - this.refs.scrollingContainer.scrollLeft = scrollLeft; - } - if (scrollTop >= 0 && scrollTop !== prevState.scrollTop && scrollTop !== this.refs.scrollingContainer.scrollTop) { - this.refs.scrollingContainer.scrollTop = scrollTop; - } - } - - // Update scroll offsets if the current :scrollToCell values requires it - if (height !== prevProps.height || scrollToCell !== prevProps.scrollToCell || width !== prevProps.width) { - this._updateScrollPositionForScrollToCell(); - } - - // Update onRowsRendered callback if start/stop indices have changed - this._invokeOnSectionRenderedHelper(); - } - }, { - key: 'componentWillMount', - value: function componentWillMount() { - var cellLayoutManager = this.props.cellLayoutManager; + }, { + key: 'getLastRenderedIndices', + value: function getLastRenderedIndices() { + return this._lastRenderedCellIndices; + } + /** + * Calculates the minimum amount of change from the current scroll position to ensure the specified cell is (fully) visible. + */ - cellLayoutManager.calculateSizeAndPositionData(); - } }, { - key: 'componentWillUnmount', - value: function componentWillUnmount() { - if (this._disablePointerEventsTimeoutId) { - clearTimeout(this._disablePointerEventsTimeoutId); - } + key: 'getScrollPositionForCell', + value: function getScrollPositionForCell(_ref2) { + var align = _ref2.align; + var cellIndex = _ref2.cellIndex; + var height = _ref2.height; + var scrollLeft = _ref2.scrollLeft; + var scrollTop = _ref2.scrollTop; + var width = _ref2.width; + var cellCount = this.props.cellCount; - if (this._setNextStateAnimationFrameId) { - _raf2.default.cancel(this._setNextStateAnimationFrameId); - } - } - /** - * @private - * This method updates scrollLeft/scrollTop in state for the following conditions: - * 1) Empty content (0 rows or columns) - * 2) New scroll props overriding the current state - * 3) Cells-count or cells-size has changed, making previous scroll offsets invalid - */ + if (cellIndex >= 0 && cellIndex < cellCount) { + var cellMetadata = this._cellMetadata[cellIndex]; - }, { - key: 'componentWillUpdate', - value: function componentWillUpdate(nextProps, nextState) { - if (nextProps.cellCount === 0 && (nextState.scrollLeft !== 0 || nextState.scrollTop !== 0)) { - this._setScrollPosition({ - scrollLeft: 0, - scrollTop: 0 - }); - } else if (nextProps.scrollLeft !== this.props.scrollLeft || nextProps.scrollTop !== this.props.scrollTop) { - this._setScrollPosition({ - scrollLeft: nextProps.scrollLeft, - scrollTop: nextProps.scrollTop + scrollLeft = (0, _getUpdatedOffsetForIndex2.default)({ + align: align, + cellOffset: cellMetadata.x, + cellSize: cellMetadata.width, + containerSize: width, + currentOffset: scrollLeft, + targetIndex: cellIndex }); - } - - if (nextProps.cellCount !== this.props.cellCount || nextProps.cellLayoutManager !== this.props.cellLayoutManager || nextState.calculateSizeAndPositionDataOnNextUpdate) { - nextProps.cellLayoutManager.calculateSizeAndPositionData(); - } - if (nextState.calculateSizeAndPositionDataOnNextUpdate) { - this.setState({ - calculateSizeAndPositionDataOnNextUpdate: false + scrollTop = (0, _getUpdatedOffsetForIndex2.default)({ + align: align, + cellOffset: cellMetadata.y, + cellSize: cellMetadata.height, + containerSize: height, + currentOffset: scrollTop, + targetIndex: cellIndex }); } + + return { + scrollLeft: scrollLeft, + scrollTop: scrollTop + }; } }, { - key: 'render', - value: function render() { - var _props3 = this.props; - var cellLayoutManager = _props3.cellLayoutManager; - var className = _props3.className; - var height = _props3.height; - var noContentRenderer = _props3.noContentRenderer; - var width = _props3.width; - var _state2 = this.state; - var isScrolling = _state2.isScrolling; - var scrollLeft = _state2.scrollLeft; - var scrollTop = _state2.scrollTop; - - - var childrenToDisplay = height > 0 && width > 0 ? cellLayoutManager.renderCells({ - height: height, - isScrolling: isScrolling, - width: width, - x: scrollLeft, - y: scrollTop - }) : []; - - var _cellLayoutManager$ge2 = cellLayoutManager.getTotalSize(); + key: 'getTotalSize', + value: function getTotalSize() { + return { + height: this._height, + width: this._width + }; + } + }, { + key: 'cellRenderers', + value: function cellRenderers(_ref3) { + var _this3 = this; - var totalHeight = _cellLayoutManager$ge2.height; - var totalWidth = _cellLayoutManager$ge2.width; + var height = _ref3.height; + var isScrolling = _ref3.isScrolling; + var width = _ref3.width; + var x = _ref3.x; + var y = _ref3.y; + var _props2 = this.props; + var cellGroupRenderer = _props2.cellGroupRenderer; + var cellRenderer = _props2.cellRenderer; + // Store for later calls to getLastRenderedIndices() - var gridStyle = { + this._lastRenderedCellIndices = this._sectionManager.getCellIndices({ height: height, - width: width - }; - - // Force browser to hide scrollbars when we know they aren't necessary. - // Otherwise once scrollbars appear they may not disappear again. - // For more info see issue #116 - if (totalHeight <= height) { - gridStyle.overflowY = 'hidden'; - } - if (totalWidth <= width) { - gridStyle.overflowX = 'hidden'; - } + width: width, + x: x, + y: y + }); - return _react2.default.createElement( - 'div', - { - ref: 'scrollingContainer', - 'aria-label': this.props['aria-label'], - className: (0, _classnames2.default)('Collection', className), - onScroll: this._onScroll, - role: 'grid', - style: gridStyle, - tabIndex: 0 + return cellGroupRenderer({ + cellCache: this._cellCache, + cellRenderer: cellRenderer, + cellSizeAndPositionGetter: function cellSizeAndPositionGetter(_ref4) { + var index = _ref4.index; + return _this3._sectionManager.getCellMetadata({ index: index }); }, - childrenToDisplay.length > 0 && _react2.default.createElement( - 'div', - { - className: 'Collection__innerScrollContainer', - style: { - height: totalHeight, - maxHeight: totalHeight, - maxWidth: totalWidth, - pointerEvents: isScrolling ? 'none' : 'auto', - width: totalWidth - } - }, - childrenToDisplay - ), - childrenToDisplay.length === 0 && noContentRenderer() - ); + indices: this._lastRenderedCellIndices, + isScrolling: isScrolling + }); } }, { - key: 'shouldComponentUpdate', - value: function shouldComponentUpdate(nextProps, nextState) { - return (0, _reactAddonsShallowCompare2.default)(this, nextProps, nextState); + key: '_isScrollingChange', + value: function _isScrollingChange(isScrolling) { + if (!isScrolling) { + this._cellCache = []; + } } + }]); - /* ---------------------------- Helper methods ---------------------------- */ + return Collection; +}(_react.Component); - /** - * Sets an :isScrolling flag for a small window of time. - * This flag is used to disable pointer events on the scrollable portion of the Collection. - * This prevents jerky/stuttery mouse-wheel scrolling. - */ +Collection.propTypes = { + 'aria-label': _react.PropTypes.string, - }, { - key: '_enablePointerEventsAfterDelay', - value: function _enablePointerEventsAfterDelay() { - var _this2 = this; + /** + * Number of cells in Collection. + */ + cellCount: _react.PropTypes.number.isRequired, - if (this._disablePointerEventsTimeoutId) { - clearTimeout(this._disablePointerEventsTimeoutId); - } + /** + * Responsible for rendering a group of cells given their indices. + * Should implement the following interface: ({ + * cellSizeAndPositionGetter:Function, + * indices: Array, + * cellRenderer: Function + * }): Array + */ + cellGroupRenderer: _react.PropTypes.func.isRequired, - this._disablePointerEventsTimeoutId = setTimeout(function () { - _this2._disablePointerEventsTimeoutId = null; - _this2.setState({ - isScrolling: false - }); - }, IS_SCROLLING_TIMEOUT); - } - }, { - key: '_invokeOnSectionRenderedHelper', - value: function _invokeOnSectionRenderedHelper() { - var _props4 = this.props; - var cellLayoutManager = _props4.cellLayoutManager; - var onSectionRendered = _props4.onSectionRendered; + /** + * Responsible for rendering a cell given an row and column index. + * Should implement the following interface: ({ index: number }): PropTypes.element + */ + cellRenderer: _react.PropTypes.func.isRequired, + /** + * Callback responsible for returning size and offset/position information for a given cell (index). + * ({ index: number }): { height: number, width: number, x: number, y: number } + */ + cellSizeAndPositionGetter: _react.PropTypes.func.isRequired, - this._onSectionRenderedMemoizer({ - callback: onSectionRendered, - indices: cellLayoutManager.getLastRenderedIndices() - }); - } - }, { - key: '_invokeOnScrollMemoizer', - value: function _invokeOnScrollMemoizer(_ref) { - var _this3 = this; + /** + * Optionally override the size of the sections a Collection's cells are split into. + */ + sectionSize: _react.PropTypes.number +}; +Collection.defaultProps = { + 'aria-label': 'grid', + cellGroupRenderer: defaultCellGroupRenderer +}; +exports.default = Collection; - var scrollLeft = _ref.scrollLeft; - var scrollTop = _ref.scrollTop; - var totalHeight = _ref.totalHeight; - var totalWidth = _ref.totalWidth; - this._onScrollMemoizer({ - callback: function callback(_ref2) { - var scrollLeft = _ref2.scrollLeft; - var scrollTop = _ref2.scrollTop; - var _props5 = _this3.props; - var height = _props5.height; - var onScroll = _props5.onScroll; - var width = _props5.width; +function defaultCellGroupRenderer(_ref5) { + var cellCache = _ref5.cellCache; + var cellRenderer = _ref5.cellRenderer; + var cellSizeAndPositionGetter = _ref5.cellSizeAndPositionGetter; + var indices = _ref5.indices; + var isScrolling = _ref5.isScrolling; + return indices.map(function (index) { + var cellMetadata = cellSizeAndPositionGetter({ index: index }); + + // Avoid re-creating cells while scrolling. + // This can lead to the same cell being created many times and can cause performance issues for "heavy" cells. + // If a scroll is in progress- cache and reuse cells. + // This cache will be thrown away once scrolling complets. + var renderedCell = void 0; + + if (isScrolling) { + if (!(index in cellCache)) { + cellCache[index] = cellRenderer({ + index: index, + isScrolling: isScrolling + }); + } - onScroll({ - clientHeight: height, - clientWidth: width, - scrollHeight: totalHeight, - scrollLeft: scrollLeft, - scrollTop: scrollTop, - scrollWidth: totalWidth - }); - }, - indices: { - scrollLeft: scrollLeft, - scrollTop: scrollTop - } + renderedCell = cellCache[index]; + } else { + renderedCell = cellRenderer({ + index: index, + isScrolling: isScrolling }); } - /** - * Updates the state during the next animation frame. - * Use this method to avoid multiple renders in a small span of time. - * This helps performance for bursty events (like onScroll). - */ + if (renderedCell == null || renderedCell === false) { + return null; + } - }, { - key: '_setNextState', - value: function _setNextState(state) { - var _this4 = this; - - if (this._setNextStateAnimationFrameId) { - _raf2.default.cancel(this._setNextStateAnimationFrameId); - } + return _react2.default.createElement( + 'div', + { + className: 'Collection__cell', + key: index, + style: { + height: cellMetadata.height, + left: cellMetadata.x, + top: cellMetadata.y, + width: cellMetadata.width + } + }, + renderedCell + ); + }).filter(function (renderedCell) { + return !!renderedCell; + }); +} +},{"../utils/getUpdatedOffsetForIndex":85,"./CollectionView":50,"./utils/calculateSizeAndPositionData":54,"react":undefined,"react-addons-shallow-compare":18}],50:[function(require,module,exports){ +'use strict'; - this._setNextStateAnimationFrameId = (0, _raf2.default)(function () { - _this4._setNextStateAnimationFrameId = null; - _this4.setState(state); - }); - } - }, { - key: '_setScrollPosition', - value: function _setScrollPosition(_ref3) { - var scrollLeft = _ref3.scrollLeft; - var scrollTop = _ref3.scrollTop; +Object.defineProperty(exports, "__esModule", { + value: true +}); - var newState = { - scrollPositionChangeReason: SCROLL_POSITION_CHANGE_REASONS.REQUESTED - }; +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; - if (scrollLeft >= 0) { - newState.scrollLeft = scrollLeft; - } +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - if (scrollTop >= 0) { - newState.scrollTop = scrollTop; - } +var _react = require('react'); - if (scrollLeft >= 0 && scrollLeft !== this.state.scrollLeft || scrollTop >= 0 && scrollTop !== this.state.scrollTop) { - this.setState(newState); - } - } - }, { - key: '_updateScrollPositionForScrollToCell', - value: function _updateScrollPositionForScrollToCell() { - var _props6 = this.props; - var cellLayoutManager = _props6.cellLayoutManager; - var height = _props6.height; - var scrollToCell = _props6.scrollToCell; - var width = _props6.width; - var _state3 = this.state; - var scrollLeft = _state3.scrollLeft; - var scrollTop = _state3.scrollTop; +var _react2 = _interopRequireDefault(_react); +var _classnames = require('classnames'); - if (scrollToCell >= 0) { - var scrollPosition = cellLayoutManager.getScrollPositionForCell({ - cellIndex: scrollToCell, - height: height, - scrollLeft: scrollLeft, - scrollTop: scrollTop, - width: width - }); +var _classnames2 = _interopRequireDefault(_classnames); - if (scrollPosition.scrollLeft !== scrollLeft || scrollPosition.scrollTop !== scrollTop) { - this._setScrollPosition(scrollPosition); - } - } - } - }, { - key: '_onScroll', - value: function _onScroll(event) { - // In certain edge-cases React dispatches an onScroll event with an invalid target.scrollLeft / target.scrollTop. - // This invalid event can be detected by comparing event.target to this component's scrollable DOM element. - // See issue #404 for more information. - if (event.target !== this.refs.scrollingContainer) { - return; - } +var _createCallbackMemoizer = require('../utils/createCallbackMemoizer'); - // Prevent pointer events from interrupting a smooth scroll - this._enablePointerEventsAfterDelay(); +var _createCallbackMemoizer2 = _interopRequireDefault(_createCallbackMemoizer); - // When this component is shrunk drastically, React dispatches a series of back-to-back scroll events, - // Gradually converging on a scrollTop that is within the bounds of the new, smaller height. - // This causes a series of rapid renders that is slow for long lists. - // We can avoid that by doing some simple bounds checking to ensure that scrollTop never exceeds the total height. - var _props7 = this.props; - var cellLayoutManager = _props7.cellLayoutManager; - var height = _props7.height; - var width = _props7.width; +var _scrollbarSize = require('dom-helpers/util/scrollbarSize'); - var scrollbarSize = this._scrollbarSize; +var _scrollbarSize2 = _interopRequireDefault(_scrollbarSize); - var _cellLayoutManager$ge3 = cellLayoutManager.getTotalSize(); +var _raf = require('raf'); - var totalHeight = _cellLayoutManager$ge3.height; - var totalWidth = _cellLayoutManager$ge3.width; +var _raf2 = _interopRequireDefault(_raf); - var scrollLeft = Math.min(totalWidth - width + scrollbarSize, event.target.scrollLeft); - var scrollTop = Math.min(totalHeight - height + scrollbarSize, event.target.scrollTop); +var _reactAddonsShallowCompare = require('react-addons-shallow-compare'); - // Certain devices (like Apple touchpad) rapid-fire duplicate events. - // Don't force a re-render if this is the case. - // The mouse may move faster then the animation frame does. - // Use requestAnimationFrame to avoid over-updating. - if (this.state.scrollLeft !== scrollLeft || this.state.scrollTop !== scrollTop) { - // Browsers with cancelable scroll events (eg. Firefox) interrupt scrolling animations if scrollTop/scrollLeft is set. - // Other browsers (eg. Safari) don't scroll as well without the help under certain conditions (DOM or style changes during scrolling). - // All things considered, this seems to be the best current work around that I'm aware of. - // For more information see https://github.com/bvaughn/react-virtualized/pull/124 - var scrollPositionChangeReason = event.cancelable ? SCROLL_POSITION_CHANGE_REASONS.OBSERVED : SCROLL_POSITION_CHANGE_REASONS.REQUESTED; +var _reactAddonsShallowCompare2 = _interopRequireDefault(_reactAddonsShallowCompare); - // Synchronously set :isScrolling the first time (since _setNextState will reschedule its animation frame each time it's called) - if (!this.state.isScrolling) { - this.setState({ - isScrolling: true - }); - } +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - this._setNextState({ - isScrolling: true, - scrollLeft: scrollLeft, - scrollPositionChangeReason: scrollPositionChangeReason, - scrollTop: scrollTop - }); - } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - this._invokeOnScrollMemoizer({ - scrollLeft: scrollLeft, - scrollTop: scrollTop, - totalWidth: totalWidth, - totalHeight: totalHeight - }); - } - }]); +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } - return CollectionView; -}(_react.Component); +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } -CollectionView.propTypes = { - 'aria-label': _react.PropTypes.string, +// @TODO It would be nice to refactor Grid to use this code as well. - /** - * Number of cells in collection. - */ - cellCount: _react.PropTypes.number.isRequired, +/** + * Specifies the number of miliseconds during which to disable pointer events while a scroll is in progress. + * This improves performance and makes scrolling smoother. + */ +var IS_SCROLLING_TIMEOUT = 150; - /** - * Calculates cell sizes and positions and manages rendering the appropriate cells given a specified window. - */ - cellLayoutManager: _react.PropTypes.object.isRequired, +/** + * Controls whether the Grid updates the DOM element's scrollLeft/scrollTop based on the current state or just observes it. + * This prevents Grid from interrupting mouse-wheel animations (see issue #2). + */ +var SCROLL_POSITION_CHANGE_REASONS = { + OBSERVED: 'observed', + REQUESTED: 'requested' +}; - /** - * Optional custom CSS class name to attach to root Collection element. - */ - className: _react.PropTypes.string, +/** + * Monitors changes in properties (eg. cellCount) and state (eg. scroll offsets) to determine when rendering needs to occur. + * This component does not render any visible content itself; it defers to the specified :cellLayoutManager. + */ - /** - * Height of Collection; this property determines the number of visible (vs virtualized) rows. - */ - height: _react.PropTypes.number.isRequired, +var CollectionView = function (_Component) { + _inherits(CollectionView, _Component); - /** - * Optional renderer to be used in place of rows when either :rowsCount or :cellCount is 0. - */ - noContentRenderer: _react.PropTypes.func.isRequired, + function CollectionView(props, context) { + _classCallCheck(this, CollectionView); - /** - * Callback invoked whenever the scroll offset changes within the inner scrollable region. - * This callback can be used to sync scrolling between lists, tables, or grids. - * ({ clientHeight, clientWidth, scrollHeight, scrollLeft, scrollTop, scrollWidth }): void - */ - onScroll: _react.PropTypes.func.isRequired, + var _this = _possibleConstructorReturn(this, (CollectionView.__proto__ || Object.getPrototypeOf(CollectionView)).call(this, props, context)); - /** - * Callback invoked with information about the section of the Collection that was just rendered. - * This callback is passed an array of the most recently rendered section indices. - */ - onSectionRendered: _react.PropTypes.func.isRequired, + _this.state = { + calculateSizeAndPositionDataOnNextUpdate: false, + isScrolling: false, + scrollLeft: 0, + scrollTop: 0 + }; - /** - * Horizontal offset. - */ - scrollLeft: _react.PropTypes.number, + // Invokes callbacks only when their values have changed. + _this._onSectionRenderedMemoizer = (0, _createCallbackMemoizer2.default)(); + _this._onScrollMemoizer = (0, _createCallbackMemoizer2.default)(false); - /** - * Cell index to ensure visible (by forcefully scrolling if necessary). - */ - scrollToCell: _react.PropTypes.number, + // Bind functions to instance so they don't lose context when passed around. + _this._invokeOnSectionRenderedHelper = _this._invokeOnSectionRenderedHelper.bind(_this); + _this._onScroll = _this._onScroll.bind(_this); + _this._updateScrollPositionForScrollToCell = _this._updateScrollPositionForScrollToCell.bind(_this); + return _this; + } /** - * Vertical offset. + * Forced recompute of cell sizes and positions. + * This function should be called if cell sizes have changed but nothing else has. + * Since cell positions are calculated by callbacks, the collection view has no way of detecting when the underlying data has changed. */ - scrollTop: _react.PropTypes.number, - /** - * Width of Collection; this property determines the number of visible (vs virtualized) columns. - */ - width: _react.PropTypes.number.isRequired -}; -CollectionView.defaultProps = { - 'aria-label': 'grid', - noContentRenderer: function noContentRenderer() { - return null; - }, - onScroll: function onScroll() { - return null; - }, - onSectionRendered: function onSectionRendered() { - return null; - } -}; -exports.default = CollectionView; -},{"../utils/createCallbackMemoizer":68,"classnames":undefined,"dom-helpers/util/scrollbarSize":73,"raf":74,"react":undefined,"react-addons-shallow-compare":18}],43:[function(require,module,exports){ -'use strict'; -Object.defineProperty(exports, "__esModule", { - value: true -}); + _createClass(CollectionView, [{ + key: 'recomputeCellSizesAndPositions', + value: function recomputeCellSizesAndPositions() { + this.setState({ + calculateSizeAndPositionDataOnNextUpdate: true + }); + } -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + /* ---------------------------- Component lifecycle methods ---------------------------- */ -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + }, { + key: 'componentDidMount', + value: function componentDidMount() { + var _props = this.props; + var cellLayoutManager = _props.cellLayoutManager; + var scrollLeft = _props.scrollLeft; + var scrollToCell = _props.scrollToCell; + var scrollTop = _props.scrollTop; -/** - * A section of the Window. - * Window Sections are used to group nearby cells. - * This enables us to more quickly determine which cells to display in a given region of the Window. - * Sections have a fixed size and contain 0 to many cells (tracked by their indices). - */ + // If this component was first rendered server-side, scrollbar size will be undefined. + // In that event we need to remeasure. -var Section = function () { - function Section(_ref) { - var height = _ref.height; - var width = _ref.width; - var x = _ref.x; - var y = _ref.y; + if (!this._scrollbarSizeMeasured) { + this._scrollbarSize = (0, _scrollbarSize2.default)(); + this._scrollbarSizeMeasured = true; + this.setState({}); + } - _classCallCheck(this, Section); + if (scrollToCell >= 0) { + this._updateScrollPositionForScrollToCell(); + } else if (scrollLeft >= 0 || scrollTop >= 0) { + this._setScrollPosition({ scrollLeft: scrollLeft, scrollTop: scrollTop }); + } - this.height = height; - this.width = width; - this.x = x; - this.y = y; + // Update onSectionRendered callback. + this._invokeOnSectionRenderedHelper(); - this._indexMap = {}; - this._indices = []; - } + var _cellLayoutManager$ge = cellLayoutManager.getTotalSize(); - /** Add a cell to this section. */ + var totalHeight = _cellLayoutManager$ge.height; + var totalWidth = _cellLayoutManager$ge.width; + // Initialize onScroll callback. - _createClass(Section, [{ - key: 'addCellIndex', - value: function addCellIndex(index) { - if (!this._indexMap[index]) { - this._indexMap[index] = true; - this._indices.push(index); - } + this._invokeOnScrollMemoizer({ + scrollLeft: scrollLeft || 0, + scrollTop: scrollTop || 0, + totalHeight: totalHeight, + totalWidth: totalWidth + }); } + }, { + key: 'componentDidUpdate', + value: function componentDidUpdate(prevProps, prevState) { + var _props2 = this.props; + var height = _props2.height; + var scrollToCell = _props2.scrollToCell; + var width = _props2.width; + var _state = this.state; + var scrollLeft = _state.scrollLeft; + var scrollPositionChangeReason = _state.scrollPositionChangeReason; + var scrollToAlignment = _state.scrollToAlignment; + var scrollTop = _state.scrollTop; - /** Get all cell indices that have been added to this section. */ + // Make sure requested changes to :scrollLeft or :scrollTop get applied. + // Assigning to scrollLeft/scrollTop tells the browser to interrupt any running scroll animations, + // And to discard any pending async changes to the scroll position that may have happened in the meantime (e.g. on a separate scrolling thread). + // So we only set these when we require an adjustment of the scroll position. + // See issue #2 for more information. - }, { - key: 'getCellIndices', - value: function getCellIndices() { - return this._indices; + if (scrollPositionChangeReason === SCROLL_POSITION_CHANGE_REASONS.REQUESTED) { + if (scrollLeft >= 0 && scrollLeft !== prevState.scrollLeft && scrollLeft !== this._scrollingContainer.scrollLeft) { + this._scrollingContainer.scrollLeft = scrollLeft; + } + if (scrollTop >= 0 && scrollTop !== prevState.scrollTop && scrollTop !== this._scrollingContainer.scrollTop) { + this._scrollingContainer.scrollTop = scrollTop; + } + } + + // Update scroll offsets if the current :scrollToCell values requires it + if (height !== prevProps.height || scrollToAlignment !== prevProps.scrollToAlignment || scrollToCell !== prevProps.scrollToCell || width !== prevProps.width) { + this._updateScrollPositionForScrollToCell(); + } + + // Update onRowsRendered callback if start/stop indices have changed + this._invokeOnSectionRenderedHelper(); } + }, { + key: 'componentWillMount', + value: function componentWillMount() { + var cellLayoutManager = this.props.cellLayoutManager; - /** Intended for debugger/test purposes only */ + cellLayoutManager.calculateSizeAndPositionData(); + + // If this component is being rendered server-side, getScrollbarSize() will return undefined. + // We handle this case in componentDidMount() + this._scrollbarSize = (0, _scrollbarSize2.default)(); + if (this._scrollbarSize === undefined) { + this._scrollbarSizeMeasured = false; + this._scrollbarSize = 0; + } else { + this._scrollbarSizeMeasured = true; + } + } }, { - key: 'toString', - value: function toString() { - return this.x + ',' + this.y + ' ' + this.width + 'x' + this.height; + key: 'componentWillUnmount', + value: function componentWillUnmount() { + if (this._disablePointerEventsTimeoutId) { + clearTimeout(this._disablePointerEventsTimeoutId); + } + + if (this._setNextStateAnimationFrameId) { + _raf2.default.cancel(this._setNextStateAnimationFrameId); + } } - }]); - return Section; -}(); /** @rlow */ + /** + * @private + * This method updates scrollLeft/scrollTop in state for the following conditions: + * 1) Empty content (0 rows or columns) + * 2) New scroll props overriding the current state + * 3) Cells-count or cells-size has changed, making previous scroll offsets invalid + */ + }, { + key: 'componentWillUpdate', + value: function componentWillUpdate(nextProps, nextState) { + if (nextProps.cellCount === 0 && (nextState.scrollLeft !== 0 || nextState.scrollTop !== 0)) { + this._setScrollPosition({ + scrollLeft: 0, + scrollTop: 0 + }); + } else if (nextProps.scrollLeft !== this.props.scrollLeft || nextProps.scrollTop !== this.props.scrollTop) { + this._setScrollPosition({ + scrollLeft: nextProps.scrollLeft, + scrollTop: nextProps.scrollTop + }); + } -exports.default = Section; -},{}],44:[function(require,module,exports){ -'use strict'; + if (nextProps.cellCount !== this.props.cellCount || nextProps.cellLayoutManager !== this.props.cellLayoutManager || nextState.calculateSizeAndPositionDataOnNextUpdate) { + nextProps.cellLayoutManager.calculateSizeAndPositionData(); + } -Object.defineProperty(exports, "__esModule", { - value: true -}); + if (nextState.calculateSizeAndPositionDataOnNextUpdate) { + this.setState({ + calculateSizeAndPositionDataOnNextUpdate: false + }); + } + } + }, { + key: 'render', + value: function render() { + var _this2 = this; -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /** - * Window Sections are used to group nearby cells. - * This enables us to more quickly determine which cells to display in a given region of the Window. - * - */ + var _props3 = this.props; + var autoHeight = _props3.autoHeight; + var cellCount = _props3.cellCount; + var cellLayoutManager = _props3.cellLayoutManager; + var className = _props3.className; + var height = _props3.height; + var horizontalOverscanSize = _props3.horizontalOverscanSize; + var noContentRenderer = _props3.noContentRenderer; + var style = _props3.style; + var verticalOverscanSize = _props3.verticalOverscanSize; + var width = _props3.width; + var _state2 = this.state; + var isScrolling = _state2.isScrolling; + var scrollLeft = _state2.scrollLeft; + var scrollTop = _state2.scrollTop; + var _cellLayoutManager$ge2 = cellLayoutManager.getTotalSize(); -var _Section = require('./Section'); + var totalHeight = _cellLayoutManager$ge2.height; + var totalWidth = _cellLayoutManager$ge2.width; -var _Section2 = _interopRequireDefault(_Section); + // Safely expand the rendered area by the specified overscan amount -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + var left = Math.max(0, scrollLeft - horizontalOverscanSize); + var top = Math.max(0, scrollTop - verticalOverscanSize); + var right = Math.min(totalWidth, scrollLeft + width + horizontalOverscanSize); + var bottom = Math.min(totalHeight, scrollTop + height + verticalOverscanSize); -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + var childrenToDisplay = height > 0 && width > 0 ? cellLayoutManager.cellRenderers({ + height: bottom - top, + isScrolling: isScrolling, + width: right - left, + x: left, + y: top + }) : []; -var SECTION_SIZE = 100; + var collectionStyle = { + height: autoHeight ? 'auto' : height, + width: width + }; -/** - * Contains 0 to many Sections. - * Grows (and adds Sections) dynamically as cells are registered. - * Automatically adds cells to the appropriate Section(s). - */ + // Force browser to hide scrollbars when we know they aren't necessary. + // Otherwise once scrollbars appear they may not disappear again. + // For more info see issue #116 + var verticalScrollBarSize = totalHeight > height ? this._scrollbarSize : 0; + var horizontalScrollBarSize = totalWidth > width ? this._scrollbarSize : 0; + if (totalWidth + verticalScrollBarSize <= width) { + collectionStyle.overflowX = 'hidden'; + } + if (totalHeight + horizontalScrollBarSize <= height) { + collectionStyle.overflowY = 'hidden'; + } -var SectionManager = function () { - function SectionManager() { - var sectionSize = arguments.length <= 0 || arguments[0] === undefined ? SECTION_SIZE : arguments[0]; + return _react2.default.createElement( + 'div', + { + ref: function ref(_ref) { + _this2._scrollingContainer = _ref; + }, + 'aria-label': this.props['aria-label'], + className: (0, _classnames2.default)('Collection', className), + onScroll: this._onScroll, + role: 'grid', + style: _extends({}, collectionStyle, style), + tabIndex: 0 + }, + cellCount > 0 && _react2.default.createElement( + 'div', + { + className: 'Collection__innerScrollContainer', + style: { + height: totalHeight, + maxHeight: totalHeight, + maxWidth: totalWidth, + pointerEvents: isScrolling ? 'none' : '', + width: totalWidth + } + }, + childrenToDisplay + ), + cellCount === 0 && noContentRenderer() + ); + } + }, { + key: 'shouldComponentUpdate', + value: function shouldComponentUpdate(nextProps, nextState) { + return (0, _reactAddonsShallowCompare2.default)(this, nextProps, nextState); + } - _classCallCheck(this, SectionManager); + /* ---------------------------- Helper methods ---------------------------- */ - this._sectionSize = sectionSize; + /** + * Sets an :isScrolling flag for a small window of time. + * This flag is used to disable pointer events on the scrollable portion of the Collection. + * This prevents jerky/stuttery mouse-wheel scrolling. + */ - this._cellMetadata = []; - this._sections = {}; - } + }, { + key: '_enablePointerEventsAfterDelay', + value: function _enablePointerEventsAfterDelay() { + var _this3 = this; - /** - * Gets all cell indices contained in the specified region. - * A region may encompass 1 or more Sections. - */ + if (this._disablePointerEventsTimeoutId) { + clearTimeout(this._disablePointerEventsTimeoutId); + } + this._disablePointerEventsTimeoutId = setTimeout(function () { + var isScrollingChange = _this3.props.isScrollingChange; - _createClass(SectionManager, [{ - key: 'getCellIndices', - value: function getCellIndices(_ref) { - var height = _ref.height; - var width = _ref.width; - var x = _ref.x; - var y = _ref.y; - var indices = {}; + isScrollingChange(false); - this.getSections({ height: height, width: width, x: x, y: y }).forEach(function (section) { - return section.getCellIndices().forEach(function (index) { - return indices[index] = index; + _this3._disablePointerEventsTimeoutId = null; + _this3.setState({ + isScrolling: false }); - }); - - // Object keys are strings; this function returns numbers - return Object.keys(indices).map(function (index) { - return indices[index]; - }); + }, IS_SCROLLING_TIMEOUT); } - - /** Get size and position information for the cell specified. */ - }, { - key: 'getCellMetadata', - value: function getCellMetadata(index) { - return this._cellMetadata[index]; - } + key: '_invokeOnSectionRenderedHelper', + value: function _invokeOnSectionRenderedHelper() { + var _props4 = this.props; + var cellLayoutManager = _props4.cellLayoutManager; + var onSectionRendered = _props4.onSectionRendered; - /** Get all Sections overlapping the specified region. */ + this._onSectionRenderedMemoizer({ + callback: onSectionRendered, + indices: { + indices: cellLayoutManager.getLastRenderedIndices() + } + }); + } }, { - key: 'getSections', - value: function getSections(_ref2) { - var height = _ref2.height; - var width = _ref2.width; - var x = _ref2.x; - var y = _ref2.y; - - var sectionXStart = Math.floor(x / this._sectionSize); - var sectionXStop = Math.floor((x + width - 1) / this._sectionSize); - var sectionYStart = Math.floor(y / this._sectionSize); - var sectionYStop = Math.floor((y + height - 1) / this._sectionSize); + key: '_invokeOnScrollMemoizer', + value: function _invokeOnScrollMemoizer(_ref2) { + var _this4 = this; - var sections = []; + var scrollLeft = _ref2.scrollLeft; + var scrollTop = _ref2.scrollTop; + var totalHeight = _ref2.totalHeight; + var totalWidth = _ref2.totalWidth; - for (var sectionX = sectionXStart; sectionX <= sectionXStop; sectionX++) { - for (var sectionY = sectionYStart; sectionY <= sectionYStop; sectionY++) { - var key = sectionX + '.' + sectionY; + this._onScrollMemoizer({ + callback: function callback(_ref3) { + var scrollLeft = _ref3.scrollLeft; + var scrollTop = _ref3.scrollTop; + var _props5 = _this4.props; + var height = _props5.height; + var onScroll = _props5.onScroll; + var width = _props5.width; - if (!this._sections[key]) { - this._sections[key] = new _Section2.default({ - height: this._sectionSize, - width: this._sectionSize, - x: sectionX * this._sectionSize, - y: sectionY * this._sectionSize - }); - } - sections.push(this._sections[key]); + onScroll({ + clientHeight: height, + clientWidth: width, + scrollHeight: totalHeight, + scrollLeft: scrollLeft, + scrollTop: scrollTop, + scrollWidth: totalWidth + }); + }, + indices: { + scrollLeft: scrollLeft, + scrollTop: scrollTop } - } - - return sections; + }); } - /** Total number of Sections based on the currently registered cells. */ + /** + * Updates the state during the next animation frame. + * Use this method to avoid multiple renders in a small span of time. + * This helps performance for bursty events (like onScroll). + */ }, { - key: 'getTotalSectionCount', - value: function getTotalSectionCount() { - return Object.keys(this._sections).length; - } - - /** Intended for debugger/test purposes only */ + key: '_setNextState', + value: function _setNextState(state) { + var _this5 = this; - }, { - key: 'toString', - value: function toString() { - var _this = this; + if (this._setNextStateAnimationFrameId) { + _raf2.default.cancel(this._setNextStateAnimationFrameId); + } - return Object.keys(this._sections).map(function (index) { - return _this._sections[index].toString(); + this._setNextStateAnimationFrameId = (0, _raf2.default)(function () { + _this5._setNextStateAnimationFrameId = null; + _this5.setState(state); }); } + }, { + key: '_setScrollPosition', + value: function _setScrollPosition(_ref4) { + var scrollLeft = _ref4.scrollLeft; + var scrollTop = _ref4.scrollTop; - /** Adds a cell to the appropriate Sections and registers it metadata for later retrievable. */ + var newState = { + scrollPositionChangeReason: SCROLL_POSITION_CHANGE_REASONS.REQUESTED + }; - }, { - key: 'registerCell', - value: function registerCell(_ref3) { - var cellMetadatum = _ref3.cellMetadatum; - var index = _ref3.index; + if (scrollLeft >= 0) { + newState.scrollLeft = scrollLeft; + } - this._cellMetadata[index] = cellMetadatum; + if (scrollTop >= 0) { + newState.scrollTop = scrollTop; + } - this.getSections(cellMetadatum).forEach(function (section) { - return section.addCellIndex(index); - }); + if (scrollLeft >= 0 && scrollLeft !== this.state.scrollLeft || scrollTop >= 0 && scrollTop !== this.state.scrollTop) { + this.setState(newState); + } } - }]); - - return SectionManager; -}(); + }, { + key: '_updateScrollPositionForScrollToCell', + value: function _updateScrollPositionForScrollToCell() { + var _props6 = this.props; + var cellLayoutManager = _props6.cellLayoutManager; + var height = _props6.height; + var scrollToAlignment = _props6.scrollToAlignment; + var scrollToCell = _props6.scrollToCell; + var width = _props6.width; + var _state3 = this.state; + var scrollLeft = _state3.scrollLeft; + var scrollTop = _state3.scrollTop; -exports.default = SectionManager; -},{"./Section":43}],45:[function(require,module,exports){ -'use strict'; -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.Collection = exports.default = undefined; + if (scrollToCell >= 0) { + var scrollPosition = cellLayoutManager.getScrollPositionForCell({ + align: scrollToAlignment, + cellIndex: scrollToCell, + height: height, + scrollLeft: scrollLeft, + scrollTop: scrollTop, + width: width + }); -var _Collection2 = require('./Collection'); + if (scrollPosition.scrollLeft !== scrollLeft || scrollPosition.scrollTop !== scrollTop) { + this._setScrollPosition(scrollPosition); + } + } + } + }, { + key: '_onScroll', + value: function _onScroll(event) { + // In certain edge-cases React dispatches an onScroll event with an invalid target.scrollLeft / target.scrollTop. + // This invalid event can be detected by comparing event.target to this component's scrollable DOM element. + // See issue #404 for more information. + if (event.target !== this._scrollingContainer) { + return; + } -var _Collection3 = _interopRequireDefault(_Collection2); + // Prevent pointer events from interrupting a smooth scroll + this._enablePointerEventsAfterDelay(); -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + // When this component is shrunk drastically, React dispatches a series of back-to-back scroll events, + // Gradually converging on a scrollTop that is within the bounds of the new, smaller height. + // This causes a series of rapid renders that is slow for long lists. + // We can avoid that by doing some simple bounds checking to ensure that scrollTop never exceeds the total height. + var _props7 = this.props; + var cellLayoutManager = _props7.cellLayoutManager; + var height = _props7.height; + var isScrollingChange = _props7.isScrollingChange; + var width = _props7.width; -exports.default = _Collection3.default; -exports.Collection = _Collection3.default; -},{"./Collection":41}],46:[function(require,module,exports){ -'use strict'; + var scrollbarSize = this._scrollbarSize; -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = calculateSizeAndPositionData; + var _cellLayoutManager$ge3 = cellLayoutManager.getTotalSize(); -var _SectionManager = require('../SectionManager'); + var totalHeight = _cellLayoutManager$ge3.height; + var totalWidth = _cellLayoutManager$ge3.width; -var _SectionManager2 = _interopRequireDefault(_SectionManager); + var scrollLeft = Math.max(0, Math.min(totalWidth - width + scrollbarSize, event.target.scrollLeft)); + var scrollTop = Math.max(0, Math.min(totalHeight - height + scrollbarSize, event.target.scrollTop)); -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + // Certain devices (like Apple touchpad) rapid-fire duplicate events. + // Don't force a re-render if this is the case. + // The mouse may move faster then the animation frame does. + // Use requestAnimationFrame to avoid over-updating. + if (this.state.scrollLeft !== scrollLeft || this.state.scrollTop !== scrollTop) { + // Browsers with cancelable scroll events (eg. Firefox) interrupt scrolling animations if scrollTop/scrollLeft is set. + // Other browsers (eg. Safari) don't scroll as well without the help under certain conditions (DOM or style changes during scrolling). + // All things considered, this seems to be the best current work around that I'm aware of. + // For more information see https://github.com/bvaughn/react-virtualized/pull/124 + var scrollPositionChangeReason = event.cancelable ? SCROLL_POSITION_CHANGE_REASONS.OBSERVED : SCROLL_POSITION_CHANGE_REASONS.REQUESTED; -function calculateSizeAndPositionData(_ref) { - var cellCount = _ref.cellCount; - var cellSizeAndPositionGetter = _ref.cellSizeAndPositionGetter; - var sectionSize = _ref.sectionSize; + // Synchronously set :isScrolling the first time (since _setNextState will reschedule its animation frame each time it's called) + if (!this.state.isScrolling) { + isScrollingChange(true); - var cellMetadata = []; - var sectionManager = new _SectionManager2.default(sectionSize); - var height = 0; - var width = 0; + this.setState({ + isScrolling: true + }); + } - for (var index = 0; index < cellCount; index++) { - var cellMetadatum = cellSizeAndPositionGetter(index); + this._setNextState({ + isScrolling: true, + scrollLeft: scrollLeft, + scrollPositionChangeReason: scrollPositionChangeReason, + scrollTop: scrollTop + }); + } - if (cellMetadatum.height == null || isNaN(cellMetadatum.height) || cellMetadatum.width == null || isNaN(cellMetadatum.width) || cellMetadatum.x == null || isNaN(cellMetadatum.x) || cellMetadatum.y == null || isNaN(cellMetadatum.y)) { - throw Error('Invalid metadata returned for cell ' + index + ':\n x:' + cellMetadatum.x + ', y:' + cellMetadatum.y + ', width:' + cellMetadatum.width + ', height:' + cellMetadatum.height); + this._invokeOnScrollMemoizer({ + scrollLeft: scrollLeft, + scrollTop: scrollTop, + totalWidth: totalWidth, + totalHeight: totalHeight + }); } + }]); - height = Math.max(height, cellMetadatum.y + cellMetadatum.height); - width = Math.max(width, cellMetadatum.x + cellMetadatum.width); - - cellMetadata[index] = cellMetadatum; - sectionManager.registerCell({ - cellMetadatum: cellMetadatum, - index: index - }); - } + return CollectionView; +}(_react.Component); - return { - cellMetadata: cellMetadata, - height: height, - sectionManager: sectionManager, - width: width - }; -} -},{"../SectionManager":44}],47:[function(require,module,exports){ -'use strict'; +CollectionView.propTypes = { + 'aria-label': _react.PropTypes.string, -Object.defineProperty(exports, "__esModule", { - value: true -}); + /** + * Removes fixed height from the scrollingContainer so that the total height + * of rows can stretch the window. Intended for use with WindowScroller + */ + autoHeight: _react.PropTypes.bool, -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + /** + * Number of cells in collection. + */ + cellCount: _react.PropTypes.number.isRequired, -var _react = require('react'); + /** + * Calculates cell sizes and positions and manages rendering the appropriate cells given a specified window. + */ + cellLayoutManager: _react.PropTypes.object.isRequired, -var _reactAddonsShallowCompare = require('react-addons-shallow-compare'); + /** + * Optional custom CSS class name to attach to root Collection element. + */ + className: _react.PropTypes.string, -var _reactAddonsShallowCompare2 = _interopRequireDefault(_reactAddonsShallowCompare); + /** + * Height of Collection; this property determines the number of visible (vs virtualized) rows. + */ + height: _react.PropTypes.number.isRequired, -var _Grid = require('../Grid'); + /** + * Enables the `Collection` to horiontally "overscan" its content similar to how `Grid` does. + * This can reduce flicker around the edges when a user scrolls quickly. + */ + horizontalOverscanSize: _react.PropTypes.number.isRequired, -var _Grid2 = _interopRequireDefault(_Grid); + isScrollingChange: _react.PropTypes.func, -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** + * Optional renderer to be used in place of rows when either :rowCount or :cellCount is 0. + */ + noContentRenderer: _react.PropTypes.func.isRequired, -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + /** + * Callback invoked whenever the scroll offset changes within the inner scrollable region. + * This callback can be used to sync scrolling between lists, tables, or grids. + * ({ clientHeight, clientWidth, scrollHeight, scrollLeft, scrollTop, scrollWidth }): void + */ + onScroll: _react.PropTypes.func.isRequired, -function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + /** + * Callback invoked with information about the section of the Collection that was just rendered. + * This callback is passed a named :indices parameter which is an Array of the most recently rendered section indices. + */ + onSectionRendered: _react.PropTypes.func.isRequired, -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + /** + * Horizontal offset. + */ + scrollLeft: _react.PropTypes.number, -/** - * High-order component that auto-calculates column-widths for `Grid` cells. - */ + /** + * Controls scroll-to-cell behavior of the Grid. + * The default ("auto") scrolls the least amount possible to ensure that the specified cell is fully visible. + * Use "start" to align cells to the top/left of the Grid and "end" to align bottom/right. + */ + scrollToAlignment: _react.PropTypes.oneOf(['auto', 'end', 'start', 'center']).isRequired, -var ColumnSizer = function (_Component) { - _inherits(ColumnSizer, _Component); + /** + * Cell index to ensure visible (by forcefully scrolling if necessary). + */ + scrollToCell: _react.PropTypes.number, - function ColumnSizer(props, context) { - _classCallCheck(this, ColumnSizer); + /** + * Vertical offset. + */ + scrollTop: _react.PropTypes.number, - var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(ColumnSizer).call(this, props, context)); + /** + * Optional custom inline style to attach to root Collection element. + */ + style: _react.PropTypes.object, - _this._registerChild = _this._registerChild.bind(_this); - return _this; - } + /** + * Enables the `Collection` to vertically "overscan" its content similar to how `Grid` does. + * This can reduce flicker around the edges when a user scrolls quickly. + */ + verticalOverscanSize: _react.PropTypes.number.isRequired, - _createClass(ColumnSizer, [{ - key: 'componentDidUpdate', - value: function componentDidUpdate(prevProps, prevState) { - var _props = this.props; - var columnMaxWidth = _props.columnMaxWidth; - var columnMinWidth = _props.columnMinWidth; - var columnsCount = _props.columnsCount; - var width = _props.width; + /** + * Width of Collection; this property determines the number of visible (vs virtualized) columns. + */ + width: _react.PropTypes.number.isRequired +}; +CollectionView.defaultProps = { + 'aria-label': 'grid', + horizontalOverscanSize: 0, + noContentRenderer: function noContentRenderer() { + return null; + }, + onScroll: function onScroll() { + return null; + }, + onSectionRendered: function onSectionRendered() { + return null; + }, + scrollToAlignment: 'auto', + style: {}, + verticalOverscanSize: 0 +}; +exports.default = CollectionView; +},{"../utils/createCallbackMemoizer":84,"classnames":undefined,"dom-helpers/util/scrollbarSize":88,"raf":89,"react":undefined,"react-addons-shallow-compare":18}],51:[function(require,module,exports){ +'use strict'; +Object.defineProperty(exports, "__esModule", { + value: true +}); - if (columnMaxWidth !== prevProps.columnMaxWidth || columnMinWidth !== prevProps.columnMinWidth || columnsCount !== prevProps.columnsCount || width !== prevProps.width) { - if (this._registeredChild) { - this._registeredChild.recomputeGridSize(); - } - } - } - }, { - key: 'render', - value: function render() { - var _props2 = this.props; - var children = _props2.children; - var columnMaxWidth = _props2.columnMaxWidth; - var columnMinWidth = _props2.columnMinWidth; - var columnsCount = _props2.columnsCount; - var width = _props2.width; +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - var safeColumnMinWidth = columnMinWidth || 1; +/** + * A section of the Window. + * Window Sections are used to group nearby cells. + * This enables us to more quickly determine which cells to display in a given region of the Window. + * Sections have a fixed size and contain 0 to many cells (tracked by their indices). + */ +var Section = function () { + function Section(_ref) { + var height = _ref.height; + var width = _ref.width; + var x = _ref.x; + var y = _ref.y; - var safeColumnMaxWidth = columnMaxWidth ? Math.min(columnMaxWidth, width) : width; + _classCallCheck(this, Section); - var columnWidth = width / columnsCount; - columnWidth = Math.max(safeColumnMinWidth, columnWidth); - columnWidth = Math.min(safeColumnMaxWidth, columnWidth); - columnWidth = Math.floor(columnWidth); + this.height = height; + this.width = width; + this.x = x; + this.y = y; + + this._indexMap = {}; + this._indices = []; + } - var adjustedWidth = Math.min(width, columnWidth * columnsCount); + /** Add a cell to this section. */ - return children({ - adjustedWidth: adjustedWidth, - getColumnWidth: function getColumnWidth() { - return columnWidth; - }, - registerChild: this._registerChild - }); - } - }, { - key: 'shouldComponentUpdate', - value: function shouldComponentUpdate(nextProps, nextState) { - return (0, _reactAddonsShallowCompare2.default)(this, nextProps, nextState); - } - }, { - key: '_registerChild', - value: function _registerChild(child) { - if (child !== null && !(child instanceof _Grid2.default)) { - throw Error('Unexpected child type registered; only Grid children are supported.'); - } - this._registeredChild = child; + _createClass(Section, [{ + key: 'addCellIndex', + value: function addCellIndex(_ref2) { + var index = _ref2.index; - if (this._registeredChild) { - this._registeredChild.recomputeGridSize(); + if (!this._indexMap[index]) { + this._indexMap[index] = true; + this._indices.push(index); } } - }]); - - return ColumnSizer; -}(_react.Component); - -ColumnSizer.propTypes = { - /** - * Function respondible for rendering a virtualized Grid. - * This function should implement the following signature: - * ({ adjustedWidth, getColumnWidth, registerChild }) => PropTypes.element - * - * The specified :getColumnWidth function should be passed to the Grid's :columnWidth property. - * The :registerChild should be passed to the Grid's :ref property. - * The :adjustedWidth property is optional; it reflects the lesser of the overall width or the width of all columns. - */ - children: _react.PropTypes.func.isRequired, - - /** Optional maximum allowed column width */ - columnMaxWidth: _react.PropTypes.number, - /** Optional minimum allowed column width */ - columnMinWidth: _react.PropTypes.number, - - /** Number of columns in Grid or FlexTable child */ - columnsCount: _react.PropTypes.number.isRequired, + /** Get all cell indices that have been added to this section. */ - /** Width of Grid or FlexTable child */ - width: _react.PropTypes.number.isRequired -}; -exports.default = ColumnSizer; -},{"../Grid":55,"react":undefined,"react-addons-shallow-compare":18}],48:[function(require,module,exports){ -'use strict'; + }, { + key: 'getCellIndices', + value: function getCellIndices() { + return this._indices; + } -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.ColumnSizer = exports.default = undefined; + /** Intended for debugger/test purposes only */ -var _ColumnSizer2 = require('./ColumnSizer'); + }, { + key: 'toString', + value: function toString() { + return this.x + ',' + this.y + ' ' + this.width + 'x' + this.height; + } + }]); -var _ColumnSizer3 = _interopRequireDefault(_ColumnSizer2); + return Section; +}(); /** @rlow */ -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -exports.default = _ColumnSizer3.default; -exports.ColumnSizer = _ColumnSizer3.default; -},{"./ColumnSizer":47}],49:[function(require,module,exports){ +exports.default = Section; +},{}],52:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); -exports.defaultCellRenderer = defaultCellRenderer; -exports.defaultCellDataGetter = defaultCellDataGetter; -exports.defaultHeaderRenderer = defaultHeaderRenderer; -var _react = require('react'); +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /** + * Window Sections are used to group nearby cells. + * This enables us to more quickly determine which cells to display in a given region of the Window. + * + */ -var _react2 = _interopRequireDefault(_react); -var _SortIndicator = require('./SortIndicator'); +var _Section = require('./Section'); -var _SortIndicator2 = _interopRequireDefault(_SortIndicator); +var _Section2 = _interopRequireDefault(_Section); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } -function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } +var SECTION_SIZE = 100; /** - * Default cell renderer that displays an attribute as a simple string - * You should override the column's cellRenderer if your data is some other type of object. + * Contains 0 to many Sections. + * Grows (and adds Sections) dynamically as cells are registered. + * Automatically adds cells to the appropriate Section(s). */ -function defaultCellRenderer(cellData, cellDataKey, rowData, rowIndex, columnData) { - if (cellData === null || cellData === undefined) { - return ''; - } else { - return String(cellData); - } -} +var SectionManager = function () { + function SectionManager() { + var sectionSize = arguments.length <= 0 || arguments[0] === undefined ? SECTION_SIZE : arguments[0]; -/** - * Default accessor for returning a cell value for a given attribute. - * This function expects to operate on either a vanilla Object or an Immutable Map. - * You should override the column's cellDataGetter if your data is some other type of object. - */ -function defaultCellDataGetter(dataKey, rowData, columnData) { - if (rowData.get instanceof Function) { - return rowData.get(dataKey); - } else { - return rowData[dataKey]; + _classCallCheck(this, SectionManager); + + this._sectionSize = sectionSize; + + this._cellMetadata = []; + this._sections = {}; } -} -/** - * Default table header renderer. - */ -function defaultHeaderRenderer(_ref) { - var columnData = _ref.columnData; - var dataKey = _ref.dataKey; - var disableSort = _ref.disableSort; - var label = _ref.label; - var sortBy = _ref.sortBy; - var sortDirection = _ref.sortDirection; + /** + * Gets all cell indices contained in the specified region. + * A region may encompass 1 or more Sections. + */ - var showSortIndicator = sortBy === dataKey; - var children = [_react2.default.createElement( - 'div', - { - className: 'FlexTable__headerTruncatedText', - key: 'label', - title: label - }, - label - )]; - if (showSortIndicator) { - children.push(_react2.default.createElement(_SortIndicator2.default, { - key: 'SortIndicator', - sortDirection: sortDirection - })); - } + _createClass(SectionManager, [{ + key: 'getCellIndices', + value: function getCellIndices(_ref) { + var height = _ref.height; + var width = _ref.width; + var x = _ref.x; + var y = _ref.y; - return children; -} + var indices = {}; -/** - * Describes the header and cell contents of a table column. - */ + this.getSections({ height: height, width: width, x: x, y: y }).forEach(function (section) { + return section.getCellIndices().forEach(function (index) { + indices[index] = index; + }); + }); -var Column = function (_Component) { - _inherits(Column, _Component); + // Object keys are strings; this function returns numbers + return Object.keys(indices).map(function (index) { + return indices[index]; + }); + } - function Column() { - _classCallCheck(this, Column); + /** Get size and position information for the cell specified. */ - return _possibleConstructorReturn(this, Object.getPrototypeOf(Column).apply(this, arguments)); - } + }, { + key: 'getCellMetadata', + value: function getCellMetadata(_ref2) { + var index = _ref2.index; - return Column; -}(_react.Component); + return this._cellMetadata[index]; + } -Column.defaultProps = { - cellDataGetter: defaultCellDataGetter, - cellRenderer: defaultCellRenderer, - flexGrow: 0, - flexShrink: 1, - headerRenderer: defaultHeaderRenderer -}; -Column.propTypes = { - /** Optional aria-label value to set on the column header */ - 'aria-label': _react.PropTypes.string, + /** Get all Sections overlapping the specified region. */ - /** Optional CSS class to apply to cell */ - cellClassName: _react.PropTypes.string, + }, { + key: 'getSections', + value: function getSections(_ref3) { + var height = _ref3.height; + var width = _ref3.width; + var x = _ref3.x; + var y = _ref3.y; - /** - * Callback responsible for returning a cell's data, given its :dataKey - * (dataKey: string, rowData: any): any - */ - cellDataGetter: _react.PropTypes.func, + var sectionXStart = Math.floor(x / this._sectionSize); + var sectionXStop = Math.floor((x + width - 1) / this._sectionSize); + var sectionYStart = Math.floor(y / this._sectionSize); + var sectionYStop = Math.floor((y + height - 1) / this._sectionSize); - /** - * Callback responsible for rendering a cell's contents. - * (cellData: any, cellDataKey: string, rowData: any, rowIndex: number, columnData: any): element - */ - cellRenderer: _react.PropTypes.func, + var sections = []; - /** Optional additional data passed to this column's :cellDataGetter */ - columnData: _react.PropTypes.object, + for (var sectionX = sectionXStart; sectionX <= sectionXStop; sectionX++) { + for (var sectionY = sectionYStart; sectionY <= sectionYStop; sectionY++) { + var key = sectionX + '.' + sectionY; - /** Uniquely identifies the row-data attribute correspnding to this cell */ - dataKey: _react.PropTypes.any.isRequired, + if (!this._sections[key]) { + this._sections[key] = new _Section2.default({ + height: this._sectionSize, + width: this._sectionSize, + x: sectionX * this._sectionSize, + y: sectionY * this._sectionSize + }); + } - /** If sort is enabled for the table at large, disable it for this column */ - disableSort: _react.PropTypes.bool, + sections.push(this._sections[key]); + } + } - /** Flex grow style; defaults to 0 */ - flexGrow: _react.PropTypes.number, + return sections; + } - /** Flex shrink style; defaults to 1 */ - flexShrink: _react.PropTypes.number, + /** Total number of Sections based on the currently registered cells. */ - /** Optional CSS class to apply to this column's header */ - headerClassName: _react.PropTypes.string, + }, { + key: 'getTotalSectionCount', + value: function getTotalSectionCount() { + return Object.keys(this._sections).length; + } - /** - * Optional callback responsible for rendering a column header contents. - * ({ columnData: object, dataKey: string, disableSort: boolean, label: string, sortBy: string, sortDirection: string }): PropTypes.node - */ - headerRenderer: _react.PropTypes.func.isRequired, + /** Intended for debugger/test purposes only */ - /** Header label for this column */ - label: _react.PropTypes.string, + }, { + key: 'toString', + value: function toString() { + var _this = this; - /** Maximum width of column; this property will only be used if :flexGrow is > 0. */ - maxWidth: _react.PropTypes.number, + return Object.keys(this._sections).map(function (index) { + return _this._sections[index].toString(); + }); + } - /** Minimum width of column. */ - minWidth: _react.PropTypes.number, + /** Adds a cell to the appropriate Sections and registers it metadata for later retrievable. */ - /** Flex basis (width) for this column; This value can grow or shrink based on :flexGrow and :flexShrink properties. */ - width: _react.PropTypes.number.isRequired -}; -exports.default = Column; -},{"./SortIndicator":52,"react":undefined}],50:[function(require,module,exports){ + }, { + key: 'registerCell', + value: function registerCell(_ref4) { + var cellMetadatum = _ref4.cellMetadatum; + var index = _ref4.index; + + this._cellMetadata[index] = cellMetadatum; + + this.getSections(cellMetadatum).forEach(function (section) { + return section.addCellIndex({ index: index }); + }); + } + }]); + + return SectionManager; +}(); + +exports.default = SectionManager; +},{"./Section":51}],53:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); +exports.Collection = exports.default = undefined; -var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; +var _Collection2 = require('./Collection'); -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); +var _Collection3 = _interopRequireDefault(_Collection2); -var _classnames = require('classnames'); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -var _classnames2 = _interopRequireDefault(_classnames); +exports.default = _Collection3.default; +exports.Collection = _Collection3.default; +},{"./Collection":49}],54:[function(require,module,exports){ +'use strict'; -var _FlexColumn = require('./FlexColumn'); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = calculateSizeAndPositionData; -var _FlexColumn2 = _interopRequireDefault(_FlexColumn); +var _SectionManager = require('../SectionManager'); -var _react = require('react'); +var _SectionManager2 = _interopRequireDefault(_SectionManager); -var _react2 = _interopRequireDefault(_react); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -var _reactDom = require('react-dom'); +function calculateSizeAndPositionData(_ref) { + var cellCount = _ref.cellCount; + var cellSizeAndPositionGetter = _ref.cellSizeAndPositionGetter; + var sectionSize = _ref.sectionSize; + + var cellMetadata = []; + var sectionManager = new _SectionManager2.default(sectionSize); + var height = 0; + var width = 0; + + for (var index = 0; index < cellCount; index++) { + var cellMetadatum = cellSizeAndPositionGetter({ index: index }); + + if (cellMetadatum.height == null || isNaN(cellMetadatum.height) || cellMetadatum.width == null || isNaN(cellMetadatum.width) || cellMetadatum.x == null || isNaN(cellMetadatum.x) || cellMetadatum.y == null || isNaN(cellMetadatum.y)) { + throw Error('Invalid metadata returned for cell ' + index + ':\n x:' + cellMetadatum.x + ', y:' + cellMetadatum.y + ', width:' + cellMetadatum.width + ', height:' + cellMetadatum.height); + } + + height = Math.max(height, cellMetadatum.y + cellMetadatum.height); + width = Math.max(width, cellMetadatum.x + cellMetadatum.width); + + cellMetadata[index] = cellMetadatum; + sectionManager.registerCell({ + cellMetadatum: cellMetadatum, + index: index + }); + } + + return { + cellMetadata: cellMetadata, + height: height, + sectionManager: sectionManager, + width: width + }; +} +},{"../SectionManager":52}],55:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _react = require('react'); var _reactAddonsShallowCompare = require('react-addons-shallow-compare'); @@ -5829,10 +6859,6 @@ var _Grid = require('../Grid'); var _Grid2 = _interopRequireDefault(_Grid); -var _SortDirection = require('./SortDirection'); - -var _SortDirection2 = _interopRequireDefault(_SortDirection); - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } @@ -5842,135 +6868,65 @@ function _possibleConstructorReturn(self, call) { if (!self) { throw new Referen function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** - * Table component with fixed headers and virtualized rows for improved performance with large data sets. - * This component expects explicit width, height, and padding parameters. + * High-order component that auto-calculates column-widths for `Grid` cells. */ +var ColumnSizer = function (_Component) { + _inherits(ColumnSizer, _Component); -var FlexTable = function (_Component) { - _inherits(FlexTable, _Component); - - function FlexTable(props) { - _classCallCheck(this, FlexTable); - - var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(FlexTable).call(this, props)); + function ColumnSizer(props, context) { + _classCallCheck(this, ColumnSizer); - _this.state = { - scrollbarWidth: 0 - }; + var _this = _possibleConstructorReturn(this, (ColumnSizer.__proto__ || Object.getPrototypeOf(ColumnSizer)).call(this, props, context)); - _this._createRow = _this._createRow.bind(_this); + _this._registerChild = _this._registerChild.bind(_this); return _this; } - /** - * See Grid#recomputeGridSize - */ + _createClass(ColumnSizer, [{ + key: 'componentDidUpdate', + value: function componentDidUpdate(prevProps, prevState) { + var _props = this.props; + var columnMaxWidth = _props.columnMaxWidth; + var columnMinWidth = _props.columnMinWidth; + var columnCount = _props.columnCount; + var width = _props.width; - _createClass(FlexTable, [{ - key: 'recomputeRowHeights', - value: function recomputeRowHeights() { - this.refs.Grid.recomputeGridSize(); - } - }, { - key: 'componentDidMount', - value: function componentDidMount() { - this._setScrollbarWidth(); - } - }, { - key: 'componentDidUpdate', - value: function componentDidUpdate() { - this._setScrollbarWidth(); + if (columnMaxWidth !== prevProps.columnMaxWidth || columnMinWidth !== prevProps.columnMinWidth || columnCount !== prevProps.columnCount || width !== prevProps.width) { + if (this._registeredChild) { + this._registeredChild.recomputeGridSize(); + } + } } }, { key: 'render', value: function render() { - var _this2 = this; + var _props2 = this.props; + var children = _props2.children; + var columnMaxWidth = _props2.columnMaxWidth; + var columnMinWidth = _props2.columnMinWidth; + var columnCount = _props2.columnCount; + var width = _props2.width; - var _props = this.props; - var className = _props.className; - var disableHeader = _props.disableHeader; - var headerHeight = _props.headerHeight; - var height = _props.height; - var noRowsRenderer = _props.noRowsRenderer; - var onRowsRendered = _props.onRowsRendered; - var _onScroll = _props.onScroll; - var overscanRowsCount = _props.overscanRowsCount; - var rowClassName = _props.rowClassName; - var rowHeight = _props.rowHeight; - var rowsCount = _props.rowsCount; - var scrollToIndex = _props.scrollToIndex; - var scrollTop = _props.scrollTop; - var width = _props.width; - var scrollbarWidth = this.state.scrollbarWidth; + var safeColumnMinWidth = columnMinWidth || 1; - var availableRowsHeight = height - headerHeight; + var safeColumnMaxWidth = columnMaxWidth ? Math.min(columnMaxWidth, width) : width; - // This row-renderer wrapper function is necessary in order to trigger re-render when the - // sort-by or sort-direction have changed (else Grid will not see any props changes) - var rowRenderer = function rowRenderer(index) { - return _this2._createRow(index); - }; + var columnWidth = width / columnCount; + columnWidth = Math.max(safeColumnMinWidth, columnWidth); + columnWidth = Math.min(safeColumnMaxWidth, columnWidth); + columnWidth = Math.floor(columnWidth); - var rowClass = rowClassName instanceof Function ? rowClassName(-1) : rowClassName; + var adjustedWidth = Math.min(width, columnWidth * columnCount); - return _react2.default.createElement( - 'div', - { - className: (0, _classnames2.default)('FlexTable', className) + return children({ + adjustedWidth: adjustedWidth, + getColumnWidth: function getColumnWidth() { + return columnWidth; }, - !disableHeader && _react2.default.createElement( - 'div', - { - className: (0, _classnames2.default)('FlexTable__headerRow', rowClass), - style: { - height: headerHeight, - paddingRight: scrollbarWidth, - width: width - } - }, - this._getRenderedHeaderRow() - ), - _react2.default.createElement(_Grid2.default, { - 'aria-label': this.props['aria-label'], - ref: 'Grid', - className: 'FlexTable__Grid', - columnWidth: width, - columnsCount: 1, - height: availableRowsHeight, - noContentRenderer: noRowsRenderer, - onScroll: function onScroll(_ref) { - var clientHeight = _ref.clientHeight; - var scrollHeight = _ref.scrollHeight; - var scrollTop = _ref.scrollTop; - return _onScroll({ clientHeight: clientHeight, scrollHeight: scrollHeight, scrollTop: scrollTop }); - }, - onSectionRendered: function onSectionRendered(_ref2) { - var rowOverscanStartIndex = _ref2.rowOverscanStartIndex; - var rowOverscanStopIndex = _ref2.rowOverscanStopIndex; - var rowStartIndex = _ref2.rowStartIndex; - var rowStopIndex = _ref2.rowStopIndex; - return onRowsRendered({ - overscanStartIndex: rowOverscanStartIndex, - overscanStopIndex: rowOverscanStopIndex, - startIndex: rowStartIndex, - stopIndex: rowStopIndex - }); - }, - overscanRowsCount: overscanRowsCount, - renderCell: function renderCell(_ref3) { - var columnIndex = _ref3.columnIndex; - var rowIndex = _ref3.rowIndex; - return rowRenderer(rowIndex); - }, - rowHeight: rowHeight, - rowsCount: rowsCount, - scrollToRow: scrollToIndex, - scrollTop: scrollTop, - width: width - }) - ); + registerChild: this._registerChild + }); } }, { key: 'shouldComponentUpdate', @@ -5978,1373 +6934,2811 @@ var FlexTable = function (_Component) { return (0, _reactAddonsShallowCompare2.default)(this, nextProps, nextState); } }, { - key: '_createColumn', - value: function _createColumn(column, columnIndex, rowData, rowIndex) { - var _column$props = column.props; - var cellClassName = _column$props.cellClassName; - var cellDataGetter = _column$props.cellDataGetter; - var columnData = _column$props.columnData; - var dataKey = _column$props.dataKey; - var cellRenderer = _column$props.cellRenderer; + key: '_registerChild', + value: function _registerChild(child) { + if (child !== null && !(child instanceof _Grid2.default)) { + throw Error('Unexpected child type registered; only Grid children are supported.'); + } - var cellData = cellDataGetter(dataKey, rowData, columnData); - var renderedCell = cellRenderer(cellData, dataKey, rowData, rowIndex, columnData); + this._registeredChild = child; - var style = this._getFlexStyleForColumn(column); + if (this._registeredChild) { + this._registeredChild.recomputeGridSize(); + } + } + }]); - var title = typeof renderedCell === 'string' ? renderedCell : null; + return ColumnSizer; +}(_react.Component); - return _react2.default.createElement( - 'div', - { - key: 'Row' + rowIndex + '-Col' + columnIndex, - className: (0, _classnames2.default)('FlexTable__rowColumn', cellClassName), - style: style - }, - _react2.default.createElement( - 'div', - { - className: 'FlexTable__truncatedColumnText', - title: title - }, - renderedCell - ) - ); - } - }, { - key: '_createHeader', - value: function _createHeader(column, columnIndex) { - var _props2 = this.props; - var headerClassName = _props2.headerClassName; - var onHeaderClick = _props2.onHeaderClick; - var sort = _props2.sort; - var sortBy = _props2.sortBy; - var sortDirection = _props2.sortDirection; - var _column$props2 = column.props; - var dataKey = _column$props2.dataKey; - var disableSort = _column$props2.disableSort; - var headerRenderer = _column$props2.headerRenderer; - var label = _column$props2.label; - var columnData = _column$props2.columnData; +ColumnSizer.propTypes = { + /** + * Function respondible for rendering a virtualized Grid. + * This function should implement the following signature: + * ({ adjustedWidth, getColumnWidth, registerChild }) => PropTypes.element + * + * The specified :getColumnWidth function should be passed to the Grid's :columnWidth property. + * The :registerChild should be passed to the Grid's :ref property. + * The :adjustedWidth property is optional; it reflects the lesser of the overall width or the width of all columns. + */ + children: _react.PropTypes.func.isRequired, - var sortEnabled = !disableSort && sort; + /** Optional maximum allowed column width */ + columnMaxWidth: _react.PropTypes.number, - var classNames = (0, _classnames2.default)('FlexTable__headerColumn', headerClassName, column.props.headerClassName, { - 'FlexTable__sortableHeaderColumn': sortEnabled - }); - var style = this._getFlexStyleForColumn(column); + /** Optional minimum allowed column width */ + columnMinWidth: _react.PropTypes.number, - var renderedHeader = headerRenderer({ - columnData: columnData, - dataKey: dataKey, - disableSort: disableSort, - label: label, - sortBy: sortBy, - sortDirection: sortDirection - }); + /** Number of columns in Grid or FlexTable child */ + columnCount: _react.PropTypes.number.isRequired, - var a11yProps = {}; + /** Width of Grid or FlexTable child */ + width: _react.PropTypes.number.isRequired +}; +exports.default = ColumnSizer; +},{"../Grid":68,"react":undefined,"react-addons-shallow-compare":18}],56:[function(require,module,exports){ +'use strict'; - if (sortEnabled || onHeaderClick) { - (function () { - // If this is a sortable header, clicking it should update the table data's sorting. - var newSortDirection = sortBy !== dataKey || sortDirection === _SortDirection2.default.DESC ? _SortDirection2.default.ASC : _SortDirection2.default.DESC; +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.ColumnSizer = exports.default = undefined; - var onClick = function onClick() { - sortEnabled && sort(dataKey, newSortDirection); - onHeaderClick && onHeaderClick(dataKey, columnData); - }; +var _ColumnSizer2 = require('./ColumnSizer'); - var onKeyDown = function onKeyDown(event) { - if (event.key === 'Enter' || event.key === ' ') { - onClick(); - } - }; +var _ColumnSizer3 = _interopRequireDefault(_ColumnSizer2); - a11yProps['aria-label'] = column.props['aria-label'] || label || dataKey; - a11yProps.role = 'rowheader'; - a11yProps.tabIndex = 0; - a11yProps.onClick = onClick; - a11yProps.onKeyDown = onKeyDown; - })(); - } +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - return _react2.default.createElement( - 'div', - _extends({}, a11yProps, { - key: 'Header-Col' + columnIndex, - className: classNames, - style: style - }), - renderedHeader - ); - } - }, { - key: '_createRow', - value: function _createRow(rowIndex) { - var _this3 = this; +exports.default = _ColumnSizer3.default; +exports.ColumnSizer = _ColumnSizer3.default; +},{"./ColumnSizer":55}],57:[function(require,module,exports){ +'use strict'; - var _props3 = this.props; - var children = _props3.children; - var onRowClick = _props3.onRowClick; - var rowClassName = _props3.rowClassName; - var rowGetter = _props3.rowGetter; - var scrollbarWidth = this.state.scrollbarWidth; +Object.defineProperty(exports, "__esModule", { + value: true +}); +var _react = require('react'); - var rowClass = rowClassName instanceof Function ? rowClassName(rowIndex) : rowClassName; - var rowData = rowGetter(rowIndex); +var _defaultHeaderRenderer = require('./defaultHeaderRenderer'); - var renderedRow = _react2.default.Children.toArray(children).map(function (column, columnIndex) { - return _this3._createColumn(column, columnIndex, rowData, rowIndex); - }); +var _defaultHeaderRenderer2 = _interopRequireDefault(_defaultHeaderRenderer); - var a11yProps = {}; +var _defaultCellRenderer = require('./defaultCellRenderer'); - if (onRowClick) { - a11yProps['aria-label'] = 'row'; - a11yProps.role = 'row'; - a11yProps.tabIndex = 0; - a11yProps.onClick = function () { - return onRowClick(rowIndex); - }; - } +var _defaultCellRenderer2 = _interopRequireDefault(_defaultCellRenderer); - return _react2.default.createElement( - 'div', - _extends({}, a11yProps, { - key: rowIndex, - className: (0, _classnames2.default)('FlexTable__row', rowClass), - style: { - height: this._getRowHeight(rowIndex), - paddingRight: scrollbarWidth - } - }), - renderedRow - ); - } +var _defaultCellDataGetter = require('./defaultCellDataGetter'); - /** - * Determines the flex-shrink, flex-grow, and width values for a cell (header or column). - */ +var _defaultCellDataGetter2 = _interopRequireDefault(_defaultCellDataGetter); - }, { - key: '_getFlexStyleForColumn', - value: function _getFlexStyleForColumn(column) { - var flexValue = column.props.flexGrow + ' ' + column.props.flexShrink + ' ' + column.props.width + 'px'; +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - var style = { - flex: flexValue, - msFlex: flexValue, - WebkitFlex: flexValue - }; +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - if (column.props.maxWidth) { - style.maxWidth = column.props.maxWidth; - } +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } - if (column.props.minWidth) { - style.minWidth = column.props.minWidth; - } +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } - return style; - } - }, { - key: '_getRenderedHeaderRow', - value: function _getRenderedHeaderRow() { - var _this4 = this; +/** + * Describes the header and cell contents of a table column. + */ +var Column = function (_Component) { + _inherits(Column, _Component); - var _props4 = this.props; - var children = _props4.children; - var disableHeader = _props4.disableHeader; + function Column() { + _classCallCheck(this, Column); - var items = disableHeader ? [] : _react2.default.Children.toArray(children); + return _possibleConstructorReturn(this, (Column.__proto__ || Object.getPrototypeOf(Column)).apply(this, arguments)); + } - return items.map(function (column, index) { - return _this4._createHeader(column, index); - }); - } - }, { - key: '_getRowHeight', - value: function _getRowHeight(rowIndex) { - var rowHeight = this.props.rowHeight; - - - return rowHeight instanceof Function ? rowHeight(rowIndex) : rowHeight; - } - }, { - key: '_setScrollbarWidth', - value: function _setScrollbarWidth() { - var Grid = (0, _reactDom.findDOMNode)(this.refs.Grid); - var clientWidth = Grid.clientWidth || 0; - var offsetWidth = Grid.offsetWidth || 0; - var scrollbarWidth = offsetWidth - clientWidth; - - this.setState({ scrollbarWidth: scrollbarWidth }); - } - }]); - - return FlexTable; + return Column; }(_react.Component); -FlexTable.propTypes = { +Column.defaultProps = { + cellDataGetter: _defaultCellDataGetter2.default, + cellRenderer: _defaultCellRenderer2.default, + flexGrow: 0, + flexShrink: 1, + headerRenderer: _defaultHeaderRenderer2.default, + style: {} +}; +Column.propTypes = { + /** Optional aria-label value to set on the column header */ 'aria-label': _react.PropTypes.string, - /** One or more FlexColumns describing the data displayed in this row */ - children: function children(props, propName, componentName) { - var children = _react2.default.Children.toArray(props.children); - for (var i = 0; i < children.length; i++) { - if (children[i].type !== _FlexColumn2.default) { - return new Error('FlexTable only accepts children of type FlexColumn'); - } - } - }, - - /** Optional CSS class name */ - className: _react.PropTypes.string, - - /** Disable rendering the header at all */ - disableHeader: _react.PropTypes.bool, - - /** Optional CSS class to apply to all column headers */ - headerClassName: _react.PropTypes.string, - - /** Fixed height of header row */ - headerHeight: _react.PropTypes.number.isRequired, - - /** Fixed/available height for out DOM element */ - height: _react.PropTypes.number.isRequired, - - /** Optional renderer to be used in place of table body rows when rowsCount is 0 */ - noRowsRenderer: _react.PropTypes.func, - - /** - * Optional callback when a column's header is clicked. - * (dataKey: string): void - */ - onHeaderClick: _react.PropTypes.func, - - /** - * Callback invoked when a user clicks on a table row. - * (rowIndex: number): void - */ - onRowClick: _react.PropTypes.func, - /** - * Callback invoked with information about the slice of rows that were just rendered. - * ({ startIndex, stopIndex }): void + * Callback responsible for returning a cell's data, given its :dataKey + * ({ columnData: any, dataKey: string, rowData: any }): any */ - onRowsRendered: _react.PropTypes.func, + cellDataGetter: _react.PropTypes.func, /** - * Callback invoked whenever the scroll offset changes within the inner scrollable region. - * This callback can be used to sync scrolling between lists, tables, or grids. - * ({ clientHeight, scrollHeight, scrollTop }): void + * Callback responsible for rendering a cell's contents. + * ({ cellData: any, columnData: any, dataKey: string, rowData: any, rowIndex: number }): node */ - onScroll: _react.PropTypes.func.isRequired, + cellRenderer: _react.PropTypes.func, - /** - * Number of rows to render above/below the visible bounds of the list. - * These rows can help for smoother scrolling on touch devices. - */ - overscanRowsCount: _react.PropTypes.number.isRequired, + /** Optional CSS class to apply to cell */ + className: _react.PropTypes.string, - /** - * Optional CSS class to apply to all table rows (including the header row). - * This property can be a CSS class name (string) or a function that returns a class name. - * If a function is provided its signature should be: (rowIndex: number): string - */ - rowClassName: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.func]), + /** Optional additional data passed to this column's :cellDataGetter */ + columnData: _react.PropTypes.object, - /** - * Callback responsible for returning a data row given an index. - * (index: number): any - */ - rowGetter: _react.PropTypes.func.isRequired, + /** Uniquely identifies the row-data attribute correspnding to this cell */ + dataKey: _react.PropTypes.any.isRequired, - /** - * Either a fixed row height (number) or a function that returns the height of a row given its index. - * (index: number): number - */ - rowHeight: _react.PropTypes.oneOfType([_react.PropTypes.number, _react.PropTypes.func]).isRequired, + /** If sort is enabled for the table at large, disable it for this column */ + disableSort: _react.PropTypes.bool, - /** Number of rows in table. */ - rowsCount: _react.PropTypes.number.isRequired, + /** Flex grow style; defaults to 0 */ + flexGrow: _react.PropTypes.number, - /** Row index to ensure visible (by forcefully scrolling if necessary) */ - scrollToIndex: _react.PropTypes.number, + /** Flex shrink style; defaults to 1 */ + flexShrink: _react.PropTypes.number, - /** Vertical offset. */ - scrollTop: _react.PropTypes.number, + /** Optional CSS class to apply to this column's header */ + headerClassName: _react.PropTypes.string, /** - * Sort function to be called if a sortable header is clicked. - * (dataKey: string, sortDirection: SortDirection): void + * Optional callback responsible for rendering a column header contents. + * ({ columnData: object, dataKey: string, disableSort: boolean, label: string, sortBy: string, sortDirection: string }): PropTypes.node */ - sort: _react.PropTypes.func, + headerRenderer: _react.PropTypes.func.isRequired, - /** FlexTable data is currently sorted by this :dataKey (if it is sorted at all) */ - sortBy: _react.PropTypes.string, + /** Header label for this column */ + label: _react.PropTypes.string, - /** FlexTable data is currently sorted in this direction (if it is sorted at all) */ - sortDirection: _react.PropTypes.oneOf([_SortDirection2.default.ASC, _SortDirection2.default.DESC]), + /** Maximum width of column; this property will only be used if :flexGrow is > 0. */ + maxWidth: _react.PropTypes.number, - /** Width of list */ - width: _react.PropTypes.number.isRequired -}; -FlexTable.defaultProps = { - disableHeader: false, - headerHeight: 0, - noRowsRenderer: function noRowsRenderer() { - return null; - }, - onRowsRendered: function onRowsRendered() { - return null; - }, - onScroll: function onScroll() { - return null; - }, - overscanRowsCount: 10 -}; -exports.default = FlexTable; -},{"../Grid":55,"./FlexColumn":49,"./SortDirection":51,"classnames":undefined,"react":undefined,"react-addons-shallow-compare":18,"react-dom":undefined}],51:[function(require,module,exports){ -'use strict'; + /** Minimum width of column. */ + minWidth: _react.PropTypes.number, -Object.defineProperty(exports, "__esModule", { - value: true -}); -var SortDirection = { - /** - * Sort items in ascending order. - * This means arranging from the lowest value to the highest (e.g. a-z, 0-9). - */ - ASC: 'ASC', + /** Optional inline style to apply to cell */ + style: _react.PropTypes.object, - /** - * Sort items in descending order. - * This means arranging from the highest value to the lowest (e.g. z-a, 9-0). - */ - DESC: 'DESC' + /** Flex basis (width) for this column; This value can grow or shrink based on :flexGrow and :flexShrink properties. */ + width: _react.PropTypes.number.isRequired }; - -exports.default = SortDirection; -},{}],52:[function(require,module,exports){ +exports.default = Column; +},{"./defaultCellDataGetter":61,"./defaultCellRenderer":62,"./defaultHeaderRenderer":63,"react":undefined}],58:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = SortIndicator; -var _react = require('react'); +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; -var _react2 = _interopRequireDefault(_react); +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _classnames = require('classnames'); var _classnames2 = _interopRequireDefault(_classnames); -var _SortDirection = require('./SortDirection'); +var _FlexColumn = require('./FlexColumn'); -var _SortDirection2 = _interopRequireDefault(_SortDirection); +var _FlexColumn2 = _interopRequireDefault(_FlexColumn); -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +var _react = require('react'); -/** - * Displayed beside a header to indicate that a FlexTable is currently sorted by this column. - */ -function SortIndicator(_ref) { - var sortDirection = _ref.sortDirection; +var _react2 = _interopRequireDefault(_react); - var classNames = (0, _classnames2.default)('FlexTable__sortableHeaderIcon', { - 'FlexTable__sortableHeaderIcon--ASC': sortDirection === _SortDirection2.default.ASC, - 'FlexTable__sortableHeaderIcon--DESC': sortDirection === _SortDirection2.default.DESC - }); +var _reactDom = require('react-dom'); - return _react2.default.createElement( - 'svg', - { - className: classNames, - width: 18, - height: 18, - viewBox: '0 0 24 24', - xmlns: 'http://www.w3.org/2000/svg' - }, - sortDirection === _SortDirection2.default.ASC ? _react2.default.createElement('path', { d: 'M7 14l5-5 5 5z' }) : _react2.default.createElement('path', { d: 'M7 10l5 5 5-5z' }), - _react2.default.createElement('path', { d: 'M0 0h24v24H0z', fill: 'none' }) - ); -} -SortIndicator.propTypes = { - sortDirection: _react.PropTypes.oneOf([_SortDirection2.default.ASC, _SortDirection2.default.DESC]) -}; -},{"./SortDirection":51,"classnames":undefined,"react":undefined}],53:[function(require,module,exports){ -'use strict'; +var _reactAddonsShallowCompare = require('react-addons-shallow-compare'); -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.SortIndicator = exports.SortDirection = exports.FlexColumn = exports.FlexTable = exports.default = undefined; +var _reactAddonsShallowCompare2 = _interopRequireDefault(_reactAddonsShallowCompare); -var _FlexTable2 = require('./FlexTable'); +var _Grid = require('../Grid'); -var _FlexTable3 = _interopRequireDefault(_FlexTable2); +var _Grid2 = _interopRequireDefault(_Grid); -var _FlexColumn2 = require('./FlexColumn'); +var _defaultRowRenderer = require('./defaultRowRenderer'); -var _FlexColumn3 = _interopRequireDefault(_FlexColumn2); +var _defaultRowRenderer2 = _interopRequireDefault(_defaultRowRenderer); -var _SortDirection2 = require('./SortDirection'); +var _SortDirection = require('./SortDirection'); -var _SortDirection3 = _interopRequireDefault(_SortDirection2); +var _SortDirection2 = _interopRequireDefault(_SortDirection); -var _SortIndicator2 = require('./SortIndicator'); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -var _SortIndicator3 = _interopRequireDefault(_SortIndicator2); +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } -exports.default = _FlexTable3.default; -exports.FlexTable = _FlexTable3.default; -exports.FlexColumn = _FlexColumn3.default; -exports.SortDirection = _SortDirection3.default; -exports.SortIndicator = _SortIndicator3.default; -},{"./FlexColumn":49,"./FlexTable":50,"./SortDirection":51,"./SortIndicator":52}],54:[function(require,module,exports){ -'use strict'; +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } -Object.defineProperty(exports, "__esModule", { - value: true -}); +/** + * Table component with fixed headers and virtualized rows for improved performance with large data sets. + * This component expects explicit width, height, and padding parameters. + */ +var FlexTable = function (_Component) { + _inherits(FlexTable, _Component); -var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + function FlexTable(props) { + _classCallCheck(this, FlexTable); -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + var _this = _possibleConstructorReturn(this, (FlexTable.__proto__ || Object.getPrototypeOf(FlexTable)).call(this, props)); -var _react = require('react'); + _this.state = { + scrollbarWidth: 0 + }; -var _react2 = _interopRequireDefault(_react); + _this._cellClassName = _this._cellClassName.bind(_this); + _this._cellStyle = _this._cellStyle.bind(_this); + _this._createColumn = _this._createColumn.bind(_this); + _this._createRow = _this._createRow.bind(_this); + _this._onScroll = _this._onScroll.bind(_this); + _this._onSectionRendered = _this._onSectionRendered.bind(_this); + return _this; + } -var _classnames = require('classnames'); + _createClass(FlexTable, [{ + key: 'forceUpdateGrid', + value: function forceUpdateGrid() { + this.Grid.forceUpdate(); + } -var _classnames2 = _interopRequireDefault(_classnames); + /** See Grid#measureAllCells */ -var _calculateSizeAndPositionDataAndUpdateScrollOffset = require('./utils/calculateSizeAndPositionDataAndUpdateScrollOffset'); - -var _calculateSizeAndPositionDataAndUpdateScrollOffset2 = _interopRequireDefault(_calculateSizeAndPositionDataAndUpdateScrollOffset); - -var _createCallbackMemoizer = require('../utils/createCallbackMemoizer'); - -var _createCallbackMemoizer2 = _interopRequireDefault(_createCallbackMemoizer); - -var _getNearestIndex = require('./utils/getNearestIndex'); + }, { + key: 'measureAllRows', + value: function measureAllRows() { + this.Grid.measureAllCells(); + } -var _getNearestIndex2 = _interopRequireDefault(_getNearestIndex); + /** See Grid#recomputeGridSize */ -var _getOverscanIndices = require('./utils/getOverscanIndices'); + }, { + key: 'recomputeRowHeights', + value: function recomputeRowHeights() { + var index = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0]; -var _getOverscanIndices2 = _interopRequireDefault(_getOverscanIndices); + this.Grid.recomputeGridSize({ + rowIndex: index + }); + this.forceUpdateGrid(); + } + }, { + key: 'componentDidMount', + value: function componentDidMount() { + this._setScrollbarWidth(); + } + }, { + key: 'componentDidUpdate', + value: function componentDidUpdate() { + this._setScrollbarWidth(); + } + }, { + key: 'render', + value: function render() { + var _this2 = this; -var _scrollbarSize = require('dom-helpers/util/scrollbarSize'); + var _props = this.props; + var children = _props.children; + var className = _props.className; + var disableHeader = _props.disableHeader; + var gridClassName = _props.gridClassName; + var gridStyle = _props.gridStyle; + var headerHeight = _props.headerHeight; + var height = _props.height; + var noRowsRenderer = _props.noRowsRenderer; + var rowClassName = _props.rowClassName; + var rowStyle = _props.rowStyle; + var scrollToIndex = _props.scrollToIndex; + var style = _props.style; + var width = _props.width; + var scrollbarWidth = this.state.scrollbarWidth; -var _scrollbarSize2 = _interopRequireDefault(_scrollbarSize); -var _getUpdatedOffsetForIndex = require('../utils/getUpdatedOffsetForIndex'); + var availableRowsHeight = height - headerHeight; -var _getUpdatedOffsetForIndex2 = _interopRequireDefault(_getUpdatedOffsetForIndex); + var rowClass = rowClassName instanceof Function ? rowClassName({ index: -1 }) : rowClassName; + var rowStyleObject = rowStyle instanceof Function ? rowStyle({ index: -1 }) : rowStyle; -var _getVisibleCellIndices = require('./utils/getVisibleCellIndices'); + // Precompute and cache column styles before rendering rows and columns to speed things up + this._cachedColumnStyles = []; + _react2.default.Children.toArray(children).forEach(function (column, index) { + _this2._cachedColumnStyles[index] = _this2._getFlexStyleForColumn(column, column.props.style); + }); -var _getVisibleCellIndices2 = _interopRequireDefault(_getVisibleCellIndices); + // Note that we specify :numChildren, :scrollbarWidth, :sortBy, and :sortDirection as properties on Grid even though these have nothing to do with Grid. + // This is done because Grid is a pure component and won't update unless its properties or state has changed. + // Any property that should trigger a re-render of Grid then is specified here to avoid a stale display. + return _react2.default.createElement( + 'div', + { + className: (0, _classnames2.default)('FlexTable', className), + style: style + }, + !disableHeader && _react2.default.createElement( + 'div', + { + className: (0, _classnames2.default)('FlexTable__headerRow', rowClass), + style: _extends({}, rowStyleObject, { + height: headerHeight, + paddingRight: scrollbarWidth, + width: width + }) + }, + this._getRenderedHeaderRow() + ), + _react2.default.createElement(_Grid2.default, _extends({}, this.props, { + autoContainerWidth: true, + className: (0, _classnames2.default)('FlexTable__Grid', gridClassName), + cellClassName: this._cellClassName, + cellRenderer: this._createRow, + cellStyle: this._cellStyle, + columnWidth: width, + columnCount: 1, + height: availableRowsHeight, + noContentRenderer: noRowsRenderer, + onScroll: this._onScroll, + onSectionRendered: this._onSectionRendered, + ref: function ref(_ref) { + _this2.Grid = _ref; + }, + scrollbarWidth: scrollbarWidth, + scrollToRow: scrollToIndex, + style: gridStyle + })) + ); + } + }, { + key: 'shouldComponentUpdate', + value: function shouldComponentUpdate(nextProps, nextState) { + return (0, _reactAddonsShallowCompare2.default)(this, nextProps, nextState); + } + }, { + key: '_cellClassName', + value: function _cellClassName(_ref2) { + var rowIndex = _ref2.rowIndex; + var rowWrapperClassName = this.props.rowWrapperClassName; -var _initCellMetadata = require('../utils/initCellMetadata'); -var _initCellMetadata2 = _interopRequireDefault(_initCellMetadata); + return rowWrapperClassName instanceof Function ? rowWrapperClassName({ index: rowIndex - 1 }) : rowWrapperClassName; + } + }, { + key: '_cellStyle', + value: function _cellStyle(_ref3) { + var rowIndex = _ref3.rowIndex; + var rowWrapperStyle = this.props.rowWrapperStyle; -var _raf = require('raf'); -var _raf2 = _interopRequireDefault(_raf); + return rowWrapperStyle instanceof Function ? rowWrapperStyle({ index: rowIndex - 1 }) : rowWrapperStyle; + } + }, { + key: '_createColumn', + value: function _createColumn(_ref4) { + var column = _ref4.column; + var columnIndex = _ref4.columnIndex; + var isScrolling = _ref4.isScrolling; + var rowData = _ref4.rowData; + var rowIndex = _ref4.rowIndex; + var _column$props = column.props; + var cellDataGetter = _column$props.cellDataGetter; + var cellRenderer = _column$props.cellRenderer; + var className = _column$props.className; + var columnData = _column$props.columnData; + var dataKey = _column$props.dataKey; -var _reactAddonsShallowCompare = require('react-addons-shallow-compare'); -var _reactAddonsShallowCompare2 = _interopRequireDefault(_reactAddonsShallowCompare); + var cellData = cellDataGetter({ columnData: columnData, dataKey: dataKey, rowData: rowData }); + var renderedCell = cellRenderer({ cellData: cellData, columnData: columnData, dataKey: dataKey, isScrolling: isScrolling, rowData: rowData, rowIndex: rowIndex }); -var _updateScrollIndexHelper = require('./utils/updateScrollIndexHelper'); + var style = this._cachedColumnStyles[columnIndex]; -var _updateScrollIndexHelper2 = _interopRequireDefault(_updateScrollIndexHelper); + var title = typeof renderedCell === 'string' ? renderedCell : null; -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + return _react2.default.createElement( + 'div', + { + key: 'Row' + rowIndex + '-Col' + columnIndex, + className: (0, _classnames2.default)('FlexTable__rowColumn', className), + style: style, + title: title + }, + renderedCell + ); + } + }, { + key: '_createHeader', + value: function _createHeader(_ref5) { + var column = _ref5.column; + var index = _ref5.index; + var _props2 = this.props; + var headerClassName = _props2.headerClassName; + var headerStyle = _props2.headerStyle; + var onHeaderClick = _props2.onHeaderClick; + var sort = _props2.sort; + var sortBy = _props2.sortBy; + var sortDirection = _props2.sortDirection; + var _column$props2 = column.props; + var dataKey = _column$props2.dataKey; + var disableSort = _column$props2.disableSort; + var headerRenderer = _column$props2.headerRenderer; + var label = _column$props2.label; + var columnData = _column$props2.columnData; -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + var sortEnabled = !disableSort && sort; -function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + var classNames = (0, _classnames2.default)('FlexTable__headerColumn', headerClassName, column.props.headerClassName, { + 'FlexTable__sortableHeaderColumn': sortEnabled + }); + var style = this._getFlexStyleForColumn(column, headerStyle); -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + var renderedHeader = headerRenderer({ + columnData: columnData, + dataKey: dataKey, + disableSort: disableSort, + label: label, + sortBy: sortBy, + sortDirection: sortDirection + }); -/** - * Specifies the number of miliseconds during which to disable pointer events while a scroll is in progress. - * This improves performance and makes scrolling smoother. - */ -var IS_SCROLLING_TIMEOUT = 150; + var a11yProps = {}; -/** - * Controls whether the Grid updates the DOM element's scrollLeft/scrollTop based on the current state or just observes it. - * This prevents Grid from interrupting mouse-wheel animations (see issue #2). - */ -var SCROLL_POSITION_CHANGE_REASONS = { - OBSERVED: 'observed', - REQUESTED: 'requested' -}; + if (sortEnabled || onHeaderClick) { + (function () { + // If this is a sortable header, clicking it should update the table data's sorting. + var newSortDirection = sortBy !== dataKey || sortDirection === _SortDirection2.default.DESC ? _SortDirection2.default.ASC : _SortDirection2.default.DESC; -/** - * Renders tabular data with virtualization along the vertical and horizontal axes. - * Row heights and column widths must be known ahead of time and specified as properties. - */ + var onClick = function onClick() { + sortEnabled && sort({ + sortBy: dataKey, + sortDirection: newSortDirection + }); + onHeaderClick && onHeaderClick({ columnData: columnData, dataKey: dataKey }); + }; -var Grid = function (_Component) { - _inherits(Grid, _Component); + var onKeyDown = function onKeyDown(event) { + if (event.key === 'Enter' || event.key === ' ') { + onClick(); + } + }; - function Grid(props, context) { - _classCallCheck(this, Grid); + a11yProps['aria-label'] = column.props['aria-label'] || label || dataKey; + a11yProps.role = 'rowheader'; + a11yProps.tabIndex = 0; + a11yProps.onClick = onClick; + a11yProps.onKeyDown = onKeyDown; + })(); + } - var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(Grid).call(this, props, context)); + return _react2.default.createElement( + 'div', + _extends({}, a11yProps, { + key: 'Header-Col' + index, + className: classNames, + style: style + }), + renderedHeader + ); + } + }, { + key: '_createRow', + value: function _createRow(_ref6) { + var _this3 = this; - _this.state = { - computeGridMetadataOnNextUpdate: false, - isScrolling: false, - scrollLeft: 0, - scrollTop: 0 - }; + var index = _ref6.rowIndex; + var isScrolling = _ref6.isScrolling; + var _props3 = this.props; + var children = _props3.children; + var onRowClick = _props3.onRowClick; + var onRowDoubleClick = _props3.onRowDoubleClick; + var onRowMouseOver = _props3.onRowMouseOver; + var onRowMouseOut = _props3.onRowMouseOut; + var rowClassName = _props3.rowClassName; + var rowGetter = _props3.rowGetter; + var rowRenderer = _props3.rowRenderer; + var rowStyle = _props3.rowStyle; + var scrollbarWidth = this.state.scrollbarWidth; - // Invokes onSectionRendered callback only when start/stop row or column indices change - _this._onGridRenderedMemoizer = (0, _createCallbackMemoizer2.default)(); - _this._onScrollMemoizer = (0, _createCallbackMemoizer2.default)(false); - // Bind functions to instance so they don't lose context when passed around - _this._computeColumnMetadata = _this._computeColumnMetadata.bind(_this); - _this._computeRowMetadata = _this._computeRowMetadata.bind(_this); - _this._invokeOnGridRenderedHelper = _this._invokeOnGridRenderedHelper.bind(_this); - _this._onScroll = _this._onScroll.bind(_this); - _this._updateScrollLeftForScrollToColumn = _this._updateScrollLeftForScrollToColumn.bind(_this); - _this._updateScrollTopForScrollToRow = _this._updateScrollTopForScrollToRow.bind(_this); - return _this; - } + var rowClass = rowClassName instanceof Function ? rowClassName({ index: index }) : rowClassName; + var rowStyleObject = rowStyle instanceof Function ? rowStyle({ index: index }) : rowStyle; + var rowData = rowGetter({ index: index }); - /** - * Forced recompute of row heights and column widths. - * This function should be called if dynamic column or row sizes have changed but nothing else has. - * Since Grid only receives :columnsCount and :rowsCount it has no way of detecting when the underlying data changes. - */ + var columns = _react2.default.Children.toArray(children).map(function (column, columnIndex) { + return _this3._createColumn({ + column: column, + columnIndex: columnIndex, + isScrolling: isScrolling, + rowData: rowData, + rowIndex: index, + scrollbarWidth: scrollbarWidth + }); + }); + var className = (0, _classnames2.default)('FlexTable__row', rowClass); + var style = _extends({}, rowStyleObject, { + height: this._getRowHeight(index), + paddingRight: scrollbarWidth + }); - _createClass(Grid, [{ - key: 'recomputeGridSize', - value: function recomputeGridSize() { - this.setState({ - computeGridMetadataOnNextUpdate: true + return rowRenderer({ + className: className, + columns: columns, + index: index, + isScrolling: isScrolling, + onRowClick: onRowClick, + onRowDoubleClick: onRowDoubleClick, + onRowMouseOver: onRowMouseOver, + onRowMouseOut: onRowMouseOut, + rowData: rowData, + style: style }); } + + /** + * Determines the flex-shrink, flex-grow, and width values for a cell (header or column). + */ + }, { - key: 'componentDidMount', - value: function componentDidMount() { - var _props = this.props; - var scrollLeft = _props.scrollLeft; - var scrollToColumn = _props.scrollToColumn; - var scrollTop = _props.scrollTop; - var scrollToRow = _props.scrollToRow; + key: '_getFlexStyleForColumn', + value: function _getFlexStyleForColumn(column) { + var customStyle = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + var flexValue = column.props.flexGrow + ' ' + column.props.flexShrink + ' ' + column.props.width + 'px'; - this._scrollbarSize = (0, _scrollbarSize2.default)(); + var style = _extends({}, customStyle, { + flex: flexValue, + msFlex: flexValue, + WebkitFlex: flexValue + }); - if (scrollLeft >= 0 || scrollTop >= 0) { - this._setScrollPosition({ scrollLeft: scrollLeft, scrollTop: scrollTop }); + if (column.props.maxWidth) { + style.maxWidth = column.props.maxWidth; } - if (scrollToColumn >= 0 || scrollToRow >= 0) { - this._updateScrollLeftForScrollToColumn(); - this._updateScrollTopForScrollToRow(); + if (column.props.minWidth) { + style.minWidth = column.props.minWidth; } - // Update onRowsRendered callback - this._invokeOnGridRenderedHelper(); - - // Initialize onScroll callback - this._invokeOnScrollMemoizer({ - scrollLeft: scrollLeft || 0, - scrollTop: scrollTop || 0, - totalColumnsWidth: this._getTotalColumnsWidth(), - totalRowsHeight: this._getTotalRowsHeight() - }); + return style; } - - /** - * @private + }, { + key: '_getRenderedHeaderRow', + value: function _getRenderedHeaderRow() { + var _this4 = this; + + var _props4 = this.props; + var children = _props4.children; + var disableHeader = _props4.disableHeader; + + var items = disableHeader ? [] : _react2.default.Children.toArray(children); + + return items.map(function (column, index) { + return _this4._createHeader({ column: column, index: index }); + }); + } + }, { + key: '_getRowHeight', + value: function _getRowHeight(rowIndex) { + var rowHeight = this.props.rowHeight; + + + return rowHeight instanceof Function ? rowHeight({ index: rowIndex }) : rowHeight; + } + }, { + key: '_onScroll', + value: function _onScroll(_ref7) { + var clientHeight = _ref7.clientHeight; + var scrollHeight = _ref7.scrollHeight; + var scrollTop = _ref7.scrollTop; + var onScroll = this.props.onScroll; + + + onScroll({ clientHeight: clientHeight, scrollHeight: scrollHeight, scrollTop: scrollTop }); + } + }, { + key: '_onSectionRendered', + value: function _onSectionRendered(_ref8) { + var rowOverscanStartIndex = _ref8.rowOverscanStartIndex; + var rowOverscanStopIndex = _ref8.rowOverscanStopIndex; + var rowStartIndex = _ref8.rowStartIndex; + var rowStopIndex = _ref8.rowStopIndex; + var onRowsRendered = this.props.onRowsRendered; + + + onRowsRendered({ + overscanStartIndex: rowOverscanStartIndex, + overscanStopIndex: rowOverscanStopIndex, + startIndex: rowStartIndex, + stopIndex: rowStopIndex + }); + } + }, { + key: '_setScrollbarWidth', + value: function _setScrollbarWidth() { + var Grid = (0, _reactDom.findDOMNode)(this.Grid); + var clientWidth = Grid.clientWidth || 0; + var offsetWidth = Grid.offsetWidth || 0; + var scrollbarWidth = offsetWidth - clientWidth; + + this.setState({ scrollbarWidth: scrollbarWidth }); + } + }]); + + return FlexTable; +}(_react.Component); + +FlexTable.propTypes = { + 'aria-label': _react.PropTypes.string, + + /** + * Removes fixed height from the scrollingContainer so that the total height + * of rows can stretch the window. Intended for use with WindowScroller + */ + autoHeight: _react.PropTypes.bool, + + /** One or more FlexColumns describing the data displayed in this row */ + children: function children(props, propName, componentName) { + var children = _react2.default.Children.toArray(props.children); + for (var i = 0; i < children.length; i++) { + if (children[i].type !== _FlexColumn2.default) { + return new Error('FlexTable only accepts children of type FlexColumn'); + } + } + }, + + /** Optional CSS class name */ + className: _react.PropTypes.string, + + /** Disable rendering the header at all */ + disableHeader: _react.PropTypes.bool, + + /** + * Used to estimate the total height of a FlexTable before all of its rows have actually been measured. + * The estimated total height is adjusted as rows are rendered. + */ + estimatedRowSize: _react.PropTypes.number.isRequired, + + /** Optional custom CSS class name to attach to inner Grid element. */ + gridClassName: _react.PropTypes.string, + + /** Optional inline style to attach to inner Grid element. */ + gridStyle: _react.PropTypes.object, + + /** Optional CSS class to apply to all column headers */ + headerClassName: _react.PropTypes.string, + + /** Fixed height of header row */ + headerHeight: _react.PropTypes.number.isRequired, + + /** Fixed/available height for out DOM element */ + height: _react.PropTypes.number.isRequired, + + /** Optional renderer to be used in place of table body rows when rowCount is 0 */ + noRowsRenderer: _react.PropTypes.func, + + /** + * Optional callback when a column's header is clicked. + * ({ columnData: any, dataKey: string }): void + */ + onHeaderClick: _react.PropTypes.func, + + /** Optional custom inline style to attach to table header columns. */ + headerStyle: _react.PropTypes.object, + + /** + * Callback invoked when a user clicks on a table row. + * ({ index: number }): void + */ + onRowClick: _react.PropTypes.func, + + /** + * Callback invoked when a user double-clicks on a table row. + * ({ index: number }): void + */ + onRowDoubleClick: _react.PropTypes.func, + + /** + * Callback invoked when the mouse leaves a table row. + * ({ index: number }): void + */ + onRowMouseOut: _react.PropTypes.func, + + /** + * Callback invoked when a user moves the mouse over a table row. + * ({ index: number }): void + */ + onRowMouseOver: _react.PropTypes.func, + + /** + * Callback invoked with information about the slice of rows that were just rendered. + * ({ startIndex, stopIndex }): void + */ + onRowsRendered: _react.PropTypes.func, + + /** + * Callback invoked whenever the scroll offset changes within the inner scrollable region. + * This callback can be used to sync scrolling between lists, tables, or grids. + * ({ clientHeight, scrollHeight, scrollTop }): void + */ + onScroll: _react.PropTypes.func.isRequired, + + /** + * Number of rows to render above/below the visible bounds of the list. + * These rows can help for smoother scrolling on touch devices. + */ + overscanRowCount: _react.PropTypes.number.isRequired, + + /** + * Optional CSS class to apply to all table rows (including the header row). + * This property can be a CSS class name (string) or a function that returns a class name. + * If a function is provided its signature should be: ({ index: number }): string + */ + rowClassName: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.func]), + + /** + * Callback responsible for returning a data row given an index. + * ({ index: number }): any + */ + rowGetter: _react.PropTypes.func.isRequired, + + /** + * Either a fixed row height (number) or a function that returns the height of a row given its index. + * ({ index: number }): number + */ + rowHeight: _react.PropTypes.oneOfType([_react.PropTypes.number, _react.PropTypes.func]).isRequired, + + /** Number of rows in table. */ + rowCount: _react.PropTypes.number.isRequired, + + /** + * Responsible for rendering a table row given an array of columns: + * Should implement the following interface: ({ + * className: string, + * columns: Array, + * index: number, + * isScrolling: boolean, + * onRowClick: ?Function, + * onRowDoubleClick: ?Function, + * onRowMouseOver: ?Function, + * onRowMouseOut: ?Function, + * rowData: any, + * style: any + * }): PropTypes.node + */ + rowRenderer: _react.PropTypes.func, + + /** Optional custom inline style to attach to table rows. */ + rowStyle: _react.PropTypes.oneOfType([_react.PropTypes.object, _react.PropTypes.func]).isRequired, + + /** Optional custom CSS class for individual rows */ + rowWrapperClassName: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.func]), + + /** Optional custom CSS class for individual rows */ + rowWrapperStyle: _react.PropTypes.oneOfType([_react.PropTypes.object, _react.PropTypes.func]), + + /** See Grid#scrollToAlignment */ + scrollToAlignment: _react.PropTypes.oneOf(['auto', 'end', 'start', 'center']).isRequired, + + /** Row index to ensure visible (by forcefully scrolling if necessary) */ + scrollToIndex: _react.PropTypes.number, + + /** Vertical offset. */ + scrollTop: _react.PropTypes.number, + + /** + * Sort function to be called if a sortable header is clicked. + * ({ sortBy: string, sortDirection: SortDirection }): void + */ + sort: _react.PropTypes.func, + + /** FlexTable data is currently sorted by this :dataKey (if it is sorted at all) */ + sortBy: _react.PropTypes.string, + + /** FlexTable data is currently sorted in this direction (if it is sorted at all) */ + sortDirection: _react.PropTypes.oneOf([_SortDirection2.default.ASC, _SortDirection2.default.DESC]), + + /** Optional inline style */ + style: _react.PropTypes.object, + + /** Tab index for focus */ + tabIndex: _react.PropTypes.number, + + /** Width of list */ + width: _react.PropTypes.number.isRequired +}; +FlexTable.defaultProps = { + disableHeader: false, + estimatedRowSize: 30, + headerHeight: 0, + headerStyle: {}, + noRowsRenderer: function noRowsRenderer() { + return null; + }, + onRowsRendered: function onRowsRendered() { + return null; + }, + onScroll: function onScroll() { + return null; + }, + overscanRowCount: 10, + rowRenderer: _defaultRowRenderer2.default, + rowStyle: {}, + scrollToAlignment: 'auto', + style: {} +}; +exports.default = FlexTable; +},{"../Grid":68,"./FlexColumn":57,"./SortDirection":59,"./defaultRowRenderer":64,"classnames":undefined,"react":undefined,"react-addons-shallow-compare":18,"react-dom":undefined}],59:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +var SortDirection = { + /** + * Sort items in ascending order. + * This means arranging from the lowest value to the highest (e.g. a-z, 0-9). + */ + ASC: 'ASC', + + /** + * Sort items in descending order. + * This means arranging from the highest value to the lowest (e.g. z-a, 9-0). + */ + DESC: 'DESC' +}; + +exports.default = SortDirection; +},{}],60:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = SortIndicator; + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _classnames = require('classnames'); + +var _classnames2 = _interopRequireDefault(_classnames); + +var _SortDirection = require('./SortDirection'); + +var _SortDirection2 = _interopRequireDefault(_SortDirection); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Displayed beside a header to indicate that a FlexTable is currently sorted by this column. + */ +function SortIndicator(_ref) { + var sortDirection = _ref.sortDirection; + + var classNames = (0, _classnames2.default)('FlexTable__sortableHeaderIcon', { + 'FlexTable__sortableHeaderIcon--ASC': sortDirection === _SortDirection2.default.ASC, + 'FlexTable__sortableHeaderIcon--DESC': sortDirection === _SortDirection2.default.DESC + }); + + return _react2.default.createElement( + 'svg', + { + className: classNames, + width: 18, + height: 18, + viewBox: '0 0 24 24' + }, + sortDirection === _SortDirection2.default.ASC ? _react2.default.createElement('path', { d: 'M7 14l5-5 5 5z' }) : _react2.default.createElement('path', { d: 'M7 10l5 5 5-5z' }), + _react2.default.createElement('path', { d: 'M0 0h24v24H0z', fill: 'none' }) + ); +} +SortIndicator.propTypes = { + sortDirection: _react.PropTypes.oneOf([_SortDirection2.default.ASC, _SortDirection2.default.DESC]) +}; +},{"./SortDirection":59,"classnames":undefined,"react":undefined}],61:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = defaultCellDataGetter; + + +/** + * Default accessor for returning a cell value for a given attribute. + * This function expects to operate on either a vanilla Object or an Immutable Map. + * You should override the column's cellDataGetter if your data is some other type of object. + */ +function defaultCellDataGetter(_ref) { + var columnData = _ref.columnData; + var dataKey = _ref.dataKey; + var rowData = _ref.rowData; + + if (rowData.get instanceof Function) { + return rowData.get(dataKey); + } else { + return rowData[dataKey]; + } +} +},{}],62:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = defaultCellRenderer; + + +/** + * Default cell renderer that displays an attribute as a simple string + * You should override the column's cellRenderer if your data is some other type of object. + */ +function defaultCellRenderer(_ref) { + var cellData = _ref.cellData; + var cellDataKey = _ref.cellDataKey; + var columnData = _ref.columnData; + var rowData = _ref.rowData; + var rowIndex = _ref.rowIndex; + + if (cellData == null) { + return ''; + } else { + return String(cellData); + } +} +},{}],63:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = defaultHeaderRenderer; + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _SortIndicator = require('./SortIndicator'); + +var _SortIndicator2 = _interopRequireDefault(_SortIndicator); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Default table header renderer. + */ +function defaultHeaderRenderer(_ref) { + var columnData = _ref.columnData; + var dataKey = _ref.dataKey; + var disableSort = _ref.disableSort; + var label = _ref.label; + var sortBy = _ref.sortBy; + var sortDirection = _ref.sortDirection; + + var showSortIndicator = sortBy === dataKey; + var children = [_react2.default.createElement( + 'span', + { + className: 'FlexTable__headerTruncatedText', + key: 'label', + title: label + }, + label + )]; + + if (showSortIndicator) { + children.push(_react2.default.createElement(_SortIndicator2.default, { + key: 'SortIndicator', + sortDirection: sortDirection + })); + } + + return children; +} +},{"./SortIndicator":60,"react":undefined}],64:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +exports.default = defaultRowRenderer; + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Default row renderer for FlexTable. + */ +function defaultRowRenderer(_ref) { + var className = _ref.className; + var columns = _ref.columns; + var index = _ref.index; + var isScrolling = _ref.isScrolling; + var onRowClick = _ref.onRowClick; + var onRowDoubleClick = _ref.onRowDoubleClick; + var onRowMouseOver = _ref.onRowMouseOver; + var onRowMouseOut = _ref.onRowMouseOut; + var rowData = _ref.rowData; + var style = _ref.style; + + var a11yProps = {}; + + if (onRowClick || onRowDoubleClick || onRowMouseOver || onRowMouseOut) { + a11yProps['aria-label'] = 'row'; + a11yProps.role = 'row'; + a11yProps.tabIndex = 0; + + if (onRowClick) { + a11yProps.onClick = function () { + return onRowClick({ index: index }); + }; + } + if (onRowDoubleClick) { + a11yProps.onDoubleClick = function () { + return onRowDoubleClick({ index: index }); + }; + } + if (onRowMouseOut) { + a11yProps.onMouseOut = function () { + return onRowMouseOut({ index: index }); + }; + } + if (onRowMouseOver) { + a11yProps.onMouseOver = function () { + return onRowMouseOver({ index: index }); + }; + } + } + + return _react2.default.createElement( + 'div', + _extends({}, a11yProps, { + className: className, + style: style + }), + columns + ); +} +},{"react":undefined}],65:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.SortIndicator = exports.SortDirection = exports.FlexColumn = exports.FlexTable = exports.defaultRowRenderer = exports.defaultHeaderRenderer = exports.defaultCellRenderer = exports.defaultCellDataGetter = exports.default = undefined; + +var _FlexTable2 = require('./FlexTable'); + +var _FlexTable3 = _interopRequireDefault(_FlexTable2); + +var _defaultCellDataGetter2 = require('./defaultCellDataGetter'); + +var _defaultCellDataGetter3 = _interopRequireDefault(_defaultCellDataGetter2); + +var _defaultCellRenderer2 = require('./defaultCellRenderer'); + +var _defaultCellRenderer3 = _interopRequireDefault(_defaultCellRenderer2); + +var _defaultHeaderRenderer2 = require('./defaultHeaderRenderer'); + +var _defaultHeaderRenderer3 = _interopRequireDefault(_defaultHeaderRenderer2); + +var _defaultRowRenderer2 = require('./defaultRowRenderer'); + +var _defaultRowRenderer3 = _interopRequireDefault(_defaultRowRenderer2); + +var _FlexColumn2 = require('./FlexColumn'); + +var _FlexColumn3 = _interopRequireDefault(_FlexColumn2); + +var _SortDirection2 = require('./SortDirection'); + +var _SortDirection3 = _interopRequireDefault(_SortDirection2); + +var _SortIndicator2 = require('./SortIndicator'); + +var _SortIndicator3 = _interopRequireDefault(_SortIndicator2); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +exports.default = _FlexTable3.default; +exports.defaultCellDataGetter = _defaultCellDataGetter3.default; +exports.defaultCellRenderer = _defaultCellRenderer3.default; +exports.defaultHeaderRenderer = _defaultHeaderRenderer3.default; +exports.defaultRowRenderer = _defaultRowRenderer3.default; +exports.FlexTable = _FlexTable3.default; +exports.FlexColumn = _FlexColumn3.default; +exports.SortDirection = _SortDirection3.default; +exports.SortIndicator = _SortIndicator3.default; +},{"./FlexColumn":57,"./FlexTable":58,"./SortDirection":59,"./SortIndicator":60,"./defaultCellDataGetter":61,"./defaultCellRenderer":62,"./defaultHeaderRenderer":63,"./defaultRowRenderer":64}],66:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DEFAULT_SCROLLING_RESET_TIME_INTERVAL = undefined; + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _classnames = require('classnames'); + +var _classnames2 = _interopRequireDefault(_classnames); + +var _calculateSizeAndPositionDataAndUpdateScrollOffset = require('./utils/calculateSizeAndPositionDataAndUpdateScrollOffset'); + +var _calculateSizeAndPositionDataAndUpdateScrollOffset2 = _interopRequireDefault(_calculateSizeAndPositionDataAndUpdateScrollOffset); + +var _ScalingCellSizeAndPositionManager = require('./utils/ScalingCellSizeAndPositionManager'); + +var _ScalingCellSizeAndPositionManager2 = _interopRequireDefault(_ScalingCellSizeAndPositionManager); + +var _createCallbackMemoizer = require('../utils/createCallbackMemoizer'); + +var _createCallbackMemoizer2 = _interopRequireDefault(_createCallbackMemoizer); + +var _getOverscanIndices = require('./utils/getOverscanIndices'); + +var _getOverscanIndices2 = _interopRequireDefault(_getOverscanIndices); + +var _scrollbarSize = require('dom-helpers/util/scrollbarSize'); + +var _scrollbarSize2 = _interopRequireDefault(_scrollbarSize); + +var _raf = require('raf'); + +var _raf2 = _interopRequireDefault(_raf); + +var _reactAddonsShallowCompare = require('react-addons-shallow-compare'); + +var _reactAddonsShallowCompare2 = _interopRequireDefault(_reactAddonsShallowCompare); + +var _updateScrollIndexHelper = require('./utils/updateScrollIndexHelper'); + +var _updateScrollIndexHelper2 = _interopRequireDefault(_updateScrollIndexHelper); + +var _defaultCellRangeRenderer = require('./defaultCellRangeRenderer'); + +var _defaultCellRangeRenderer2 = _interopRequireDefault(_defaultCellRangeRenderer); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +/** + * Specifies the number of miliseconds during which to disable pointer events while a scroll is in progress. + * This improves performance and makes scrolling smoother. + */ +var DEFAULT_SCROLLING_RESET_TIME_INTERVAL = exports.DEFAULT_SCROLLING_RESET_TIME_INTERVAL = 150; + +/** + * Controls whether the Grid updates the DOM element's scrollLeft/scrollTop based on the current state or just observes it. + * This prevents Grid from interrupting mouse-wheel animations (see issue #2). + */ +var SCROLL_POSITION_CHANGE_REASONS = { + OBSERVED: 'observed', + REQUESTED: 'requested' +}; + +/** + * Renders tabular data with virtualization along the vertical and horizontal axes. + * Row heights and column widths must be known ahead of time and specified as properties. + */ + +var Grid = function (_Component) { + _inherits(Grid, _Component); + + function Grid(props, context) { + _classCallCheck(this, Grid); + + var _this = _possibleConstructorReturn(this, (Grid.__proto__ || Object.getPrototypeOf(Grid)).call(this, props, context)); + + _this.state = { + isScrolling: false, + scrollDirectionHorizontal: _getOverscanIndices.SCROLL_DIRECTION_FIXED, + scrollDirectionVertical: _getOverscanIndices.SCROLL_DIRECTION_FIXED, + scrollLeft: 0, + scrollTop: 0 + }; + + // Invokes onSectionRendered callback only when start/stop row or column indices change + _this._onGridRenderedMemoizer = (0, _createCallbackMemoizer2.default)(); + _this._onScrollMemoizer = (0, _createCallbackMemoizer2.default)(false); + + // Bind functions to instance so they don't lose context when passed around + _this._enablePointerEventsAfterDelayCallback = _this._enablePointerEventsAfterDelayCallback.bind(_this); + _this._invokeOnGridRenderedHelper = _this._invokeOnGridRenderedHelper.bind(_this); + _this._onScroll = _this._onScroll.bind(_this); + _this._setNextStateCallback = _this._setNextStateCallback.bind(_this); + _this._updateScrollLeftForScrollToColumn = _this._updateScrollLeftForScrollToColumn.bind(_this); + _this._updateScrollTopForScrollToRow = _this._updateScrollTopForScrollToRow.bind(_this); + + _this._columnWidthGetter = _this._wrapSizeGetter(props.columnWidth); + _this._rowHeightGetter = _this._wrapSizeGetter(props.rowHeight); + + _this._columnSizeAndPositionManager = new _ScalingCellSizeAndPositionManager2.default({ + cellCount: props.columnCount, + cellSizeGetter: function cellSizeGetter(index) { + return _this._columnWidthGetter(index); + }, + estimatedCellSize: _this._getEstimatedColumnSize(props) + }); + _this._rowSizeAndPositionManager = new _ScalingCellSizeAndPositionManager2.default({ + cellCount: props.rowCount, + cellSizeGetter: function cellSizeGetter(index) { + return _this._rowHeightGetter(index); + }, + estimatedCellSize: _this._getEstimatedRowSize(props) + }); + + // See defaultCellRangeRenderer() for more information on the usage of this cache + _this._cellCache = {}; + return _this; + } + + /** + * Pre-measure all columns and rows in a Grid. + * Typically cells are only measured as needed and estimated sizes are used for cells that have not yet been measured. + * This method ensures that the next call to getTotalSize() returns an exact size (as opposed to just an estimated one). + */ + + + _createClass(Grid, [{ + key: 'measureAllCells', + value: function measureAllCells() { + var _props = this.props; + var columnCount = _props.columnCount; + var rowCount = _props.rowCount; + + + this._columnSizeAndPositionManager.getSizeAndPositionOfCell(columnCount - 1); + this._rowSizeAndPositionManager.getSizeAndPositionOfCell(rowCount - 1); + } + + /** + * Forced recompute of row heights and column widths. + * This function should be called if dynamic column or row sizes have changed but nothing else has. + * Since Grid only receives :columnCount and :rowCount it has no way of detecting when the underlying data changes. + */ + + }, { + key: 'recomputeGridSize', + value: function recomputeGridSize() { + var _ref = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; + + var _ref$columnIndex = _ref.columnIndex; + var columnIndex = _ref$columnIndex === undefined ? 0 : _ref$columnIndex; + var _ref$rowIndex = _ref.rowIndex; + var rowIndex = _ref$rowIndex === undefined ? 0 : _ref$rowIndex; + + this._columnSizeAndPositionManager.resetCell(columnIndex); + this._rowSizeAndPositionManager.resetCell(rowIndex); + + // Clear cell cache in case we are scrolling; + // Invalid row heights likely mean invalid cached content as well. + this._cellCache = {}; + + this.forceUpdate(); + } + }, { + key: 'componentDidMount', + value: function componentDidMount() { + var _props2 = this.props; + var scrollLeft = _props2.scrollLeft; + var scrollToColumn = _props2.scrollToColumn; + var scrollTop = _props2.scrollTop; + var scrollToRow = _props2.scrollToRow; + + // If this component was first rendered server-side, scrollbar size will be undefined. + // In that event we need to remeasure. + + if (!this._scrollbarSizeMeasured) { + this._scrollbarSize = (0, _scrollbarSize2.default)(); + this._scrollbarSizeMeasured = true; + this.setState({}); + } + + if (scrollLeft >= 0 || scrollTop >= 0) { + this._setScrollPosition({ scrollLeft: scrollLeft, scrollTop: scrollTop }); + } + + if (scrollToColumn >= 0 || scrollToRow >= 0) { + this._updateScrollLeftForScrollToColumn(); + this._updateScrollTopForScrollToRow(); + } + + // Update onRowsRendered callback + this._invokeOnGridRenderedHelper(); + + // Initialize onScroll callback + this._invokeOnScrollMemoizer({ + scrollLeft: scrollLeft || 0, + scrollTop: scrollTop || 0, + totalColumnsWidth: this._columnSizeAndPositionManager.getTotalSize(), + totalRowsHeight: this._rowSizeAndPositionManager.getTotalSize() + }); + } + + /** + * @private * This method updates scrollLeft/scrollTop in state for the following conditions: * 1) New scroll-to-cell props have been set */ - }, { - key: 'componentDidUpdate', - value: function componentDidUpdate(prevProps, prevState) { - var _this2 = this; + }, { + key: 'componentDidUpdate', + value: function componentDidUpdate(prevProps, prevState) { + var _this2 = this; + + var _props3 = this.props; + var autoHeight = _props3.autoHeight; + var columnCount = _props3.columnCount; + var height = _props3.height; + var rowCount = _props3.rowCount; + var scrollToAlignment = _props3.scrollToAlignment; + var scrollToColumn = _props3.scrollToColumn; + var scrollToRow = _props3.scrollToRow; + var width = _props3.width; + var _state = this.state; + var scrollLeft = _state.scrollLeft; + var scrollPositionChangeReason = _state.scrollPositionChangeReason; + var scrollTop = _state.scrollTop; + + // Handle edge case where column or row count has only just increased over 0. + // In this case we may have to restore a previously-specified scroll offset. + // For more info see bvaughn/react-virtualized/issues/218 + + var columnOrRowCountJustIncreasedFromZero = columnCount > 0 && prevProps.columnCount === 0 || rowCount > 0 && prevProps.rowCount === 0; + + // Make sure requested changes to :scrollLeft or :scrollTop get applied. + // Assigning to scrollLeft/scrollTop tells the browser to interrupt any running scroll animations, + // And to discard any pending async changes to the scroll position that may have happened in the meantime (e.g. on a separate scrolling thread). + // So we only set these when we require an adjustment of the scroll position. + // See issue #2 for more information. + if (scrollPositionChangeReason === SCROLL_POSITION_CHANGE_REASONS.REQUESTED) { + if (scrollLeft >= 0 && (scrollLeft !== prevState.scrollLeft && scrollLeft !== this._scrollingContainer.scrollLeft || columnOrRowCountJustIncreasedFromZero)) { + this._scrollingContainer.scrollLeft = scrollLeft; + } + + // @TRICKY :autoHeight property instructs Grid to leave :scrollTop management to an external HOC (eg WindowScroller). + // In this case we should avoid checking scrollingContainer.scrollTop since it forces layout/flow. + if (!autoHeight && scrollTop >= 0 && (scrollTop !== prevState.scrollTop && scrollTop !== this._scrollingContainer.scrollTop || columnOrRowCountJustIncreasedFromZero)) { + this._scrollingContainer.scrollTop = scrollTop; + } + } + + // Update scroll offsets if the current :scrollToColumn or :scrollToRow values requires it + // @TODO Do we also need this check or can the one in componentWillUpdate() suffice? + (0, _updateScrollIndexHelper2.default)({ + cellSizeAndPositionManager: this._columnSizeAndPositionManager, + previousCellsCount: prevProps.columnCount, + previousCellSize: prevProps.columnWidth, + previousScrollToAlignment: prevProps.scrollToAlignment, + previousScrollToIndex: prevProps.scrollToColumn, + previousSize: prevProps.width, + scrollOffset: scrollLeft, + scrollToAlignment: scrollToAlignment, + scrollToIndex: scrollToColumn, + size: width, + updateScrollIndexCallback: function updateScrollIndexCallback(scrollToColumn) { + return _this2._updateScrollLeftForScrollToColumn(_extends({}, _this2.props, { scrollToColumn: scrollToColumn })); + } + }); + (0, _updateScrollIndexHelper2.default)({ + cellSizeAndPositionManager: this._rowSizeAndPositionManager, + previousCellsCount: prevProps.rowCount, + previousCellSize: prevProps.rowHeight, + previousScrollToAlignment: prevProps.scrollToAlignment, + previousScrollToIndex: prevProps.scrollToRow, + previousSize: prevProps.height, + scrollOffset: scrollTop, + scrollToAlignment: scrollToAlignment, + scrollToIndex: scrollToRow, + size: height, + updateScrollIndexCallback: function updateScrollIndexCallback(scrollToRow) { + return _this2._updateScrollTopForScrollToRow(_extends({}, _this2.props, { scrollToRow: scrollToRow })); + } + }); + + // Update onRowsRendered callback if start/stop indices have changed + this._invokeOnGridRenderedHelper(); + } + }, { + key: 'componentWillMount', + value: function componentWillMount() { + // If this component is being rendered server-side, getScrollbarSize() will return undefined. + // We handle this case in componentDidMount() + this._scrollbarSize = (0, _scrollbarSize2.default)(); + if (this._scrollbarSize === undefined) { + this._scrollbarSizeMeasured = false; + this._scrollbarSize = 0; + } else { + this._scrollbarSizeMeasured = true; + } + + this._calculateChildrenToRender(); + } + }, { + key: 'componentWillUnmount', + value: function componentWillUnmount() { + if (this._disablePointerEventsTimeoutId) { + clearTimeout(this._disablePointerEventsTimeoutId); + } + + if (this._setNextStateAnimationFrameId) { + _raf2.default.cancel(this._setNextStateAnimationFrameId); + } + } + + /** + * @private + * This method updates scrollLeft/scrollTop in state for the following conditions: + * 1) Empty content (0 rows or columns) + * 2) New scroll props overriding the current state + * 3) Cells-count or cells-size has changed, making previous scroll offsets invalid + */ + + }, { + key: 'componentWillUpdate', + value: function componentWillUpdate(nextProps, nextState) { + var _this3 = this; + + if (nextProps.columnCount === 0 && nextState.scrollLeft !== 0 || nextProps.rowCount === 0 && nextState.scrollTop !== 0) { + this._setScrollPosition({ + scrollLeft: 0, + scrollTop: 0 + }); + } else if (nextProps.scrollLeft !== this.props.scrollLeft || nextProps.scrollTop !== this.props.scrollTop) { + this._setScrollPosition({ + scrollLeft: nextProps.scrollLeft, + scrollTop: nextProps.scrollTop + }); + } + + this._columnWidthGetter = this._wrapSizeGetter(nextProps.columnWidth); + this._rowHeightGetter = this._wrapSizeGetter(nextProps.rowHeight); + + this._columnSizeAndPositionManager.configure({ + cellCount: nextProps.columnCount, + estimatedCellSize: this._getEstimatedColumnSize(nextProps) + }); + this._rowSizeAndPositionManager.configure({ + cellCount: nextProps.rowCount, + estimatedCellSize: this._getEstimatedRowSize(nextProps) + }); + + // Update scroll offsets if the size or number of cells have changed, invalidating the previous value + (0, _calculateSizeAndPositionDataAndUpdateScrollOffset2.default)({ + cellCount: this.props.columnCount, + cellSize: this.props.columnWidth, + computeMetadataCallback: function computeMetadataCallback() { + return _this3._columnSizeAndPositionManager.resetCell(0); + }, + computeMetadataCallbackProps: nextProps, + nextCellsCount: nextProps.columnCount, + nextCellSize: nextProps.columnWidth, + nextScrollToIndex: nextProps.scrollToColumn, + scrollToIndex: this.props.scrollToColumn, + updateScrollOffsetForScrollToIndex: function updateScrollOffsetForScrollToIndex() { + return _this3._updateScrollLeftForScrollToColumn(nextProps, nextState); + } + }); + (0, _calculateSizeAndPositionDataAndUpdateScrollOffset2.default)({ + cellCount: this.props.rowCount, + cellSize: this.props.rowHeight, + computeMetadataCallback: function computeMetadataCallback() { + return _this3._rowSizeAndPositionManager.resetCell(0); + }, + computeMetadataCallbackProps: nextProps, + nextCellsCount: nextProps.rowCount, + nextCellSize: nextProps.rowHeight, + nextScrollToIndex: nextProps.scrollToRow, + scrollToIndex: this.props.scrollToRow, + updateScrollOffsetForScrollToIndex: function updateScrollOffsetForScrollToIndex() { + return _this3._updateScrollTopForScrollToRow(nextProps, nextState); + } + }); + + this._calculateChildrenToRender(nextProps, nextState); + } + }, { + key: 'render', + value: function render() { + var _this4 = this; + + var _props4 = this.props; + var autoContainerWidth = _props4.autoContainerWidth; + var autoHeight = _props4.autoHeight; + var className = _props4.className; + var height = _props4.height; + var noContentRenderer = _props4.noContentRenderer; + var style = _props4.style; + var tabIndex = _props4.tabIndex; + var width = _props4.width; + var isScrolling = this.state.isScrolling; + + + var gridStyle = { + height: autoHeight ? 'auto' : height, + width: width + }; + + var totalColumnsWidth = this._columnSizeAndPositionManager.getTotalSize(); + var totalRowsHeight = this._rowSizeAndPositionManager.getTotalSize(); + + // Force browser to hide scrollbars when we know they aren't necessary. + // Otherwise once scrollbars appear they may not disappear again. + // For more info see issue #116 + var verticalScrollBarSize = totalRowsHeight > height ? this._scrollbarSize : 0; + var horizontalScrollBarSize = totalColumnsWidth > width ? this._scrollbarSize : 0; + + // Also explicitly init styles to 'auto' if scrollbars are required. + // This works around an obscure edge case where external CSS styles have not yet been loaded, + // But an initial scroll index of offset is set as an external prop. + // Without this style, Grid would render the correct range of cells but would NOT update its internal offset. + // This was originally reported via clauderic/react-infinite-calendar/issues/23 + gridStyle.overflowX = totalColumnsWidth + verticalScrollBarSize <= width ? 'hidden' : 'auto'; + gridStyle.overflowY = totalRowsHeight + horizontalScrollBarSize <= height ? 'hidden' : 'auto'; + + var childrenToDisplay = this._childrenToDisplay; + + var showNoContentRenderer = childrenToDisplay.length === 0 && height > 0 && width > 0; + + return _react2.default.createElement( + 'div', + { + ref: function ref(_ref2) { + _this4._scrollingContainer = _ref2; + }, + 'aria-label': this.props['aria-label'], + className: (0, _classnames2.default)('Grid', className), + onScroll: this._onScroll, + role: 'grid', + style: _extends({}, gridStyle, style), + tabIndex: tabIndex + }, + childrenToDisplay.length > 0 && _react2.default.createElement( + 'div', + { + className: 'Grid__innerScrollContainer', + style: { + width: autoContainerWidth ? 'auto' : totalColumnsWidth, + height: totalRowsHeight, + maxWidth: totalColumnsWidth, + maxHeight: totalRowsHeight, + pointerEvents: isScrolling ? 'none' : '' + } + }, + childrenToDisplay + ), + showNoContentRenderer && noContentRenderer() + ); + } + }, { + key: 'shouldComponentUpdate', + value: function shouldComponentUpdate(nextProps, nextState) { + return (0, _reactAddonsShallowCompare2.default)(this, nextProps, nextState); + } + + /* ---------------------------- Helper methods ---------------------------- */ + + }, { + key: '_calculateChildrenToRender', + value: function _calculateChildrenToRender() { + var props = arguments.length <= 0 || arguments[0] === undefined ? this.props : arguments[0]; + var state = arguments.length <= 1 || arguments[1] === undefined ? this.state : arguments[1]; + var cellClassName = props.cellClassName; + var cellRenderer = props.cellRenderer; + var cellRangeRenderer = props.cellRangeRenderer; + var cellStyle = props.cellStyle; + var columnCount = props.columnCount; + var height = props.height; + var overscanColumnCount = props.overscanColumnCount; + var overscanRowCount = props.overscanRowCount; + var rowCount = props.rowCount; + var width = props.width; + var isScrolling = state.isScrolling; + var scrollDirectionHorizontal = state.scrollDirectionHorizontal; + var scrollDirectionVertical = state.scrollDirectionVertical; + var scrollLeft = state.scrollLeft; + var scrollTop = state.scrollTop; + + + this._childrenToDisplay = []; + + // Render only enough columns and rows to cover the visible area of the grid. + if (height > 0 && width > 0) { + var visibleColumnIndices = this._columnSizeAndPositionManager.getVisibleCellRange({ + containerSize: width, + offset: scrollLeft + }); + var visibleRowIndices = this._rowSizeAndPositionManager.getVisibleCellRange({ + containerSize: height, + offset: scrollTop + }); + + var horizontalOffsetAdjustment = this._columnSizeAndPositionManager.getOffsetAdjustment({ + containerSize: width, + offset: scrollLeft + }); + var verticalOffsetAdjustment = this._rowSizeAndPositionManager.getOffsetAdjustment({ + containerSize: height, + offset: scrollTop + }); + + // Store for _invokeOnGridRenderedHelper() + this._renderedColumnStartIndex = visibleColumnIndices.start; + this._renderedColumnStopIndex = visibleColumnIndices.stop; + this._renderedRowStartIndex = visibleRowIndices.start; + this._renderedRowStopIndex = visibleRowIndices.stop; + + var overscanColumnIndices = (0, _getOverscanIndices2.default)({ + cellCount: columnCount, + overscanCellsCount: overscanColumnCount, + scrollDirection: scrollDirectionHorizontal, + startIndex: this._renderedColumnStartIndex, + stopIndex: this._renderedColumnStopIndex + }); + + var overscanRowIndices = (0, _getOverscanIndices2.default)({ + cellCount: rowCount, + overscanCellsCount: overscanRowCount, + scrollDirection: scrollDirectionVertical, + startIndex: this._renderedRowStartIndex, + stopIndex: this._renderedRowStopIndex + }); + + // Store for _invokeOnGridRenderedHelper() + this._columnStartIndex = overscanColumnIndices.overscanStartIndex; + this._columnStopIndex = overscanColumnIndices.overscanStopIndex; + this._rowStartIndex = overscanRowIndices.overscanStartIndex; + this._rowStopIndex = overscanRowIndices.overscanStopIndex; + + this._childrenToDisplay = cellRangeRenderer({ + cellCache: this._cellCache, + cellClassName: this._wrapCellClassNameGetter(cellClassName), + cellRenderer: cellRenderer, + cellStyle: this._wrapCellStyleGetter(cellStyle), + columnSizeAndPositionManager: this._columnSizeAndPositionManager, + columnStartIndex: this._columnStartIndex, + columnStopIndex: this._columnStopIndex, + horizontalOffsetAdjustment: horizontalOffsetAdjustment, + isScrolling: isScrolling, + rowSizeAndPositionManager: this._rowSizeAndPositionManager, + rowStartIndex: this._rowStartIndex, + rowStopIndex: this._rowStopIndex, + scrollLeft: scrollLeft, + scrollTop: scrollTop, + verticalOffsetAdjustment: verticalOffsetAdjustment + }); + } + } + + /** + * Sets an :isScrolling flag for a small window of time. + * This flag is used to disable pointer events on the scrollable portion of the Grid. + * This prevents jerky/stuttery mouse-wheel scrolling. + */ + + }, { + key: '_enablePointerEventsAfterDelay', + value: function _enablePointerEventsAfterDelay() { + var scrollingResetTimeInterval = this.props.scrollingResetTimeInterval; + + + if (this._disablePointerEventsTimeoutId) { + clearTimeout(this._disablePointerEventsTimeoutId); + } + + this._disablePointerEventsTimeoutId = setTimeout(this._enablePointerEventsAfterDelayCallback, scrollingResetTimeInterval); + } + }, { + key: '_enablePointerEventsAfterDelayCallback', + value: function _enablePointerEventsAfterDelayCallback() { + this._disablePointerEventsTimeoutId = null; + + // Throw away cell cache once scrolling is complete + this._cellCache = {}; + + this.setState({ + isScrolling: false, + scrollDirectionHorizontal: _getOverscanIndices.SCROLL_DIRECTION_FIXED, + scrollDirectionVertical: _getOverscanIndices.SCROLL_DIRECTION_FIXED + }); + } + }, { + key: '_getEstimatedColumnSize', + value: function _getEstimatedColumnSize(props) { + return typeof props.columnWidth === 'number' ? props.columnWidth : props.estimatedColumnSize; + } + }, { + key: '_getEstimatedRowSize', + value: function _getEstimatedRowSize(props) { + return typeof props.rowHeight === 'number' ? props.rowHeight : props.estimatedRowSize; + } + }, { + key: '_invokeOnGridRenderedHelper', + value: function _invokeOnGridRenderedHelper() { + var onSectionRendered = this.props.onSectionRendered; + + + this._onGridRenderedMemoizer({ + callback: onSectionRendered, + indices: { + columnOverscanStartIndex: this._columnStartIndex, + columnOverscanStopIndex: this._columnStopIndex, + columnStartIndex: this._renderedColumnStartIndex, + columnStopIndex: this._renderedColumnStopIndex, + rowOverscanStartIndex: this._rowStartIndex, + rowOverscanStopIndex: this._rowStopIndex, + rowStartIndex: this._renderedRowStartIndex, + rowStopIndex: this._renderedRowStopIndex + } + }); + } + }, { + key: '_invokeOnScrollMemoizer', + value: function _invokeOnScrollMemoizer(_ref3) { + var _this5 = this; + + var scrollLeft = _ref3.scrollLeft; + var scrollTop = _ref3.scrollTop; + var totalColumnsWidth = _ref3.totalColumnsWidth; + var totalRowsHeight = _ref3.totalRowsHeight; + + this._onScrollMemoizer({ + callback: function callback(_ref4) { + var scrollLeft = _ref4.scrollLeft; + var scrollTop = _ref4.scrollTop; + var _props5 = _this5.props; + var height = _props5.height; + var onScroll = _props5.onScroll; + var width = _props5.width; + + + onScroll({ + clientHeight: height, + clientWidth: width, + scrollHeight: totalRowsHeight, + scrollLeft: scrollLeft, + scrollTop: scrollTop, + scrollWidth: totalColumnsWidth + }); + }, + indices: { + scrollLeft: scrollLeft, + scrollTop: scrollTop + } + }); + } + + /** + * Updates the state during the next animation frame. + * Use this method to avoid multiple renders in a small span of time. + * This helps performance for bursty events (like onScroll). + */ + + }, { + key: '_setNextState', + value: function _setNextState(state) { + this._nextState = state; + + if (!this._setNextStateAnimationFrameId) { + this._setNextStateAnimationFrameId = (0, _raf2.default)(this._setNextStateCallback); + } + } + }, { + key: '_setNextStateCallback', + value: function _setNextStateCallback() { + var state = this._nextState; + + this._setNextStateAnimationFrameId = null; + this._nextState = null; + + this.setState(state); + } + }, { + key: '_setScrollPosition', + value: function _setScrollPosition(_ref5) { + var scrollLeft = _ref5.scrollLeft; + var scrollTop = _ref5.scrollTop; + + var newState = { + scrollPositionChangeReason: SCROLL_POSITION_CHANGE_REASONS.REQUESTED + }; + + if (scrollLeft >= 0) { + newState.scrollLeft = scrollLeft; + } + + if (scrollTop >= 0) { + newState.scrollTop = scrollTop; + } + + if (scrollLeft >= 0 && scrollLeft !== this.state.scrollLeft || scrollTop >= 0 && scrollTop !== this.state.scrollTop) { + this.setState(newState); + } + } + }, { + key: '_wrapCellClassNameGetter', + value: function _wrapCellClassNameGetter(className) { + return this._wrapPropertyGetter(className); + } + }, { + key: '_wrapCellStyleGetter', + value: function _wrapCellStyleGetter(style) { + return this._wrapPropertyGetter(style); + } + }, { + key: '_wrapPropertyGetter', + value: function _wrapPropertyGetter(value) { + return value instanceof Function ? value : function () { + return value; + }; + } + }, { + key: '_wrapSizeGetter', + value: function _wrapSizeGetter(size) { + return this._wrapPropertyGetter(size); + } + }, { + key: '_updateScrollLeftForScrollToColumn', + value: function _updateScrollLeftForScrollToColumn() { + var props = arguments.length <= 0 || arguments[0] === undefined ? this.props : arguments[0]; + var state = arguments.length <= 1 || arguments[1] === undefined ? this.state : arguments[1]; + var columnCount = props.columnCount; + var scrollToAlignment = props.scrollToAlignment; + var scrollToColumn = props.scrollToColumn; + var width = props.width; + var scrollLeft = state.scrollLeft; + + + if (scrollToColumn >= 0 && columnCount > 0) { + var targetIndex = Math.max(0, Math.min(columnCount - 1, scrollToColumn)); + + var calculatedScrollLeft = this._columnSizeAndPositionManager.getUpdatedOffsetForIndex({ + align: scrollToAlignment, + containerSize: width, + currentOffset: scrollLeft, + targetIndex: targetIndex + }); + + if (scrollLeft !== calculatedScrollLeft) { + this._setScrollPosition({ + scrollLeft: calculatedScrollLeft + }); + } + } + } + }, { + key: '_updateScrollTopForScrollToRow', + value: function _updateScrollTopForScrollToRow() { + var props = arguments.length <= 0 || arguments[0] === undefined ? this.props : arguments[0]; + var state = arguments.length <= 1 || arguments[1] === undefined ? this.state : arguments[1]; + var height = props.height; + var rowCount = props.rowCount; + var scrollToAlignment = props.scrollToAlignment; + var scrollToRow = props.scrollToRow; + var scrollTop = state.scrollTop; + + + if (scrollToRow >= 0 && rowCount > 0) { + var targetIndex = Math.max(0, Math.min(rowCount - 1, scrollToRow)); + + var calculatedScrollTop = this._rowSizeAndPositionManager.getUpdatedOffsetForIndex({ + align: scrollToAlignment, + containerSize: height, + currentOffset: scrollTop, + targetIndex: targetIndex + }); + + if (scrollTop !== calculatedScrollTop) { + this._setScrollPosition({ + scrollTop: calculatedScrollTop + }); + } + } + } + }, { + key: '_onScroll', + value: function _onScroll(event) { + // In certain edge-cases React dispatches an onScroll event with an invalid target.scrollLeft / target.scrollTop. + // This invalid event can be detected by comparing event.target to this component's scrollable DOM element. + // See issue #404 for more information. + if (event.target !== this._scrollingContainer) { + return; + } + + // Prevent pointer events from interrupting a smooth scroll + this._enablePointerEventsAfterDelay(); + + // When this component is shrunk drastically, React dispatches a series of back-to-back scroll events, + // Gradually converging on a scrollTop that is within the bounds of the new, smaller height. + // This causes a series of rapid renders that is slow for long lists. + // We can avoid that by doing some simple bounds checking to ensure that scrollTop never exceeds the total height. + var _props6 = this.props; + var height = _props6.height; + var width = _props6.width; + + var scrollbarSize = this._scrollbarSize; + var totalRowsHeight = this._rowSizeAndPositionManager.getTotalSize(); + var totalColumnsWidth = this._columnSizeAndPositionManager.getTotalSize(); + var scrollLeft = Math.min(Math.max(0, totalColumnsWidth - width + scrollbarSize), event.target.scrollLeft); + var scrollTop = Math.min(Math.max(0, totalRowsHeight - height + scrollbarSize), event.target.scrollTop); + + // Certain devices (like Apple touchpad) rapid-fire duplicate events. + // Don't force a re-render if this is the case. + // The mouse may move faster then the animation frame does. + // Use requestAnimationFrame to avoid over-updating. + if (this.state.scrollLeft !== scrollLeft || this.state.scrollTop !== scrollTop) { + // Browsers with cancelable scroll events (eg. Firefox) interrupt scrolling animations if scrollTop/scrollLeft is set. + // Other browsers (eg. Safari) don't scroll as well without the help under certain conditions (DOM or style changes during scrolling). + // All things considered, this seems to be the best current work around that I'm aware of. + // For more information see https://github.com/bvaughn/react-virtualized/pull/124 + var scrollPositionChangeReason = event.cancelable ? SCROLL_POSITION_CHANGE_REASONS.OBSERVED : SCROLL_POSITION_CHANGE_REASONS.REQUESTED; + + // Track scrolling direction so we can more efficiently overscan rows to reduce empty space around the edges while scrolling. + var scrollDirectionVertical = scrollTop > this.state.scrollTop ? _getOverscanIndices.SCROLL_DIRECTION_FORWARD : _getOverscanIndices.SCROLL_DIRECTION_BACKWARD; + var scrollDirectionHorizontal = scrollLeft > this.state.scrollLeft ? _getOverscanIndices.SCROLL_DIRECTION_FORWARD : _getOverscanIndices.SCROLL_DIRECTION_BACKWARD; + + if (!this.state.isScrolling) { + this.setState({ + isScrolling: true + }); + } + + this._setNextState({ + isScrolling: true, + scrollDirectionHorizontal: scrollDirectionHorizontal, + scrollDirectionVertical: scrollDirectionVertical, + scrollLeft: scrollLeft, + scrollPositionChangeReason: scrollPositionChangeReason, + scrollTop: scrollTop + }); + } + + this._invokeOnScrollMemoizer({ scrollLeft: scrollLeft, scrollTop: scrollTop, totalColumnsWidth: totalColumnsWidth, totalRowsHeight: totalRowsHeight }); + } + }]); + + return Grid; +}(_react.Component); + +Grid.propTypes = { + 'aria-label': _react.PropTypes.string, + + /** + * Set the width of the inner scrollable container to 'auto'. + * This is useful for single-column Grids to ensure that the column doesn't extend below a vertical scrollbar. + */ + autoContainerWidth: _react.PropTypes.bool, + + /** + * Removes fixed height from the scrollingContainer so that the total height + * of rows can stretch the window. Intended for use with WindowScroller + */ + autoHeight: _react.PropTypes.bool, + + /** Optional custom CSS class for individual cells */ + cellClassName: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.func]), + + /** Optional custom styles for individual cells */ + cellStyle: _react.PropTypes.oneOfType([_react.PropTypes.object, _react.PropTypes.func]), + + /** + * Responsible for rendering a cell given an row and column index. + * Should implement the following interface: ({ columnIndex: number, rowIndex: number }): PropTypes.node + */ + cellRenderer: _react.PropTypes.func.isRequired, + + /** + * Responsible for rendering a group of cells given their index ranges. + * Should implement the following interface: ({ + * cellCache: Map, + * cellRenderer: Function, + * columnSizeAndPositionManager: CellSizeAndPositionManager, + * columnStartIndex: number, + * columnStopIndex: number, + * isScrolling: boolean, + * rowSizeAndPositionManager: CellSizeAndPositionManager, + * rowStartIndex: number, + * rowStopIndex: number, + * scrollLeft: number, + * scrollTop: number + * }): Array + */ + cellRangeRenderer: _react.PropTypes.func.isRequired, + + /** + * Optional custom CSS class name to attach to root Grid element. + */ + className: _react.PropTypes.string, + + /** + * Number of columns in grid. + */ + columnCount: _react.PropTypes.number.isRequired, + + /** + * Either a fixed column width (number) or a function that returns the width of a column given its index. + * Should implement the following interface: (index: number): number + */ + columnWidth: _react.PropTypes.oneOfType([_react.PropTypes.number, _react.PropTypes.func]).isRequired, + + /** + * Used to estimate the total width of a Grid before all of its columns have actually been measured. + * The estimated total width is adjusted as columns are rendered. + */ + estimatedColumnSize: _react.PropTypes.number.isRequired, + + /** + * Used to estimate the total height of a Grid before all of its rows have actually been measured. + * The estimated total height is adjusted as rows are rendered. + */ + estimatedRowSize: _react.PropTypes.number.isRequired, + + /** + * Height of Grid; this property determines the number of visible (vs virtualized) rows. + */ + height: _react.PropTypes.number.isRequired, + + /** + * Optional renderer to be used in place of rows when either :rowCount or :columnCount is 0. + */ + noContentRenderer: _react.PropTypes.func.isRequired, + + /** + * Callback invoked whenever the scroll offset changes within the inner scrollable region. + * This callback can be used to sync scrolling between lists, tables, or grids. + * ({ clientHeight, clientWidth, scrollHeight, scrollLeft, scrollTop, scrollWidth }): void + */ + onScroll: _react.PropTypes.func.isRequired, + + /** + * Callback invoked with information about the section of the Grid that was just rendered. + * ({ columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex }): void + */ + onSectionRendered: _react.PropTypes.func.isRequired, + + /** + * Number of columns to render before/after the visible section of the grid. + * These columns can help for smoother scrolling on touch devices or browsers that send scroll events infrequently. + */ + overscanColumnCount: _react.PropTypes.number.isRequired, + + /** + * Number of rows to render above/below the visible section of the grid. + * These rows can help for smoother scrolling on touch devices or browsers that send scroll events infrequently. + */ + overscanRowCount: _react.PropTypes.number.isRequired, - var _props2 = this.props; - var columnsCount = _props2.columnsCount; - var columnWidth = _props2.columnWidth; - var height = _props2.height; - var rowHeight = _props2.rowHeight; - var rowsCount = _props2.rowsCount; - var scrollToColumn = _props2.scrollToColumn; - var scrollToRow = _props2.scrollToRow; - var width = _props2.width; - var _state = this.state; - var scrollLeft = _state.scrollLeft; - var scrollPositionChangeReason = _state.scrollPositionChangeReason; - var scrollTop = _state.scrollTop; + /** + * Either a fixed row height (number) or a function that returns the height of a row given its index. + * Should implement the following interface: ({ index: number }): number + */ + rowHeight: _react.PropTypes.oneOfType([_react.PropTypes.number, _react.PropTypes.func]).isRequired, - // Make sure requested changes to :scrollLeft or :scrollTop get applied. - // Assigning to scrollLeft/scrollTop tells the browser to interrupt any running scroll animations, - // And to discard any pending async changes to the scroll position that may have happened in the meantime (e.g. on a separate scrolling thread). - // So we only set these when we require an adjustment of the scroll position. - // See issue #2 for more information. + /** + * Number of rows in grid. + */ + rowCount: _react.PropTypes.number.isRequired, - if (scrollPositionChangeReason === SCROLL_POSITION_CHANGE_REASONS.REQUESTED) { - if (scrollLeft >= 0 && scrollLeft !== prevState.scrollLeft && scrollLeft !== this.refs.scrollingContainer.scrollLeft) { - this.refs.scrollingContainer.scrollLeft = scrollLeft; - } - if (scrollTop >= 0 && scrollTop !== prevState.scrollTop && scrollTop !== this.refs.scrollingContainer.scrollTop) { - this.refs.scrollingContainer.scrollTop = scrollTop; - } - } + /** Wait this amount of time after the last scroll event before resetting Grid `pointer-events`. */ + scrollingResetTimeInterval: _react.PropTypes.number, - // Update scroll offsets if the current :scrollToColumn or :scrollToRow values requires it - // @TODO Do we also need this check or can the one in componentWillUpdate() suffice? - (0, _updateScrollIndexHelper2.default)({ - cellCount: columnsCount, - cellMetadata: this._columnMetadata, - cellSize: columnWidth, - previousCellsCount: prevProps.columnsCount, - previousCellSize: prevProps.columnWidth, - previousScrollToIndex: prevProps.scrollToColumn, - previousSize: prevProps.width, - scrollOffset: scrollLeft, - scrollToIndex: scrollToColumn, - size: width, - updateScrollIndexCallback: function updateScrollIndexCallback(scrollToColumn) { - return _this2._updateScrollLeftForScrollToColumn(_extends({}, _this2.props, { scrollToColumn: scrollToColumn })); - } - }); - (0, _updateScrollIndexHelper2.default)({ - cellCount: rowsCount, - cellMetadata: this._rowMetadata, - cellSize: rowHeight, - previousCellsCount: prevProps.rowsCount, - previousCellSize: prevProps.rowHeight, - previousScrollToIndex: prevProps.scrollToRow, - previousSize: prevProps.height, - scrollOffset: scrollTop, - scrollToIndex: scrollToRow, - size: height, - updateScrollIndexCallback: function updateScrollIndexCallback(scrollToRow) { - return _this2._updateScrollTopForScrollToRow(_extends({}, _this2.props, { scrollToRow: scrollToRow })); - } - }); + /** Horizontal offset. */ + scrollLeft: _react.PropTypes.number, - // Update onRowsRendered callback if start/stop indices have changed - this._invokeOnGridRenderedHelper(); - } - }, { - key: 'componentWillMount', - value: function componentWillMount() { - this._computeColumnMetadata(this.props); - this._computeRowMetadata(this.props); - } - }, { - key: 'componentWillUnmount', - value: function componentWillUnmount() { - if (this._disablePointerEventsTimeoutId) { - clearTimeout(this._disablePointerEventsTimeoutId); - } + /** + * Controls scroll-to-cell behavior of the Grid. + * The default ("auto") scrolls the least amount possible to ensure that the specified cell is fully visible. + * Use "start" to align cells to the top/left of the Grid and "end" to align bottom/right. + */ + scrollToAlignment: _react.PropTypes.oneOf(['auto', 'end', 'start', 'center']).isRequired, - if (this._setNextStateAnimationFrameId) { - _raf2.default.cancel(this._setNextStateAnimationFrameId); - } - } + /** + * Column index to ensure visible (by forcefully scrolling if necessary) + */ + scrollToColumn: _react.PropTypes.number, - /** - * @private - * This method updates scrollLeft/scrollTop in state for the following conditions: - * 1) Empty content (0 rows or columns) - * 2) New scroll props overriding the current state - * 3) Cells-count or cells-size has changed, making previous scroll offsets invalid - */ + /** Vertical offset. */ + scrollTop: _react.PropTypes.number, - }, { - key: 'componentWillUpdate', - value: function componentWillUpdate(nextProps, nextState) { - var _this3 = this; + /** + * Row index to ensure visible (by forcefully scrolling if necessary) + */ + scrollToRow: _react.PropTypes.number, - if (nextProps.columnsCount === 0 && nextState.scrollLeft !== 0 || nextProps.rowsCount === 0 && nextState.scrollTop !== 0) { - this._setScrollPosition({ - scrollLeft: 0, - scrollTop: 0 - }); - } else if (nextProps.scrollLeft !== this.props.scrollLeft || nextProps.scrollTop !== this.props.scrollTop) { - this._setScrollPosition({ - scrollLeft: nextProps.scrollLeft, - scrollTop: nextProps.scrollTop - }); - } + /** Optional inline style */ + style: _react.PropTypes.object, - // Update scroll offsets if the size or number of cells have changed, invalidating the previous value - (0, _calculateSizeAndPositionDataAndUpdateScrollOffset2.default)({ - cellCount: this.props.columnsCount, - cellSize: this.props.columnWidth, - computeMetadataCallback: this._computeColumnMetadata, - computeMetadataCallbackProps: nextProps, - computeMetadataOnNextUpdate: nextState.computeGridMetadataOnNextUpdate, - nextCellsCount: nextProps.columnsCount, - nextCellSize: nextProps.columnWidth, - nextScrollToIndex: nextProps.scrollToColumn, - scrollToIndex: this.props.scrollToColumn, - updateScrollOffsetForScrollToIndex: function updateScrollOffsetForScrollToIndex() { - return _this3._updateScrollLeftForScrollToColumn(nextProps, nextState); - } - }); - (0, _calculateSizeAndPositionDataAndUpdateScrollOffset2.default)({ - cellCount: this.props.rowsCount, - cellSize: this.props.rowHeight, - computeMetadataCallback: this._computeRowMetadata, - computeMetadataCallbackProps: nextProps, - computeMetadataOnNextUpdate: nextState.computeGridMetadataOnNextUpdate, - nextCellsCount: nextProps.rowsCount, - nextCellSize: nextProps.rowHeight, - nextScrollToIndex: nextProps.scrollToRow, - scrollToIndex: this.props.scrollToRow, - updateScrollOffsetForScrollToIndex: function updateScrollOffsetForScrollToIndex() { - return _this3._updateScrollTopForScrollToRow(nextProps, nextState); - } - }); + /** Tab index for focus */ + tabIndex: _react.PropTypes.number, - this.setState({ - computeGridMetadataOnNextUpdate: false - }); - } - }, { - key: 'render', - value: function render() { - var _props3 = this.props; - var className = _props3.className; - var columnsCount = _props3.columnsCount; - var height = _props3.height; - var noContentRenderer = _props3.noContentRenderer; - var overscanColumnsCount = _props3.overscanColumnsCount; - var overscanRowsCount = _props3.overscanRowsCount; - var renderCell = _props3.renderCell; - var renderCellRanges = _props3.renderCellRanges; - var rowsCount = _props3.rowsCount; - var width = _props3.width; - var _state2 = this.state; - var isScrolling = _state2.isScrolling; - var scrollLeft = _state2.scrollLeft; - var scrollTop = _state2.scrollTop; + /** + * Width of Grid; this property determines the number of visible (vs virtualized) columns. + */ + width: _react.PropTypes.number.isRequired +}; +Grid.defaultProps = { + 'aria-label': 'grid', + cellStyle: {}, + cellRangeRenderer: _defaultCellRangeRenderer2.default, + estimatedColumnSize: 100, + estimatedRowSize: 30, + noContentRenderer: function noContentRenderer() { + return null; + }, + onScroll: function onScroll() { + return null; + }, + onSectionRendered: function onSectionRendered() { + return null; + }, + overscanColumnCount: 0, + overscanRowCount: 10, + scrollingResetTimeInterval: DEFAULT_SCROLLING_RESET_TIME_INTERVAL, + scrollToAlignment: 'auto', + style: {}, + tabIndex: 0 +}; +exports.default = Grid; +},{"../utils/createCallbackMemoizer":84,"./defaultCellRangeRenderer":67,"./utils/ScalingCellSizeAndPositionManager":70,"./utils/calculateSizeAndPositionDataAndUpdateScrollOffset":71,"./utils/getOverscanIndices":72,"./utils/updateScrollIndexHelper":73,"classnames":undefined,"dom-helpers/util/scrollbarSize":88,"raf":89,"react":undefined,"react-addons-shallow-compare":18}],67:[function(require,module,exports){ +'use strict'; +Object.defineProperty(exports, "__esModule", { + value: true +}); - var childrenToDisplay = []; +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; - // Render only enough columns and rows to cover the visible area of the grid. - if (height > 0 && width > 0) { - var visibleColumnIndices = (0, _getVisibleCellIndices2.default)({ - cellMetadata: this._columnMetadata, - containerSize: width, - currentOffset: scrollLeft - }); +exports.default = defaultCellRangeRenderer; - var visibleRowIndices = (0, _getVisibleCellIndices2.default)({ - cellMetadata: this._rowMetadata, - containerSize: height, - currentOffset: scrollTop - }); +var _react = require('react'); - // Store for _invokeOnGridRenderedHelper() - this._renderedColumnStartIndex = visibleColumnIndices.start; - this._renderedColumnStopIndex = visibleColumnIndices.stop; - this._renderedRowStartIndex = visibleRowIndices.start; - this._renderedRowStopIndex = visibleRowIndices.stop; +var _react2 = _interopRequireDefault(_react); - var overscanColumnIndices = (0, _getOverscanIndices2.default)({ - cellCount: columnsCount, - overscanCellsCount: overscanColumnsCount, - startIndex: this._renderedColumnStartIndex, - stopIndex: this._renderedColumnStopIndex - }); +var _classnames = require('classnames'); - var overscanRowIndices = (0, _getOverscanIndices2.default)({ - cellCount: rowsCount, - overscanCellsCount: overscanRowsCount, - startIndex: this._renderedRowStartIndex, - stopIndex: this._renderedRowStopIndex - }); +var _classnames2 = _interopRequireDefault(_classnames); - // Store for _invokeOnGridRenderedHelper() - this._columnStartIndex = overscanColumnIndices.overscanStartIndex; - this._columnStopIndex = overscanColumnIndices.overscanStopIndex; - this._rowStartIndex = overscanRowIndices.overscanStartIndex; - this._rowStopIndex = overscanRowIndices.overscanStopIndex; +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - childrenToDisplay = renderCellRanges({ - columnMetadata: this._columnMetadata, - columnStartIndex: this._columnStartIndex, - columnStopIndex: this._columnStopIndex, - renderCell: renderCell, - rowMetadata: this._rowMetadata, - rowStartIndex: this._rowStartIndex, - rowStopIndex: this._rowStopIndex - }); - } +/** + * Default implementation of cellRangeRenderer used by Grid. + * This renderer supports cell-caching while the user is scrolling. + */ +function defaultCellRangeRenderer(_ref) { + var cellCache = _ref.cellCache; + var cellClassName = _ref.cellClassName; + var cellRenderer = _ref.cellRenderer; + var cellStyle = _ref.cellStyle; + var columnSizeAndPositionManager = _ref.columnSizeAndPositionManager; + var columnStartIndex = _ref.columnStartIndex; + var columnStopIndex = _ref.columnStopIndex; + var horizontalOffsetAdjustment = _ref.horizontalOffsetAdjustment; + var isScrolling = _ref.isScrolling; + var rowSizeAndPositionManager = _ref.rowSizeAndPositionManager; + var rowStartIndex = _ref.rowStartIndex; + var rowStopIndex = _ref.rowStopIndex; + var scrollLeft = _ref.scrollLeft; + var scrollTop = _ref.scrollTop; + var verticalOffsetAdjustment = _ref.verticalOffsetAdjustment; - var gridStyle = { - height: height, - width: width - }; + var renderedCells = []; - var totalColumnsWidth = this._getTotalColumnsWidth(); - var totalRowsHeight = this._getTotalRowsHeight(); + for (var rowIndex = rowStartIndex; rowIndex <= rowStopIndex; rowIndex++) { + var rowDatum = rowSizeAndPositionManager.getSizeAndPositionOfCell(rowIndex); - // Force browser to hide scrollbars when we know they aren't necessary. - // Otherwise once scrollbars appear they may not disappear again. - // For more info see issue #116 - if (totalColumnsWidth <= width) { - gridStyle.overflowX = 'hidden'; + for (var columnIndex = columnStartIndex; columnIndex <= columnStopIndex; columnIndex++) { + var columnDatum = columnSizeAndPositionManager.getSizeAndPositionOfCell(columnIndex); + var key = rowIndex + '-' + columnIndex; + var cellStyleObject = cellStyle({ rowIndex: rowIndex, columnIndex: columnIndex }); + var renderedCell = void 0; + + // Avoid re-creating cells while scrolling. + // This can lead to the same cell being created many times and can cause performance issues for "heavy" cells. + // If a scroll is in progress- cache and reuse cells. + // This cache will be thrown away once scrolling complets. + if (isScrolling) { + if (!cellCache[key]) { + cellCache[key] = cellRenderer({ + columnIndex: columnIndex, + isScrolling: isScrolling, + rowIndex: rowIndex + }); + } + renderedCell = cellCache[key]; + // If the user is no longer scrolling, don't cache cells. + // This makes dynamic cell content difficult for users and would also lead to a heavier memory footprint. + } else { + renderedCell = cellRenderer({ + columnIndex: columnIndex, + isScrolling: isScrolling, + rowIndex: rowIndex + }); } - if (totalRowsHeight <= height) { - gridStyle.overflowY = 'hidden'; + if (renderedCell == null || renderedCell === false) { + continue; } - return _react2.default.createElement( + var className = cellClassName({ columnIndex: columnIndex, rowIndex: rowIndex }); + + var child = _react2.default.createElement( 'div', { - ref: 'scrollingContainer', - 'aria-label': this.props['aria-label'], - className: (0, _classnames2.default)('Grid', className), - onScroll: this._onScroll, - role: 'grid', - style: gridStyle, - tabIndex: 0 + key: key, + className: (0, _classnames2.default)('Grid__cell', className), + style: _extends({ + height: rowDatum.size, + left: columnDatum.offset + horizontalOffsetAdjustment, + top: rowDatum.offset + verticalOffsetAdjustment, + width: columnDatum.size + }, cellStyleObject) }, - childrenToDisplay.length > 0 && _react2.default.createElement( - 'div', - { - className: 'Grid__innerScrollContainer', - style: { - width: totalColumnsWidth, - height: totalRowsHeight, - maxWidth: totalColumnsWidth, - maxHeight: totalRowsHeight, - pointerEvents: isScrolling ? 'none' : 'auto' - } - }, - childrenToDisplay - ), - childrenToDisplay.length === 0 && noContentRenderer() + renderedCell ); + + renderedCells.push(child); } - }, { - key: 'shouldComponentUpdate', - value: function shouldComponentUpdate(nextProps, nextState) { - return (0, _reactAddonsShallowCompare2.default)(this, nextProps, nextState); - } + } + + return renderedCells; +} +},{"classnames":undefined,"react":undefined}],68:[function(require,module,exports){ +'use strict'; - /* ---------------------------- Helper methods ---------------------------- */ +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.defaultCellRangeRenderer = exports.Grid = exports.default = undefined; - }, { - key: '_computeColumnMetadata', - value: function _computeColumnMetadata(props) { - var columnsCount = props.columnsCount; - var columnWidth = props.columnWidth; +var _Grid2 = require('./Grid'); +var _Grid3 = _interopRequireDefault(_Grid2); - this._columnMetadata = (0, _initCellMetadata2.default)({ - cellCount: columnsCount, - size: columnWidth - }); - } - }, { - key: '_computeRowMetadata', - value: function _computeRowMetadata(props) { - var rowHeight = props.rowHeight; - var rowsCount = props.rowsCount; +var _defaultCellRangeRenderer2 = require('./defaultCellRangeRenderer'); +var _defaultCellRangeRenderer3 = _interopRequireDefault(_defaultCellRangeRenderer2); - this._rowMetadata = (0, _initCellMetadata2.default)({ - cellCount: rowsCount, - size: rowHeight - }); - } +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - /** - * Sets an :isScrolling flag for a small window of time. - * This flag is used to disable pointer events on the scrollable portion of the Grid. - * This prevents jerky/stuttery mouse-wheel scrolling. - */ +exports.default = _Grid3.default; +exports.Grid = _Grid3.default; +exports.defaultCellRangeRenderer = _defaultCellRangeRenderer3.default; +},{"./Grid":66,"./defaultCellRangeRenderer":67}],69:[function(require,module,exports){ +'use strict'; - }, { - key: '_enablePointerEventsAfterDelay', - value: function _enablePointerEventsAfterDelay() { - var _this4 = this; +Object.defineProperty(exports, "__esModule", { + value: true +}); - if (this._disablePointerEventsTimeoutId) { - clearTimeout(this._disablePointerEventsTimeoutId); - } +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - this._disablePointerEventsTimeoutId = setTimeout(function () { - _this4._disablePointerEventsTimeoutId = null; - _this4.setState({ - isScrolling: false - }); - }, IS_SCROLLING_TIMEOUT); +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +/** + * Just-in-time calculates and caches size and position information for a collection of cells. + */ +var CellSizeAndPositionManager = function () { + function CellSizeAndPositionManager(_ref) { + var cellCount = _ref.cellCount; + var cellSizeGetter = _ref.cellSizeGetter; + var estimatedCellSize = _ref.estimatedCellSize; + + _classCallCheck(this, CellSizeAndPositionManager); + + this._cellSizeGetter = cellSizeGetter; + this._cellCount = cellCount; + this._estimatedCellSize = estimatedCellSize; + + // Cache of size and position data for cells, mapped by cell index. + // Note that invalid values may exist in this map so only rely on cells up to this._lastMeasuredIndex + this._cellSizeAndPositionData = {}; + + // Measurements for cells up to this index can be trusted; cells afterward should be estimated. + this._lastMeasuredIndex = -1; + } + + _createClass(CellSizeAndPositionManager, [{ + key: 'configure', + value: function configure(_ref2) { + var cellCount = _ref2.cellCount; + var estimatedCellSize = _ref2.estimatedCellSize; + + this._cellCount = cellCount; + this._estimatedCellSize = estimatedCellSize; } }, { - key: '_getTotalColumnsWidth', - value: function _getTotalColumnsWidth() { - if (this._columnMetadata.length === 0) { - return 0; - } - - var datum = this._columnMetadata[this._columnMetadata.length - 1]; - return datum.offset + datum.size; + key: 'getCellCount', + value: function getCellCount() { + return this._cellCount; } }, { - key: '_getTotalRowsHeight', - value: function _getTotalRowsHeight() { - if (this._rowMetadata.length === 0) { - return 0; - } - - var datum = this._rowMetadata[this._rowMetadata.length - 1]; - return datum.offset + datum.size; + key: 'getEstimatedCellSize', + value: function getEstimatedCellSize() { + return this._estimatedCellSize; } }, { - key: '_invokeOnGridRenderedHelper', - value: function _invokeOnGridRenderedHelper() { - var onSectionRendered = this.props.onSectionRendered; + key: 'getLastMeasuredIndex', + value: function getLastMeasuredIndex() { + return this._lastMeasuredIndex; + } + /** + * This method returns the size and position for the cell at the specified index. + * It just-in-time calculates (or used cached values) for cells leading up to the index. + */ - this._onGridRenderedMemoizer({ - callback: onSectionRendered, - indices: { - columnOverscanStartIndex: this._columnStartIndex, - columnOverscanStopIndex: this._columnStopIndex, - columnStartIndex: this._renderedColumnStartIndex, - columnStopIndex: this._renderedColumnStopIndex, - rowOverscanStartIndex: this._rowStartIndex, - rowOverscanStopIndex: this._rowStopIndex, - rowStartIndex: this._renderedRowStartIndex, - rowStopIndex: this._renderedRowStopIndex - } - }); - } }, { - key: '_invokeOnScrollMemoizer', - value: function _invokeOnScrollMemoizer(_ref) { - var _this5 = this; + key: 'getSizeAndPositionOfCell', + value: function getSizeAndPositionOfCell(index) { + if (index < 0 || index >= this._cellCount) { + throw Error('Requested index ' + index + ' is outside of range 0..' + this._cellCount); + } - var scrollLeft = _ref.scrollLeft; - var scrollTop = _ref.scrollTop; - var totalColumnsWidth = _ref.totalColumnsWidth; - var totalRowsHeight = _ref.totalRowsHeight; + if (index > this._lastMeasuredIndex) { + var lastMeasuredCellSizeAndPosition = this.getSizeAndPositionOfLastMeasuredCell(); + var _offset = lastMeasuredCellSizeAndPosition.offset + lastMeasuredCellSizeAndPosition.size; - this._onScrollMemoizer({ - callback: function callback(_ref2) { - var scrollLeft = _ref2.scrollLeft; - var scrollTop = _ref2.scrollTop; - var _props4 = _this5.props; - var height = _props4.height; - var onScroll = _props4.onScroll; - var width = _props4.width; + for (var i = this._lastMeasuredIndex + 1; i <= index; i++) { + var _size = this._cellSizeGetter({ index: i }); + if (_size == null || isNaN(_size)) { + throw Error('Invalid size returned for cell ' + i + ' of value ' + _size); + } - onScroll({ - clientHeight: height, - clientWidth: width, - scrollHeight: totalRowsHeight, - scrollLeft: scrollLeft, - scrollTop: scrollTop, - scrollWidth: totalColumnsWidth - }); - }, - indices: { - scrollLeft: scrollLeft, - scrollTop: scrollTop + this._cellSizeAndPositionData[i] = { + offset: _offset, + size: _size + }; + + _offset += _size; } - }); + + this._lastMeasuredIndex = index; + } + + return this._cellSizeAndPositionData[index]; + } + }, { + key: 'getSizeAndPositionOfLastMeasuredCell', + value: function getSizeAndPositionOfLastMeasuredCell() { + return this._lastMeasuredIndex >= 0 ? this._cellSizeAndPositionData[this._lastMeasuredIndex] : { + offset: 0, + size: 0 + }; } /** - * Updates the state during the next animation frame. - * Use this method to avoid multiple renders in a small span of time. - * This helps performance for bursty events (like onScroll). + * Total size of all cells being measured. + * This value will be completedly estimated initially. + * As cells as measured the estimate will be updated. */ }, { - key: '_setNextState', - value: function _setNextState(state) { - var _this6 = this; - - if (this._setNextStateAnimationFrameId) { - _raf2.default.cancel(this._setNextStateAnimationFrameId); - } + key: 'getTotalSize', + value: function getTotalSize() { + var lastMeasuredCellSizeAndPosition = this.getSizeAndPositionOfLastMeasuredCell(); - this._setNextStateAnimationFrameId = (0, _raf2.default)(function () { - _this6._setNextStateAnimationFrameId = null; - _this6.setState(state); - }); + return lastMeasuredCellSizeAndPosition.offset + lastMeasuredCellSizeAndPosition.size + (this._cellCount - this._lastMeasuredIndex - 1) * this._estimatedCellSize; } - }, { - key: '_setScrollPosition', - value: function _setScrollPosition(_ref3) { - var scrollLeft = _ref3.scrollLeft; - var scrollTop = _ref3.scrollTop; - var newState = { - scrollPositionChangeReason: SCROLL_POSITION_CHANGE_REASONS.REQUESTED - }; + /** + * Determines a new offset that ensures a certain cell is visible, given the current offset. + * If the cell is already visible then the current offset will be returned. + * If the current offset is too great or small, it will be adjusted just enough to ensure the specified index is visible. + * + * @param align Desired alignment within container; one of "auto" (default), "start", or "end" + * @param containerSize Size (width or height) of the container viewport + * @param currentOffset Container's current (x or y) offset + * @param totalSize Total size (width or height) of all cells + * @return Offset to use to ensure the specified cell is visible + */ - if (scrollLeft >= 0) { - newState.scrollLeft = scrollLeft; + }, { + key: 'getUpdatedOffsetForIndex', + value: function getUpdatedOffsetForIndex(_ref3) { + var _ref3$align = _ref3.align; + var align = _ref3$align === undefined ? 'auto' : _ref3$align; + var containerSize = _ref3.containerSize; + var currentOffset = _ref3.currentOffset; + var targetIndex = _ref3.targetIndex; + + var datum = this.getSizeAndPositionOfCell(targetIndex); + var maxOffset = datum.offset; + var minOffset = maxOffset - containerSize + datum.size; + + var idealOffset = void 0; + + switch (align) { + case 'start': + idealOffset = maxOffset; + break; + case 'end': + idealOffset = minOffset; + break; + case 'center': + idealOffset = maxOffset - (containerSize - datum.size) / 2; + break; + default: + idealOffset = Math.max(minOffset, Math.min(maxOffset, currentOffset)); + break; } - if (scrollTop >= 0) { - newState.scrollTop = scrollTop; - } + var totalSize = this.getTotalSize(); - if (scrollLeft >= 0 && scrollLeft !== this.state.scrollLeft || scrollTop >= 0 && scrollTop !== this.state.scrollTop) { - this.setState(newState); - } + return Math.max(0, Math.min(totalSize - containerSize, idealOffset)); } }, { - key: '_updateScrollLeftForScrollToColumn', - value: function _updateScrollLeftForScrollToColumn() { - var props = arguments.length <= 0 || arguments[0] === undefined ? null : arguments[0]; - var state = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1]; + key: 'getVisibleCellRange', + value: function getVisibleCellRange(_ref4) { + var containerSize = _ref4.containerSize; + var offset = _ref4.offset; - var _ref4 = props || this.props; + var totalSize = this.getTotalSize(); - var columnsCount = _ref4.columnsCount; - var scrollToColumn = _ref4.scrollToColumn; - var width = _ref4.width; + if (totalSize === 0) { + return {}; + } - var _ref5 = state || this.state; + var maxOffset = offset + containerSize; + var start = this._findNearestCell(offset); - var scrollLeft = _ref5.scrollLeft; + var datum = this.getSizeAndPositionOfCell(start); + offset = datum.offset + datum.size; + var stop = start; - if (scrollToColumn >= 0 && columnsCount > 0) { - var targetIndex = (0, _getNearestIndex2.default)({ - cellCount: this._columnMetadata.length, - targetIndex: scrollToColumn - }); + while (offset < maxOffset && stop < this._cellCount - 1) { + stop++; - var columnMetadata = this._columnMetadata[targetIndex]; + offset += this.getSizeAndPositionOfCell(stop).size; + } - var calculatedScrollLeft = (0, _getUpdatedOffsetForIndex2.default)({ - cellOffset: columnMetadata.offset, - cellSize: columnMetadata.size, - containerSize: width, - currentOffset: scrollLeft, - targetIndex: scrollToColumn - }); + return { + start: start, + stop: stop + }; + } - if (scrollLeft !== calculatedScrollLeft) { - this._setScrollPosition({ - scrollLeft: calculatedScrollLeft - }); + /** + * Clear all cached values for cells after the specified index. + * This method should be called for any cell that has changed its size. + * It will not immediately perform any calculations; they'll be performed the next time getSizeAndPositionOfCell() is called. + */ + + }, { + key: 'resetCell', + value: function resetCell(index) { + this._lastMeasuredIndex = Math.min(this._lastMeasuredIndex, index - 1); + } + }, { + key: '_binarySearch', + value: function _binarySearch(_ref5) { + var high = _ref5.high; + var low = _ref5.low; + var offset = _ref5.offset; + + var middle = void 0; + var currentOffset = void 0; + + while (low <= high) { + middle = low + Math.floor((high - low) / 2); + currentOffset = this.getSizeAndPositionOfCell(middle).offset; + + if (currentOffset === offset) { + return middle; + } else if (currentOffset < offset) { + low = middle + 1; + } else if (currentOffset > offset) { + high = middle - 1; } } + + if (low > 0) { + return low - 1; + } } }, { - key: '_updateScrollTopForScrollToRow', - value: function _updateScrollTopForScrollToRow() { - var props = arguments.length <= 0 || arguments[0] === undefined ? null : arguments[0]; - var state = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1]; + key: '_exponentialSearch', + value: function _exponentialSearch(_ref6) { + var index = _ref6.index; + var offset = _ref6.offset; - var _ref6 = props || this.props; + var interval = 1; - var height = _ref6.height; - var rowsCount = _ref6.rowsCount; - var scrollToRow = _ref6.scrollToRow; + while (index < this._cellCount && this.getSizeAndPositionOfCell(index).offset < offset) { + index += interval; + interval *= 2; + } - var _ref7 = state || this.state; + return this._binarySearch({ + high: Math.min(index, this._cellCount - 1), + low: Math.floor(index / 2), + offset: offset + }); + } - var scrollTop = _ref7.scrollTop; + /** + * Searches for the cell (index) nearest the specified offset. + * + * If no exact match is found the next lowest cell index will be returned. + * This allows partially visible cells (with offsets just before/above the fold) to be visible. + */ + }, { + key: '_findNearestCell', + value: function _findNearestCell(offset) { + if (isNaN(offset)) { + throw Error('Invalid offset ' + offset + ' specified'); + } - if (scrollToRow >= 0 && rowsCount > 0) { - var targetIndex = (0, _getNearestIndex2.default)({ - cellCount: this._rowMetadata.length, - targetIndex: scrollToRow - }); + // Our search algorithms find the nearest match at or below the specified offset. + // So make sure the offset is at least 0 or no match will be found. + offset = Math.max(0, offset); - var rowMetadata = this._rowMetadata[targetIndex]; + var lastMeasuredCellSizeAndPosition = this.getSizeAndPositionOfLastMeasuredCell(); + var lastMeasuredIndex = Math.max(0, this._lastMeasuredIndex); - var calculatedScrollTop = (0, _getUpdatedOffsetForIndex2.default)({ - cellOffset: rowMetadata.offset, - cellSize: rowMetadata.size, - containerSize: height, - currentOffset: scrollTop, - targetIndex: scrollToRow + if (lastMeasuredCellSizeAndPosition.offset >= offset) { + // If we've already measured cells within this range just use a binary search as it's faster. + return this._binarySearch({ + high: lastMeasuredIndex, + low: 0, + offset: offset + }); + } else { + // If we haven't yet measured this high, fallback to an exponential search with an inner binary search. + // The exponential search avoids pre-computing sizes for the full set of cells as a binary search would. + // The overall complexity for this approach is O(log n). + return this._exponentialSearch({ + index: lastMeasuredIndex, + offset: offset }); - - if (scrollTop !== calculatedScrollTop) { - this._setScrollPosition({ - scrollTop: calculatedScrollTop - }); - } } } - }, { - key: '_onScroll', - value: function _onScroll(event) { - // In certain edge-cases React dispatches an onScroll event with an invalid target.scrollLeft / target.scrollTop. - // This invalid event can be detected by comparing event.target to this component's scrollable DOM element. - // See issue #404 for more information. - if (event.target !== this.refs.scrollingContainer) { - return; - } - - // Prevent pointer events from interrupting a smooth scroll - this._enablePointerEventsAfterDelay(); - - // When this component is shrunk drastically, React dispatches a series of back-to-back scroll events, - // Gradually converging on a scrollTop that is within the bounds of the new, smaller height. - // This causes a series of rapid renders that is slow for long lists. - // We can avoid that by doing some simple bounds checking to ensure that scrollTop never exceeds the total height. - var _props5 = this.props; - var height = _props5.height; - var width = _props5.width; - - var scrollbarSize = this._scrollbarSize; - var totalRowsHeight = this._getTotalRowsHeight(); - var totalColumnsWidth = this._getTotalColumnsWidth(); - var scrollLeft = Math.min(totalColumnsWidth - width + scrollbarSize, event.target.scrollLeft); - var scrollTop = Math.min(totalRowsHeight - height + scrollbarSize, event.target.scrollTop); + }]); - // Certain devices (like Apple touchpad) rapid-fire duplicate events. - // Don't force a re-render if this is the case. - // The mouse may move faster then the animation frame does. - // Use requestAnimationFrame to avoid over-updating. - if (this.state.scrollLeft !== scrollLeft || this.state.scrollTop !== scrollTop) { - // Browsers with cancelable scroll events (eg. Firefox) interrupt scrolling animations if scrollTop/scrollLeft is set. - // Other browsers (eg. Safari) don't scroll as well without the help under certain conditions (DOM or style changes during scrolling). - // All things considered, this seems to be the best current work around that I'm aware of. - // For more information see https://github.com/bvaughn/react-virtualized/pull/124 - var scrollPositionChangeReason = event.cancelable ? SCROLL_POSITION_CHANGE_REASONS.OBSERVED : SCROLL_POSITION_CHANGE_REASONS.REQUESTED; + return CellSizeAndPositionManager; +}(); - if (!this.state.isScrolling) { - this.setState({ - isScrolling: true - }); - } +exports.default = CellSizeAndPositionManager; +},{}],70:[function(require,module,exports){ +'use strict'; - this._setNextState({ - isScrolling: true, - scrollLeft: scrollLeft, - scrollPositionChangeReason: scrollPositionChangeReason, - scrollTop: scrollTop - }); - } +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DEFAULT_MAX_SCROLL_SIZE = undefined; - this._invokeOnScrollMemoizer({ scrollLeft: scrollLeft, scrollTop: scrollTop, totalColumnsWidth: totalColumnsWidth, totalRowsHeight: totalRowsHeight }); - } - }]); +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - return Grid; -}(_react.Component); +var _CellSizeAndPositionManager = require('./CellSizeAndPositionManager'); -Grid.propTypes = { - 'aria-label': _react.PropTypes.string, +var _CellSizeAndPositionManager2 = _interopRequireDefault(_CellSizeAndPositionManager); - /** - * Optional custom CSS class name to attach to root Grid element. - */ - className: _react.PropTypes.string, +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - /** - * Number of columns in grid. - */ - columnsCount: _react.PropTypes.number.isRequired, +function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } - /** - * Either a fixed column width (number) or a function that returns the width of a column given its index. - * Should implement the following interface: (index: number): number - */ - columnWidth: _react.PropTypes.oneOfType([_react.PropTypes.number, _react.PropTypes.func]).isRequired, +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - /** - * Height of Grid; this property determines the number of visible (vs virtualized) rows. - */ - height: _react.PropTypes.number.isRequired, +/** + * Browsers have scroll offset limitations (eg Chrome stops scrolling at ~33.5M pixels where as Edge tops out at ~1.5M pixels). + * After a certain position, the browser won't allow the user to scroll further (even via JavaScript scroll offset adjustments). + * This util picks a lower ceiling for max size and artificially adjusts positions within to make it transparent for users. + */ +var DEFAULT_MAX_SCROLL_SIZE = exports.DEFAULT_MAX_SCROLL_SIZE = 1500000; - /** - * Optional renderer to be used in place of rows when either :rowsCount or :columnsCount is 0. - */ - noContentRenderer: _react.PropTypes.func.isRequired, +/** + * Extends CellSizeAndPositionManager and adds scaling behavior for lists that are too large to fit within a browser's native limits. + */ - /** - * Callback invoked whenever the scroll offset changes within the inner scrollable region. - * This callback can be used to sync scrolling between lists, tables, or grids. - * ({ clientHeight, clientWidth, scrollHeight, scrollLeft, scrollTop, scrollWidth }): void - */ - onScroll: _react.PropTypes.func.isRequired, +var ScalingCellSizeAndPositionManager = function () { + function ScalingCellSizeAndPositionManager(_ref) { + var _ref$maxScrollSize = _ref.maxScrollSize; + var maxScrollSize = _ref$maxScrollSize === undefined ? DEFAULT_MAX_SCROLL_SIZE : _ref$maxScrollSize; - /** - * Callback invoked with information about the section of the Grid that was just rendered. - * ({ columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex }): void - */ - onSectionRendered: _react.PropTypes.func.isRequired, + var params = _objectWithoutProperties(_ref, ['maxScrollSize']); - /** - * Number of columns to render before/after the visible section of the grid. - * These columns can help for smoother scrolling on touch devices or browsers that send scroll events infrequently. - */ - overscanColumnsCount: _react.PropTypes.number.isRequired, + _classCallCheck(this, ScalingCellSizeAndPositionManager); - /** - * Number of rows to render above/below the visible section of the grid. - * These rows can help for smoother scrolling on touch devices or browsers that send scroll events infrequently. - */ - overscanRowsCount: _react.PropTypes.number.isRequired, + // Favor composition over inheritance to simplify IE10 support + this._cellSizeAndPositionManager = new _CellSizeAndPositionManager2.default(params); + this._maxScrollSize = maxScrollSize; + } - /** - * Responsible for rendering a cell given an row and column index. - * Should implement the following interface: ({ columnIndex: number, rowIndex: number }): PropTypes.node - */ - renderCell: _react.PropTypes.func.isRequired, + _createClass(ScalingCellSizeAndPositionManager, [{ + key: 'configure', + value: function configure(params) { + this._cellSizeAndPositionManager.configure(params); + } + }, { + key: 'getCellCount', + value: function getCellCount() { + return this._cellSizeAndPositionManager.getCellCount(); + } + }, { + key: 'getEstimatedCellSize', + value: function getEstimatedCellSize() { + return this._cellSizeAndPositionManager.getEstimatedCellSize(); + } + }, { + key: 'getLastMeasuredIndex', + value: function getLastMeasuredIndex() { + return this._cellSizeAndPositionManager.getLastMeasuredIndex(); + } - /** - * Responsible for rendering a group of cells given their index ranges. - * Should implement the following interface: ({ - * columnMetadata:Array, - * columnStartIndex: number, - * columnStopIndex: number, - * renderCell: Function, - * rowMetadata:Array, - * rowStartIndex: number, - * rowStopIndex: number - * }): Array - */ - renderCellRanges: _react.PropTypes.func.isRequired, + /** + * Number of pixels a cell at the given position (offset) should be shifted in order to fit within the scaled container. + * The offset passed to this function is scalled (safe) as well. + */ - /** - * Either a fixed row height (number) or a function that returns the height of a row given its index. - * Should implement the following interface: (index: number): number - */ - rowHeight: _react.PropTypes.oneOfType([_react.PropTypes.number, _react.PropTypes.func]).isRequired, + }, { + key: 'getOffsetAdjustment', + value: function getOffsetAdjustment(_ref2) { + var containerSize = _ref2.containerSize; + var offset = _ref2.offset; + + var totalSize = this._cellSizeAndPositionManager.getTotalSize(); + var safeTotalSize = this.getTotalSize(); + var offsetPercentage = this._getOffsetPercentage({ + containerSize: containerSize, + offset: offset, + totalSize: safeTotalSize + }); - /** - * Number of rows in grid. - */ - rowsCount: _react.PropTypes.number.isRequired, + return Math.round(offsetPercentage * (safeTotalSize - totalSize)); + } + }, { + key: 'getSizeAndPositionOfCell', + value: function getSizeAndPositionOfCell(index) { + return this._cellSizeAndPositionManager.getSizeAndPositionOfCell(index); + } + }, { + key: 'getSizeAndPositionOfLastMeasuredCell', + value: function getSizeAndPositionOfLastMeasuredCell() { + return this._cellSizeAndPositionManager.getSizeAndPositionOfLastMeasuredCell(); + } - /** Horizontal offset. */ - scrollLeft: _react.PropTypes.number, + /** See CellSizeAndPositionManager#getTotalSize */ - /** - * Column index to ensure visible (by forcefully scrolling if necessary) - */ - scrollToColumn: _react.PropTypes.number, + }, { + key: 'getTotalSize', + value: function getTotalSize() { + return Math.min(this._maxScrollSize, this._cellSizeAndPositionManager.getTotalSize()); + } - /** Vertical offset. */ - scrollTop: _react.PropTypes.number, + /** See CellSizeAndPositionManager#getUpdatedOffsetForIndex */ - /** - * Row index to ensure visible (by forcefully scrolling if necessary) - */ - scrollToRow: _react.PropTypes.number, + }, { + key: 'getUpdatedOffsetForIndex', + value: function getUpdatedOffsetForIndex(_ref3) { + var _ref3$align = _ref3.align; + var align = _ref3$align === undefined ? 'auto' : _ref3$align; + var containerSize = _ref3.containerSize; + var currentOffset = _ref3.currentOffset; + var targetIndex = _ref3.targetIndex; + var totalSize = _ref3.totalSize; + + currentOffset = this._safeOffsetToOffset({ + containerSize: containerSize, + offset: currentOffset + }); - /** - * Width of Grid; this property determines the number of visible (vs virtualized) columns. - */ - width: _react.PropTypes.number.isRequired -}; -Grid.defaultProps = { - 'aria-label': 'grid', - noContentRenderer: function noContentRenderer() { - return null; - }, - onScroll: function onScroll() { - return null; - }, - onSectionRendered: function onSectionRendered() { - return null; - }, - overscanColumnsCount: 0, - overscanRowsCount: 10, - renderCellRanges: defaultRenderCellRanges -}; -exports.default = Grid; + var offset = this._cellSizeAndPositionManager.getUpdatedOffsetForIndex({ + align: align, + containerSize: containerSize, + currentOffset: currentOffset, + targetIndex: targetIndex, + totalSize: totalSize + }); + return this._offsetToSafeOffset({ + containerSize: containerSize, + offset: offset + }); + } -function defaultRenderCellRanges(_ref8) { - var columnMetadata = _ref8.columnMetadata; - var columnStartIndex = _ref8.columnStartIndex; - var columnStopIndex = _ref8.columnStopIndex; - var renderCell = _ref8.renderCell; - var rowMetadata = _ref8.rowMetadata; - var rowStartIndex = _ref8.rowStartIndex; - var rowStopIndex = _ref8.rowStopIndex; + /** See CellSizeAndPositionManager#getVisibleCellRange */ - var renderedCells = []; + }, { + key: 'getVisibleCellRange', + value: function getVisibleCellRange(_ref4) { + var containerSize = _ref4.containerSize; + var offset = _ref4.offset; + + offset = this._safeOffsetToOffset({ + containerSize: containerSize, + offset: offset + }); - for (var rowIndex = rowStartIndex; rowIndex <= rowStopIndex; rowIndex++) { - var rowDatum = rowMetadata[rowIndex]; + return this._cellSizeAndPositionManager.getVisibleCellRange({ + containerSize: containerSize, + offset: offset + }); + } + }, { + key: 'resetCell', + value: function resetCell(index) { + this._cellSizeAndPositionManager.resetCell(index); + } + }, { + key: '_getOffsetPercentage', + value: function _getOffsetPercentage(_ref5) { + var containerSize = _ref5.containerSize; + var offset = _ref5.offset; + var totalSize = _ref5.totalSize; - for (var columnIndex = columnStartIndex; columnIndex <= columnStopIndex; columnIndex++) { - var columnDatum = columnMetadata[columnIndex]; - var renderedCell = renderCell({ columnIndex: columnIndex, rowIndex: rowIndex }); - var key = rowIndex + '-' + columnIndex; + return totalSize <= containerSize ? 0 : offset / (totalSize - containerSize); + } + }, { + key: '_offsetToSafeOffset', + value: function _offsetToSafeOffset(_ref6) { + var containerSize = _ref6.containerSize; + var offset = _ref6.offset; - if (renderedCell == null || renderedCell === false) { - continue; - } + var totalSize = this._cellSizeAndPositionManager.getTotalSize(); + var safeTotalSize = this.getTotalSize(); - var child = _react2.default.createElement( - 'div', - { - key: key, - className: 'Grid__cell', - style: { - height: rowDatum.size, - left: columnDatum.offset, - top: rowDatum.offset, - width: columnDatum.size - } - }, - renderedCell - ); + if (totalSize === safeTotalSize) { + return offset; + } else { + var offsetPercentage = this._getOffsetPercentage({ + containerSize: containerSize, + offset: offset, + totalSize: totalSize + }); - renderedCells.push(child); + return Math.round(offsetPercentage * (safeTotalSize - containerSize)); + } } - } - - return renderedCells; -} -},{"../utils/createCallbackMemoizer":68,"../utils/getUpdatedOffsetForIndex":69,"../utils/initCellMetadata":70,"./utils/calculateSizeAndPositionDataAndUpdateScrollOffset":56,"./utils/getNearestIndex":57,"./utils/getOverscanIndices":58,"./utils/getVisibleCellIndices":59,"./utils/updateScrollIndexHelper":60,"classnames":undefined,"dom-helpers/util/scrollbarSize":73,"raf":74,"react":undefined,"react-addons-shallow-compare":18}],55:[function(require,module,exports){ -'use strict'; + }, { + key: '_safeOffsetToOffset', + value: function _safeOffsetToOffset(_ref7) { + var containerSize = _ref7.containerSize; + var offset = _ref7.offset; -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.Grid = exports.default = undefined; + var totalSize = this._cellSizeAndPositionManager.getTotalSize(); + var safeTotalSize = this.getTotalSize(); -var _Grid2 = require('./Grid'); + if (totalSize === safeTotalSize) { + return offset; + } else { + var offsetPercentage = this._getOffsetPercentage({ + containerSize: containerSize, + offset: offset, + totalSize: safeTotalSize + }); -var _Grid3 = _interopRequireDefault(_Grid2); + return Math.round(offsetPercentage * (totalSize - containerSize)); + } + } + }]); -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + return ScalingCellSizeAndPositionManager; +}(); -exports.default = _Grid3.default; -exports.Grid = _Grid3.default; -},{"./Grid":54}],56:[function(require,module,exports){ +exports.default = ScalingCellSizeAndPositionManager; +},{"./CellSizeAndPositionManager":69}],71:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { @@ -7358,7 +9752,6 @@ exports.default = calculateSizeAndPositionDataAndUpdateScrollOffset; * @param cellsSize Width or height of cells for the current axis * @param computeMetadataCallback Method to invoke if cell metadata should be recalculated * @param computeMetadataCallbackProps Parameters to pass to :computeMetadataCallback - * @param computeMetadataOnNextUpdate Flag specifying that metadata should be recalculated * @param nextCellsCount Newly updated number of rows or columns in the current axis * @param nextCellsSize Newly updated width or height of cells for the current axis * @param nextScrollToIndex Newly updated scroll-to-index @@ -7370,7 +9763,6 @@ function calculateSizeAndPositionDataAndUpdateScrollOffset(_ref) { var cellSize = _ref.cellSize; var computeMetadataCallback = _ref.computeMetadataCallback; var computeMetadataCallbackProps = _ref.computeMetadataCallbackProps; - var computeMetadataOnNextUpdate = _ref.computeMetadataOnNextUpdate; var nextCellsCount = _ref.nextCellsCount; var nextCellSize = _ref.nextCellSize; var nextScrollToIndex = _ref.nextScrollToIndex; @@ -7379,7 +9771,7 @@ function calculateSizeAndPositionDataAndUpdateScrollOffset(_ref) { // Don't compare cell sizes if they are functions because inline functions would cause infinite loops. // In that event users should use the manual recompute methods to inform of changes. - if (computeMetadataOnNextUpdate || cellCount !== nextCellsCount || (typeof cellSize === 'number' || typeof nextCellSize === 'number') && cellSize !== nextCellSize) { + if (cellCount !== nextCellsCount || (typeof cellSize === 'number' || typeof nextCellSize === 'number') && cellSize !== nextCellSize) { computeMetadataCallback(computeMetadataCallbackProps); // Updated cell metadata may have hidden the previous scrolled-to item. @@ -7389,35 +9781,23 @@ function calculateSizeAndPositionDataAndUpdateScrollOffset(_ref) { } } } -},{}],57:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = getNearestIndex; -/** - * Finds the nearest valid index to the one specified if the specified index is invalid. - * @param cellCount Number of rows or columns in the current axis - * @param targetIndex Index to use if possible - */ -function getNearestIndex(_ref) { - var cellCount = _ref.cellCount; - var targetIndex = _ref.targetIndex; - - return Math.max(0, Math.min(cellCount - 1, targetIndex)); -} -},{}],58:[function(require,module,exports){ +},{}],72:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = getOverscanIndices; +var SCROLL_DIRECTION_BACKWARD = exports.SCROLL_DIRECTION_BACKWARD = -1; +var SCROLL_DIRECTION_FIXED = exports.SCROLL_DIRECTION_FIXED = 0; +var SCROLL_DIRECTION_FORWARD = exports.SCROLL_DIRECTION_FORWARD = 1; + /** * Calculates the number of cells to overscan before and after a specified range. * This function ensures that overscanning doesn't exceed the available cells. + * * @param cellCount Number of rows or columns in the current axis + * @param scrollDirection One of SCROLL_DIRECTION_BACKWARD * @param overscanCellsCount Maximum number of cells to over-render in either direction * @param startIndex Begin of range of visible cells * @param stopIndex End of range of visible cells @@ -7425,134 +9805,42 @@ exports.default = getOverscanIndices; function getOverscanIndices(_ref) { var cellCount = _ref.cellCount; var overscanCellsCount = _ref.overscanCellsCount; + var scrollDirection = _ref.scrollDirection; var startIndex = _ref.startIndex; var stopIndex = _ref.stopIndex; - return { - overscanStartIndex: Math.max(0, startIndex - overscanCellsCount), - overscanStopIndex: Math.min(cellCount - 1, stopIndex + overscanCellsCount) - }; -} -},{}],59:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = getVisibleCellIndices; -/** - * Determines the range of cells to display for a given offset in order to fill the specified container. - * - * @param cellMetadata Metadata initially computed by initCellMetadata() - * @param containerSize Total size (width or height) of the container - * @param currentOffset Container's current (x or y) offset - * @return An object containing :start and :stop attributes, each specifying a cell index - */ -function getVisibleCellIndices(_ref) { - var cellMetadata = _ref.cellMetadata; - var containerSize = _ref.containerSize; - var currentOffset = _ref.currentOffset; - - var cellCount = cellMetadata.length; - - if (cellCount === 0) { - return {}; - } - - // TODO Add better guards here against NaN offset - - var lastDatum = cellMetadata[cellMetadata.length - 1]; - var totalCellSize = lastDatum.offset + lastDatum.size; - - // Ensure offset is within reasonable bounds - currentOffset = Math.max(0, Math.min(totalCellSize - containerSize, currentOffset)); - - var maxOffset = Math.min(totalCellSize, currentOffset + containerSize); - - var start = findNearestCell({ - cellMetadata: cellMetadata, - mode: EQUAL_OR_LOWER, - offset: currentOffset - }); - - var datum = cellMetadata[start]; - currentOffset = datum.offset + datum.size; - - var stop = start; - - while (currentOffset < maxOffset && stop < cellCount - 1) { - stop++; + var overscanStartIndex = void 0; + var overscanStopIndex = void 0; - currentOffset += cellMetadata[stop].size; + if (scrollDirection === SCROLL_DIRECTION_FORWARD) { + overscanStartIndex = startIndex; + overscanStopIndex = stopIndex + overscanCellsCount * 2; + } else if (scrollDirection === SCROLL_DIRECTION_BACKWARD) { + overscanStartIndex = startIndex - overscanCellsCount * 2; + overscanStopIndex = stopIndex; + } else { + overscanStartIndex = startIndex - overscanCellsCount; + overscanStopIndex = stopIndex + overscanCellsCount; } return { - start: start, - stop: stop + overscanStartIndex: Math.max(0, overscanStartIndex), + overscanStopIndex: Math.min(cellCount - 1, overscanStopIndex) }; } - -/** - * Binary search function inspired by react-infinite. - */ -function findNearestCell(_ref2) { - var cellMetadata = _ref2.cellMetadata; - var mode = _ref2.mode; - var offset = _ref2.offset; - - var high = cellMetadata.length - 1; - var low = 0; - var middle = undefined; - var currentOffset = undefined; - - // TODO Add better guards here against NaN offset - - while (low <= high) { - middle = low + Math.floor((high - low) / 2); - currentOffset = cellMetadata[middle].offset; - - if (currentOffset === offset) { - return middle; - } else if (currentOffset < offset) { - low = middle + 1; - } else if (currentOffset > offset) { - high = middle - 1; - } - } - - if (mode === EQUAL_OR_LOWER && low > 0) { - return low - 1; - } else if (mode === EQUAL_OR_HIGHER && high < cellMetadata.length - 1) { - return high + 1; - } -} - -var EQUAL_OR_LOWER = 1; -var EQUAL_OR_HIGHER = 2; -},{}],60:[function(require,module,exports){ +},{}],73:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = updateScrollIndexHelper; - -var _getNearestIndex = require('./getNearestIndex'); - -var _getNearestIndex2 = _interopRequireDefault(_getNearestIndex); - -var _getUpdatedOffsetForIndex = require('../../utils/getUpdatedOffsetForIndex'); - -var _getUpdatedOffsetForIndex2 = _interopRequireDefault(_getUpdatedOffsetForIndex); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - /** * Helper function that determines when to update scroll offsets to ensure that a scroll-to-index remains visible. + * This function also ensures that the scroll ofset isn't past the last column/row of cells. * - * @param cellMetadata Metadata initially computed by initCellMetadata() - * @param cellCount Number of rows or columns in the current axis * @param cellsSize Width or height of cells for the current axis + * @param cellSizeAndPositionManager Manages size and position metadata of cells * @param previousCellsCount Previous number of rows or columns * @param previousCellsSize Previous width or height of cells * @param previousScrollToIndex Previous scroll-to-index @@ -7563,51 +9851,41 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de * @param updateScrollIndexCallback Callback to invoke with an scroll-to-index value */ function updateScrollIndexHelper(_ref) { - var cellMetadata = _ref.cellMetadata; - var cellCount = _ref.cellCount; var cellSize = _ref.cellSize; + var cellSizeAndPositionManager = _ref.cellSizeAndPositionManager; var previousCellsCount = _ref.previousCellsCount; var previousCellSize = _ref.previousCellSize; + var previousScrollToAlignment = _ref.previousScrollToAlignment; var previousScrollToIndex = _ref.previousScrollToIndex; var previousSize = _ref.previousSize; var scrollOffset = _ref.scrollOffset; + var scrollToAlignment = _ref.scrollToAlignment; var scrollToIndex = _ref.scrollToIndex; var size = _ref.size; var updateScrollIndexCallback = _ref.updateScrollIndexCallback; + var cellCount = cellSizeAndPositionManager.getCellCount(); var hasScrollToIndex = scrollToIndex >= 0 && scrollToIndex < cellCount; var sizeHasChanged = size !== previousSize || !previousCellSize || typeof cellSize === 'number' && cellSize !== previousCellSize; // If we have a new scroll target OR if height/row-height has changed, // We should ensure that the scroll target is visible. - if (hasScrollToIndex && (sizeHasChanged || scrollToIndex !== previousScrollToIndex)) { + if (hasScrollToIndex && (sizeHasChanged || scrollToAlignment !== previousScrollToAlignment || scrollToIndex !== previousScrollToIndex)) { updateScrollIndexCallback(scrollToIndex); // If we don't have a selected item but list size or number of children have decreased, // Make sure we aren't scrolled too far past the current content. } else if (!hasScrollToIndex && cellCount > 0 && (size < previousSize || cellCount < previousCellsCount)) { - scrollToIndex = (0, _getNearestIndex2.default)({ - cellCount: cellCount, - targetIndex: cellCount - 1 - }); - - if (scrollToIndex < cellCount) { - var cellMetadatum = cellMetadata[scrollToIndex]; - var calculatedScrollOffset = (0, _getUpdatedOffsetForIndex2.default)({ - cellOffset: cellMetadatum.offset, - cellSize: cellMetadatum.size, - containerSize: size, - currentOffset: scrollOffset - }); - - // Only adjust the scroll position if we've scrolled below the last set of rows. - if (calculatedScrollOffset < scrollOffset) { - updateScrollIndexCallback(cellCount - 1); - } - } + // We need to ensure that the current scroll offset is still within the collection's range. + // To do this, we don't need to measure everything; CellMeasurer would perform poorly. + // Just check to make sure we're still okay. + // Only adjust the scroll position if we've scrolled below the last set of rows. + if (scrollOffset > cellSizeAndPositionManager.getTotalSize() - size) { + updateScrollIndexCallback(cellCount - 1); } + } } -},{"../../utils/getUpdatedOffsetForIndex":69,"./getNearestIndex":57}],61:[function(require,module,exports){ +},{}],74:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { @@ -7618,6 +9896,7 @@ var _createClass = function () { function defineProperties(target, props) { for exports.isRangeVisible = isRangeVisible; exports.scanForUnloadedRanges = scanForUnloadedRanges; +exports.forceUpdateReactVirtualizedComponent = forceUpdateReactVirtualizedComponent; var _react = require('react'); @@ -7625,6 +9904,10 @@ var _reactAddonsShallowCompare = require('react-addons-shallow-compare'); var _reactAddonsShallowCompare2 = _interopRequireDefault(_reactAddonsShallowCompare); +var _createCallbackMemoizer = require('../utils/createCallbackMemoizer'); + +var _createCallbackMemoizer2 = _interopRequireDefault(_createCallbackMemoizer); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } @@ -7638,14 +9921,15 @@ function _inherits(subClass, superClass) { if (typeof superClass !== "function" * This component decorates a virtual component and just-in-time prefetches rows as a user scrolls. * It is intended as a convenience component; fork it if you'd like finer-grained control over data-loading. */ - var InfiniteLoader = function (_Component) { _inherits(InfiniteLoader, _Component); function InfiniteLoader(props, context) { _classCallCheck(this, InfiniteLoader); - var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(InfiniteLoader).call(this, props, context)); + var _this = _possibleConstructorReturn(this, (InfiniteLoader.__proto__ || Object.getPrototypeOf(InfiniteLoader)).call(this, props, context)); + + _this._loadMoreRowsMemoizer = (0, _createCallbackMemoizer2.default)(); _this._onRowsRendered = _this._onRowsRendered.bind(_this); _this._registerChild = _this._registerChild.bind(_this); @@ -7668,18 +9952,45 @@ var InfiniteLoader = function (_Component) { value: function shouldComponentUpdate(nextProps, nextState) { return (0, _reactAddonsShallowCompare2.default)(this, nextProps, nextState); } + }, { + key: '_loadUnloadedRanges', + value: function _loadUnloadedRanges(unloadedRanges) { + var _this2 = this; + + var loadMoreRows = this.props.loadMoreRows; + + + unloadedRanges.forEach(function (unloadedRange) { + var promise = loadMoreRows(unloadedRange); + if (promise) { + promise.then(function () { + // Refresh the visible rows if any of them have just been loaded. + // Otherwise they will remain in their unloaded visual state. + if (isRangeVisible({ + lastRenderedStartIndex: _this2._lastRenderedStartIndex, + lastRenderedStopIndex: _this2._lastRenderedStopIndex, + startIndex: unloadedRange.startIndex, + stopIndex: unloadedRange.stopIndex + })) { + if (_this2._registeredChild) { + forceUpdateReactVirtualizedComponent(_this2._registeredChild); + } + } + }); + } + }); + } }, { key: '_onRowsRendered', value: function _onRowsRendered(_ref) { - var _this2 = this; + var _this3 = this; var startIndex = _ref.startIndex; var stopIndex = _ref.stopIndex; var _props = this.props; var isRowLoaded = _props.isRowLoaded; - var loadMoreRows = _props.loadMoreRows; var minimumBatchSize = _props.minimumBatchSize; - var rowsCount = _props.rowsCount; + var rowCount = _props.rowCount; var threshold = _props.threshold; @@ -7689,29 +10000,21 @@ var InfiniteLoader = function (_Component) { var unloadedRanges = scanForUnloadedRanges({ isRowLoaded: isRowLoaded, minimumBatchSize: minimumBatchSize, - rowsCount: rowsCount, + rowCount: rowCount, startIndex: Math.max(0, startIndex - threshold), - stopIndex: Math.min(rowsCount - 1, stopIndex + threshold) + stopIndex: Math.min(rowCount - 1, stopIndex + threshold) }); - unloadedRanges.forEach(function (unloadedRange) { - var promise = loadMoreRows(unloadedRange); - if (promise) { - promise.then(function () { - // Refresh the visible rows if any of them have just been loaded. - // Otherwise they will remain in their unloaded visual state. - if (isRangeVisible({ - lastRenderedStartIndex: _this2._lastRenderedStartIndex, - lastRenderedStopIndex: _this2._lastRenderedStopIndex, - startIndex: unloadedRange.startIndex, - stopIndex: unloadedRange.stopIndex - })) { - if (_this2._registeredChild) { - _this2._registeredChild.forceUpdate(); - } - } - }); - } + // For memoize comparison + var squashedUnloadedRanges = unloadedRanges.reduce(function (reduced, unloadedRange) { + return reduced.concat([unloadedRange.startIndex, unloadedRange.stopIndex]); + }, []); + + this._loadMoreRowsMemoizer({ + callback: function callback() { + _this3._loadUnloadedRanges(unloadedRanges); + }, + indices: { squashedUnloadedRanges: squashedUnloadedRanges } }); } }, { @@ -7742,7 +10045,7 @@ InfiniteLoader.propTypes = { /** * Function responsible for tracking the loaded state of each row. - * It should implement the following signature: (index: number): boolean + * It should implement the following signature: ({ index: number }): boolean */ isRowLoaded: _react.PropTypes.func.isRequired, @@ -7764,7 +10067,7 @@ InfiniteLoader.propTypes = { /** * Number of rows in list; can be arbitrary high number if actual number is unknown. */ - rowsCount: _react.PropTypes.number.isRequired, + rowCount: _react.PropTypes.number.isRequired, /** * Threshold at which to pre-fetch data. @@ -7775,7 +10078,7 @@ InfiniteLoader.propTypes = { }; InfiniteLoader.defaultProps = { minimumBatchSize: 10, - rowsCount: 0, + rowCount: 0, threshold: 15 }; exports.default = InfiniteLoader; @@ -7794,7 +10097,7 @@ function isRangeVisible(_ref2) { function scanForUnloadedRanges(_ref3) { var isRowLoaded = _ref3.isRowLoaded; var minimumBatchSize = _ref3.minimumBatchSize; - var rowsCount = _ref3.rowsCount; + var rowCount = _ref3.rowCount; var startIndex = _ref3.startIndex; var stopIndex = _ref3.stopIndex; @@ -7803,13 +10106,13 @@ function scanForUnloadedRanges(_ref3) { var rangeStartIndex = null; var rangeStopIndex = null; - for (var i = startIndex; i <= stopIndex; i++) { - var loaded = isRowLoaded(i); + for (var index = startIndex; index <= stopIndex; index++) { + var loaded = isRowLoaded({ index: index }); if (!loaded) { - rangeStopIndex = i; + rangeStopIndex = index; if (rangeStartIndex === null) { - rangeStartIndex = i; + rangeStartIndex = index; } } else if (rangeStopIndex !== null) { unloadedRanges.push({ @@ -7821,13 +10124,14 @@ function scanForUnloadedRanges(_ref3) { } } + // If :rangeStopIndex is not null it means we haven't ran out of unloaded rows. + // Scan forward to try filling our :minimumBatchSize. if (rangeStopIndex !== null) { - // Attempt to satisfy :minimumBatchSize requirement but don't exceed :rowsCount - var potentialStopIndex = Math.min(Math.max(rangeStopIndex, rangeStartIndex + minimumBatchSize - 1), rowsCount - 1); + var potentialStopIndex = Math.min(Math.max(rangeStopIndex, rangeStartIndex + minimumBatchSize - 1), rowCount - 1); - for (var i = rangeStopIndex + 1; i <= potentialStopIndex; i++) { - if (!isRowLoaded(i)) { - rangeStopIndex = i; + for (var _index = rangeStopIndex + 1; _index <= potentialStopIndex; _index++) { + if (!isRowLoaded({ index: _index })) { + rangeStopIndex = _index; } else { break; } @@ -7839,9 +10143,36 @@ function scanForUnloadedRanges(_ref3) { }); } + // Check to see if our first range ended prematurely. + // In this case we should scan backwards to try filling our :minimumBatchSize. + if (unloadedRanges.length) { + var firstUnloadedRange = unloadedRanges[0]; + + while (firstUnloadedRange.stopIndex - firstUnloadedRange.startIndex + 1 < minimumBatchSize && firstUnloadedRange.startIndex > 0) { + var _index2 = firstUnloadedRange.startIndex - 1; + + if (!isRowLoaded({ index: _index2 })) { + firstUnloadedRange.startIndex = _index2; + } else { + break; + } + } + } + return unloadedRanges; } -},{"react":undefined,"react-addons-shallow-compare":18}],62:[function(require,module,exports){ + +/** + * Since RV components use shallowCompare we need to force a render (even though props haven't changed). + * However InfiniteLoader may wrap a Grid or it may wrap a FlexTable or VirtualScroll. + * In the first case the built-in React forceUpdate() method is sufficient to force a re-render, + * But in the latter cases we need to use the RV-specific forceUpdateGrid() method. + * Else the inner Grid will not be re-rendered and visuals may be stale. + */ +function forceUpdateReactVirtualizedComponent(component) { + typeof component.forceUpdateGrid === 'function' ? component.forceUpdateGrid() : component.forceUpdate(); +} +},{"../utils/createCallbackMemoizer":84,"react":undefined,"react-addons-shallow-compare":18}],75:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { @@ -7857,7 +10188,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de exports.default = _InfiniteLoader3.default; exports.InfiniteLoader = _InfiniteLoader3.default; -},{"./InfiniteLoader":61}],63:[function(require,module,exports){ +},{"./InfiniteLoader":74}],76:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { @@ -7883,14 +10214,13 @@ function _inherits(subClass, superClass) { if (typeof superClass !== "function" /** * HOC that simplifies the process of synchronizing scrolling between two or more virtualized components. */ - var ScrollSync = function (_Component) { _inherits(ScrollSync, _Component); function ScrollSync(props, context) { _classCallCheck(this, ScrollSync); - var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(ScrollSync).call(this, props, context)); + var _this = _possibleConstructorReturn(this, (ScrollSync.__proto__ || Object.getPrototypeOf(ScrollSync)).call(this, props, context)); _this.state = { clientHeight: 0, @@ -7959,7 +10289,7 @@ ScrollSync.propTypes = { children: _react.PropTypes.func.isRequired }; exports.default = ScrollSync; -},{"react":undefined,"react-addons-shallow-compare":18}],64:[function(require,module,exports){ +},{"react":undefined,"react-addons-shallow-compare":18}],77:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { @@ -7975,13 +10305,15 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de exports.default = _ScrollSync3.default; exports.ScrollSync = _ScrollSync3.default; -},{"./ScrollSync":63}],65:[function(require,module,exports){ +},{"./ScrollSync":76}],78:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _Grid = require('../Grid'); @@ -8006,187 +10338,576 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +/** + * It is inefficient to create and manage a large list of DOM elements within a scrolling container + * if only a few of those elements are visible. The primary purpose of this component is to improve + * performance by only rendering the DOM nodes that a user is able to see based on their current + * scroll position. + * + * This component renders a virtualized list of elements with either fixed or dynamic heights. + */ +var VirtualScroll = function (_Component) { + _inherits(VirtualScroll, _Component); + + function VirtualScroll(props, context) { + _classCallCheck(this, VirtualScroll); + + var _this = _possibleConstructorReturn(this, (VirtualScroll.__proto__ || Object.getPrototypeOf(VirtualScroll)).call(this, props, context)); + + _this._cellRenderer = _this._cellRenderer.bind(_this); + _this._createRowClassNameGetter = _this._createRowClassNameGetter.bind(_this); + _this._createRowStyleGetter = _this._createRowStyleGetter.bind(_this); + _this._onScroll = _this._onScroll.bind(_this); + _this._onSectionRendered = _this._onSectionRendered.bind(_this); + return _this; + } + + _createClass(VirtualScroll, [{ + key: 'forceUpdateGrid', + value: function forceUpdateGrid() { + this.Grid.forceUpdate(); + } + + /** See Grid#measureAllCells */ + + }, { + key: 'measureAllRows', + value: function measureAllRows() { + this.Grid.measureAllCells(); + } + + /** See Grid#recomputeGridSize */ + + }, { + key: 'recomputeRowHeights', + value: function recomputeRowHeights() { + var index = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0]; + + this.Grid.recomputeGridSize({ + rowIndex: index + }); + this.forceUpdateGrid(); + } + }, { + key: 'render', + value: function render() { + var _this2 = this; + + var _props = this.props; + var className = _props.className; + var noRowsRenderer = _props.noRowsRenderer; + var scrollToIndex = _props.scrollToIndex; + var width = _props.width; + + + var classNames = (0, _classnames2.default)('VirtualScroll', className); + + return _react2.default.createElement(_Grid2.default, _extends({}, this.props, { + autoContainerWidth: true, + cellRenderer: this._cellRenderer, + cellClassName: this._createRowClassNameGetter(), + cellStyle: this._createRowStyleGetter(), + className: classNames, + columnWidth: width, + columnCount: 1, + noContentRenderer: noRowsRenderer, + onScroll: this._onScroll, + onSectionRendered: this._onSectionRendered, + ref: function ref(_ref) { + _this2.Grid = _ref; + }, + scrollToRow: scrollToIndex + })); + } + }, { + key: 'shouldComponentUpdate', + value: function shouldComponentUpdate(nextProps, nextState) { + return (0, _reactAddonsShallowCompare2.default)(this, nextProps, nextState); + } + }, { + key: '_cellRenderer', + value: function _cellRenderer(_ref2) { + var columnIndex = _ref2.columnIndex; + var isScrolling = _ref2.isScrolling; + var rowIndex = _ref2.rowIndex; + var rowRenderer = this.props.rowRenderer; + + + return rowRenderer({ + index: rowIndex, + isScrolling: isScrolling + }); + } + }, { + key: '_createRowClassNameGetter', + value: function _createRowClassNameGetter() { + var rowClassName = this.props.rowClassName; + + + return rowClassName instanceof Function ? function (_ref3) { + var rowIndex = _ref3.rowIndex; + return rowClassName({ index: rowIndex }); + } : function () { + return rowClassName; + }; + } + }, { + key: '_createRowStyleGetter', + value: function _createRowStyleGetter() { + var rowStyle = this.props.rowStyle; + + + var wrapped = rowStyle instanceof Function ? rowStyle : function () { + return rowStyle; + }; + + // Default width to 100% to prevent list rows from flowing under the vertical scrollbar + return function (_ref4) { + var rowIndex = _ref4.rowIndex; + return _extends({ + width: '100%' + }, wrapped({ index: rowIndex })); + }; + } + }, { + key: '_onScroll', + value: function _onScroll(_ref5) { + var clientHeight = _ref5.clientHeight; + var scrollHeight = _ref5.scrollHeight; + var scrollTop = _ref5.scrollTop; + var onScroll = this.props.onScroll; + + + onScroll({ clientHeight: clientHeight, scrollHeight: scrollHeight, scrollTop: scrollTop }); + } + }, { + key: '_onSectionRendered', + value: function _onSectionRendered(_ref6) { + var rowOverscanStartIndex = _ref6.rowOverscanStartIndex; + var rowOverscanStopIndex = _ref6.rowOverscanStopIndex; + var rowStartIndex = _ref6.rowStartIndex; + var rowStopIndex = _ref6.rowStopIndex; + var onRowsRendered = this.props.onRowsRendered; + + + onRowsRendered({ + overscanStartIndex: rowOverscanStartIndex, + overscanStopIndex: rowOverscanStopIndex, + startIndex: rowStartIndex, + stopIndex: rowStopIndex + }); + } + }]); + + return VirtualScroll; +}(_react.Component); + +VirtualScroll.propTypes = { + 'aria-label': _react.PropTypes.string, + + /** + * Removes fixed height from the scrollingContainer so that the total height + * of rows can stretch the window. Intended for use with WindowScroller + */ + autoHeight: _react.PropTypes.bool, + + /** Optional CSS class name */ + className: _react.PropTypes.string, + + /** + * Used to estimate the total height of a VirtualScroll before all of its rows have actually been measured. + * The estimated total height is adjusted as rows are rendered. + */ + estimatedRowSize: _react.PropTypes.number.isRequired, + + /** Height constraint for list (determines how many actual rows are rendered) */ + height: _react.PropTypes.number.isRequired, + + /** Optional renderer to be used in place of rows when rowCount is 0 */ + noRowsRenderer: _react.PropTypes.func.isRequired, + + /** + * Callback invoked with information about the slice of rows that were just rendered. + * ({ startIndex, stopIndex }): void + */ + onRowsRendered: _react.PropTypes.func.isRequired, + + /** + * Number of rows to render above/below the visible bounds of the list. + * These rows can help for smoother scrolling on touch devices. + */ + overscanRowCount: _react.PropTypes.number.isRequired, + + /** + * Callback invoked whenever the scroll offset changes within the inner scrollable region. + * This callback can be used to sync scrolling between lists, tables, or grids. + * ({ clientHeight, scrollHeight, scrollTop }): void + */ + onScroll: _react.PropTypes.func.isRequired, + + /** + * Either a fixed row height (number) or a function that returns the height of a row given its index. + * ({ index: number }): number + */ + rowHeight: _react.PropTypes.oneOfType([_react.PropTypes.number, _react.PropTypes.func]).isRequired, + + /** Responsbile for rendering a row given an index; ({ index: number }): node */ + rowRenderer: _react.PropTypes.func.isRequired, + + /** Optional custom CSS class for individual rows */ + rowClassName: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.func]), + + /** Number of rows in list. */ + rowCount: _react.PropTypes.number.isRequired, + + /** Optional custom styles for individual cells */ + rowStyle: _react.PropTypes.oneOfType([_react.PropTypes.object, _react.PropTypes.func]), + + /** See Grid#scrollToAlignment */ + scrollToAlignment: _react.PropTypes.oneOf(['auto', 'end', 'start', 'center']).isRequired, + + /** Row index to ensure visible (by forcefully scrolling if necessary) */ + scrollToIndex: _react.PropTypes.number, + + /** Vertical offset. */ + scrollTop: _react.PropTypes.number, + + /** Optional inline style */ + style: _react.PropTypes.object, + + /** Tab index for focus */ + tabIndex: _react.PropTypes.number, + + /** Width of list */ + width: _react.PropTypes.number.isRequired +}; +VirtualScroll.defaultProps = { + estimatedRowSize: 30, + noRowsRenderer: function noRowsRenderer() { + return null; + }, + onRowsRendered: function onRowsRendered() { + return null; + }, + onScroll: function onScroll() { + return null; + }, + overscanRowCount: 10, + scrollToAlignment: 'auto', + style: {} +}; +exports.default = VirtualScroll; +},{"../Grid":68,"classnames":undefined,"react":undefined,"react-addons-shallow-compare":18}],79:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.VirtualScroll = exports.default = undefined; + +var _VirtualScroll2 = require('./VirtualScroll'); + +var _VirtualScroll3 = _interopRequireDefault(_VirtualScroll2); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +exports.default = _VirtualScroll3.default; +exports.VirtualScroll = _VirtualScroll3.default; +},{"./VirtualScroll":78}],80:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _reactDom = require('react-dom'); + +var _reactDom2 = _interopRequireDefault(_reactDom); + +var _reactAddonsShallowCompare = require('react-addons-shallow-compare'); + +var _reactAddonsShallowCompare2 = _interopRequireDefault(_reactAddonsShallowCompare); + +var _raf = require('raf'); + +var _raf2 = _interopRequireDefault(_raf); + +var _onScroll = require('./utils/onScroll'); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var WindowScroller = function (_Component) { + _inherits(WindowScroller, _Component); + + function WindowScroller(props) { + _classCallCheck(this, WindowScroller); + + var _this = _possibleConstructorReturn(this, (WindowScroller.__proto__ || Object.getPrototypeOf(WindowScroller)).call(this, props)); + + var height = typeof window !== 'undefined' ? window.innerHeight : 0; + + _this.state = { + isScrolling: false, + height: height, + scrollTop: 0 + }; + + _this._onScrollWindow = _this._onScrollWindow.bind(_this); + _this._onResizeWindow = _this._onResizeWindow.bind(_this); + _this._enablePointerEventsAfterDelayCallback = _this._enablePointerEventsAfterDelayCallback.bind(_this); + return _this; + } -/** - * It is inefficient to create and manage a large list of DOM elements within a scrolling container - * if only a few of those elements are visible. The primary purpose of this component is to improve - * performance by only rendering the DOM nodes that a user is able to see based on their current - * scroll position. - * - * This component renders a virtualized list of elements with either fixed or dynamic heights. - */ + _createClass(WindowScroller, [{ + key: 'componentDidMount', + value: function componentDidMount() { + var height = this.state.height; -var VirtualScroll = function (_Component) { - _inherits(VirtualScroll, _Component); + // Subtract documentElement top to handle edge-case where a user is navigating back (history) from an already-scrolled bage. + // In this case the body's top position will be a negative number and this element's top will be increased (by that amount). - function VirtualScroll() { - _classCallCheck(this, VirtualScroll); + this._positionFromTop = _reactDom2.default.findDOMNode(this).getBoundingClientRect().top - document.documentElement.getBoundingClientRect().top; - return _possibleConstructorReturn(this, Object.getPrototypeOf(VirtualScroll).apply(this, arguments)); - } + if (height !== window.innerHeight) { + this.setState({ + height: window.innerHeight + }); + } - _createClass(VirtualScroll, [{ - key: 'recomputeRowHeights', + (0, _onScroll.registerScrollListener)(this); + window.addEventListener('resize', this._onResizeWindow, false); + } + }, { + key: 'componentWillUnmount', + value: function componentWillUnmount() { + (0, _onScroll.unregisterScrollListener)(this); + window.removeEventListener('resize', this._onResizeWindow, false); + } /** - * See Grid#recomputeGridSize + * Updates the state during the next animation frame. + * Use this method to avoid multiple renders in a small span of time. + * This helps performance for bursty events (like onScroll). */ - value: function recomputeRowHeights() { - this.refs.Grid.recomputeGridSize(); + + }, { + key: '_setNextState', + value: function _setNextState(state) { + var _this2 = this; + + if (this._setNextStateAnimationFrameId) { + _raf2.default.cancel(this._setNextStateAnimationFrameId); + } + + this._setNextStateAnimationFrameId = (0, _raf2.default)(function () { + _this2._setNextStateAnimationFrameId = null; + _this2.setState(state); + }); } }, { key: 'render', value: function render() { - var _props = this.props; - var className = _props.className; - var height = _props.height; - var noRowsRenderer = _props.noRowsRenderer; - var onRowsRendered = _props.onRowsRendered; - var _onScroll = _props.onScroll; - var rowHeight = _props.rowHeight; - var rowRenderer = _props.rowRenderer; - var overscanRowsCount = _props.overscanRowsCount; - var rowsCount = _props.rowsCount; - var scrollToIndex = _props.scrollToIndex; - var scrollTop = _props.scrollTop; - var width = _props.width; - + var children = this.props.children; + var _state = this.state; + var isScrolling = _state.isScrolling; + var scrollTop = _state.scrollTop; + var height = _state.height; - var classNames = (0, _classnames2.default)('VirtualScroll', className); - return _react2.default.createElement(_Grid2.default, { - ref: 'Grid', - 'aria-label': this.props['aria-label'], - className: classNames, - columnWidth: width, - columnsCount: 1, - height: height, - noContentRenderer: noRowsRenderer, - onScroll: function onScroll(_ref) { - var clientHeight = _ref.clientHeight; - var scrollHeight = _ref.scrollHeight; - var scrollTop = _ref.scrollTop; - return _onScroll({ clientHeight: clientHeight, scrollHeight: scrollHeight, scrollTop: scrollTop }); - }, - onSectionRendered: function onSectionRendered(_ref2) { - var rowOverscanStartIndex = _ref2.rowOverscanStartIndex; - var rowOverscanStopIndex = _ref2.rowOverscanStopIndex; - var rowStartIndex = _ref2.rowStartIndex; - var rowStopIndex = _ref2.rowStopIndex; - return onRowsRendered({ - overscanStartIndex: rowOverscanStartIndex, - overscanStopIndex: rowOverscanStopIndex, - startIndex: rowStartIndex, - stopIndex: rowStopIndex - }); - }, - overscanRowsCount: overscanRowsCount, - renderCell: function renderCell(_ref3) { - var columnIndex = _ref3.columnIndex; - var rowIndex = _ref3.rowIndex; - return rowRenderer(rowIndex); - }, - rowHeight: rowHeight, - rowsCount: rowsCount, - scrollToRow: scrollToIndex, - scrollTop: scrollTop, - width: width - }); + return _react2.default.createElement( + 'div', + null, + children({ + height: height, + isScrolling: isScrolling, + scrollTop: scrollTop + }) + ); } }, { key: 'shouldComponentUpdate', value: function shouldComponentUpdate(nextProps, nextState) { return (0, _reactAddonsShallowCompare2.default)(this, nextProps, nextState); } - }]); + }, { + key: '_enablePointerEventsAfterDelayCallback', + value: function _enablePointerEventsAfterDelayCallback() { + this.setState({ + isScrolling: false + }); + } + }, { + key: '_onResizeWindow', + value: function _onResizeWindow(event) { + var onResize = this.props.onResize; - return VirtualScroll; -}(_react.Component); -VirtualScroll.propTypes = { - 'aria-label': _react.PropTypes.string, + var height = window.innerHeight || 0; - /** Optional CSS class name */ - className: _react.PropTypes.string, + this.setState({ height: height }); - /** Height constraint for list (determines how many actual rows are rendered) */ - height: _react.PropTypes.number.isRequired, + onResize({ height: height }); + } + }, { + key: '_onScrollWindow', + value: function _onScrollWindow(event) { + var onScroll = this.props.onScroll; - /** Optional renderer to be used in place of rows when rowsCount is 0 */ - noRowsRenderer: _react.PropTypes.func.isRequired, + // In IE10+ scrollY is undefined, so we replace that with the latter - /** - * Callback invoked with information about the slice of rows that were just rendered. - * ({ startIndex, stopIndex }): void - */ - onRowsRendered: _react.PropTypes.func.isRequired, + var scrollY = 'scrollY' in window ? window.scrollY : document.documentElement.scrollTop; - /** - * Number of rows to render above/below the visible bounds of the list. - * These rows can help for smoother scrolling on touch devices. - */ - overscanRowsCount: _react.PropTypes.number.isRequired, + var scrollTop = Math.max(0, scrollY - this._positionFromTop); - /** - * Callback invoked whenever the scroll offset changes within the inner scrollable region. - * This callback can be used to sync scrolling between lists, tables, or grids. - * ({ clientHeight, scrollHeight, scrollTop }): void - */ - onScroll: _react.PropTypes.func.isRequired, + var state = { + isScrolling: true, + scrollTop: scrollTop + }; - /** - * Either a fixed row height (number) or a function that returns the height of a row given its index. - * (index: number): number - */ - rowHeight: _react.PropTypes.oneOfType([_react.PropTypes.number, _react.PropTypes.func]).isRequired, + if (!this.state.isScrolling) { + this.setState(state); + } else { + this._setNextState(state); + } - /** Responsbile for rendering a row given an index */ - rowRenderer: _react.PropTypes.func.isRequired, + onScroll({ scrollTop: scrollTop }); + } + }]); - /** Number of rows in list. */ - rowsCount: _react.PropTypes.number.isRequired, + return WindowScroller; +}(_react.Component); - /** Row index to ensure visible (by forcefully scrolling if necessary) */ - scrollToIndex: _react.PropTypes.number, +WindowScroller.propTypes = { + /** + * Function respondible for rendering children. + * This function should implement the following signature: + * ({ height, scrollTop }) => PropTypes.element + */ + children: _react.PropTypes.func.isRequired, - /** Vertical offset. */ - scrollTop: _react.PropTypes.number, + /** Callback to be invoked on-resize: ({ height }) */ + onResize: _react.PropTypes.func.isRequired, - /** Width of list */ - width: _react.PropTypes.number.isRequired + /** Callback to be invoked on-scroll: ({ scrollTop }) */ + onScroll: _react.PropTypes.func.isRequired }; -VirtualScroll.defaultProps = { - noRowsRenderer: function noRowsRenderer() { - return null; - }, - onRowsRendered: function onRowsRendered() { - return null; - }, - onScroll: function onScroll() { - return null; - }, - overscanRowsCount: 10 +WindowScroller.defaultProps = { + onResize: function onResize() {}, + onScroll: function onScroll() {} }; -exports.default = VirtualScroll; -},{"../Grid":55,"classnames":undefined,"react":undefined,"react-addons-shallow-compare":18}],66:[function(require,module,exports){ +exports.default = WindowScroller; +},{"./utils/onScroll":82,"raf":89,"react":undefined,"react-addons-shallow-compare":18,"react-dom":undefined}],81:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); -exports.VirtualScroll = exports.default = undefined; +exports.IS_SCROLLING_TIMEOUT = exports.WindowScroller = exports.default = undefined; -var _VirtualScroll2 = require('./VirtualScroll'); +var _WindowScroller2 = require('./WindowScroller'); -var _VirtualScroll3 = _interopRequireDefault(_VirtualScroll2); +var _WindowScroller3 = _interopRequireDefault(_WindowScroller2); + +var _onScroll = require('./utils/onScroll'); + +var _onScroll2 = _interopRequireDefault(_onScroll); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -exports.default = _VirtualScroll3.default; -exports.VirtualScroll = _VirtualScroll3.default; -},{"./VirtualScroll":65}],67:[function(require,module,exports){ +exports.default = _WindowScroller3.default; +exports.WindowScroller = _WindowScroller3.default; +exports.IS_SCROLLING_TIMEOUT = _onScroll2.default; +},{"./WindowScroller":80,"./utils/onScroll":82}],82:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.registerScrollListener = registerScrollListener; +exports.unregisterScrollListener = unregisterScrollListener; +var mountedInstances = []; +var originalBodyPointerEvents = null; +var disablePointerEventsTimeoutId = null; + +/** + * Specifies the number of miliseconds during which to disable pointer events while a scroll is in progress. + * This improves performance and makes scrolling smoother. + */ +var IS_SCROLLING_TIMEOUT = exports.IS_SCROLLING_TIMEOUT = 150; + +function enablePointerEventsIfDisabled() { + if (disablePointerEventsTimeoutId) { + disablePointerEventsTimeoutId = null; + + document.body.style.pointerEvents = originalBodyPointerEvents; + + originalBodyPointerEvents = null; + } +} + +function enablePointerEventsAfterDelayCallback() { + enablePointerEventsIfDisabled(); + mountedInstances.forEach(function (component) { + return component._enablePointerEventsAfterDelayCallback(); + }); +} + +function enablePointerEventsAfterDelay() { + if (disablePointerEventsTimeoutId) { + clearTimeout(disablePointerEventsTimeoutId); + } + + disablePointerEventsTimeoutId = setTimeout(enablePointerEventsAfterDelayCallback, IS_SCROLLING_TIMEOUT); +} + +function onScrollWindow(event) { + if (originalBodyPointerEvents == null) { + originalBodyPointerEvents = document.body.style.pointerEvents; + + document.body.style.pointerEvents = 'none'; + + enablePointerEventsAfterDelay(); + } + mountedInstances.forEach(function (component) { + return component._onScrollWindow(event); + }); +} + +function registerScrollListener(component) { + if (!mountedInstances.length) { + window.addEventListener('scroll', onScrollWindow); + } + mountedInstances.push(component); +} + +function unregisterScrollListener(component) { + mountedInstances = mountedInstances.filter(function (c) { + return c !== component; + }); + if (!mountedInstances.length) { + window.removeEventListener('scroll', onScrollWindow); + if (disablePointerEventsTimeoutId) { + clearTimeout(disablePointerEventsTimeoutId); + enablePointerEventsIfDisabled(); + } + } +} +},{}],83:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { @@ -8211,6 +10932,27 @@ Object.defineProperty(exports, 'AutoSizer', { } }); +var _CellMeasurer = require('./CellMeasurer'); + +Object.defineProperty(exports, 'CellMeasurer', { + enumerable: true, + get: function get() { + return _CellMeasurer.CellMeasurer; + } +}); +Object.defineProperty(exports, 'defaultCellMeasurerCellSizeCache', { + enumerable: true, + get: function get() { + return _CellMeasurer.defaultCellSizeCache; + } +}); +Object.defineProperty(exports, 'uniformSizeCellMeasurerCellSizeCache', { + enumerable: true, + get: function get() { + return _CellMeasurer.defaultCellSizeCache; + } +}); + var _Collection = require('./Collection'); Object.defineProperty(exports, 'Collection', { @@ -8231,6 +10973,30 @@ Object.defineProperty(exports, 'ColumnSizer', { var _FlexTable = require('./FlexTable'); +Object.defineProperty(exports, 'defaultFlexTableCellDataGetter', { + enumerable: true, + get: function get() { + return _FlexTable.defaultCellDataGetter; + } +}); +Object.defineProperty(exports, 'defaultFlexTableCellRenderer', { + enumerable: true, + get: function get() { + return _FlexTable.defaultCellRenderer; + } +}); +Object.defineProperty(exports, 'defaultFlexTableHeaderRenderer', { + enumerable: true, + get: function get() { + return _FlexTable.defaultHeaderRenderer; + } +}); +Object.defineProperty(exports, 'defaultFlexTableRowRenderer', { + enumerable: true, + get: function get() { + return _FlexTable.defaultRowRenderer; + } +}); Object.defineProperty(exports, 'FlexTable', { enumerable: true, get: function get() { @@ -8258,6 +11024,12 @@ Object.defineProperty(exports, 'SortIndicator', { var _Grid = require('./Grid'); +Object.defineProperty(exports, 'defaultCellRangeRenderer', { + enumerable: true, + get: function get() { + return _Grid.defaultCellRangeRenderer; + } +}); Object.defineProperty(exports, 'Grid', { enumerable: true, get: function get() { @@ -8291,8 +11063,17 @@ Object.defineProperty(exports, 'VirtualScroll', { return _VirtualScroll.VirtualScroll; } }); -},{"./ArrowKeyStepper":38,"./AutoSizer":40,"./Collection":45,"./ColumnSizer":48,"./FlexTable":53,"./Grid":55,"./InfiniteLoader":62,"./ScrollSync":64,"./VirtualScroll":66}],68:[function(require,module,exports){ -"use strict"; + +var _WindowScroller = require('./WindowScroller'); + +Object.defineProperty(exports, 'WindowScroller', { + enumerable: true, + get: function get() { + return _WindowScroller.WindowScroller; + } +}); +},{"./ArrowKeyStepper":43,"./AutoSizer":45,"./CellMeasurer":48,"./Collection":53,"./ColumnSizer":56,"./FlexTable":65,"./Grid":68,"./InfiniteLoader":75,"./ScrollSync":77,"./VirtualScroll":79,"./WindowScroller":81}],84:[function(require,module,exports){ +'use strict'; Object.defineProperty(exports, "__esModule", { value: true @@ -8312,10 +11093,14 @@ function createCallbackMemoizer() { var keys = Object.keys(indices); var allInitialized = !requireAllKeys || keys.every(function (key) { - return indices[key] >= 0; + var value = indices[key]; + return Array.isArray(value) ? value.length > 0 : value >= 0; }); var indexChanged = keys.length !== Object.keys(cachedIndices).length || keys.some(function (key) { - return cachedIndices[key] !== indices[key]; + var cachedValue = cachedIndices[key]; + var value = indices[key]; + + return Array.isArray(value) ? cachedValue.join(',') !== value.join(',') : cachedValue !== value; }); cachedIndices = indices; @@ -8325,8 +11110,8 @@ function createCallbackMemoizer() { } }; } -},{}],69:[function(require,module,exports){ -"use strict"; +},{}],85:[function(require,module,exports){ +'use strict'; Object.defineProperty(exports, "__esModule", { value: true @@ -8337,6 +11122,7 @@ exports.default = getUpdatedOffsetForIndex; * If the cell is already visible then the current offset will be returned. * If the current offset is too great or small, it will be adjusted just enough to ensure the specified index is visible. * + * @param align Desired alignment within container; one of "auto" (default), "start", or "end" * @param cellOffset Offset (x or y) position for cell * @param cellSize Size (width or height) of cell * @param containerSize Total size (width or height) of the container @@ -8344,6 +11130,8 @@ exports.default = getUpdatedOffsetForIndex; * @return Offset to use to ensure the specified cell is visible */ function getUpdatedOffsetForIndex(_ref) { + var _ref$align = _ref.align; + var align = _ref$align === undefined ? 'auto' : _ref$align; var cellOffset = _ref.cellOffset; var cellSize = _ref.cellSize; var containerSize = _ref.containerSize; @@ -8351,54 +11139,19 @@ function getUpdatedOffsetForIndex(_ref) { var maxOffset = cellOffset; var minOffset = maxOffset - containerSize + cellSize; - var newOffset = Math.max(minOffset, Math.min(maxOffset, currentOffset)); - - return newOffset; -} -},{}],70:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = initCellMetadata; -/** - * Initializes metadata for an axis and its cells. - * This data is used to determine which cells are visible given a container size and scroll position. - * - * @param cellCount Total number of cells. - * @param size Either a fixed size or a function that returns the size for a given given an index. - * @return Object mapping cell index to cell metadata (size, offset) - */ -function initCellMetadata(_ref) { - var cellCount = _ref.cellCount; - var size = _ref.size; - - var sizeGetter = size instanceof Function ? size : function (index) { - return size; - }; - - var cellMetadata = []; - var offset = 0; - for (var i = 0; i < cellCount; i++) { - var _size = sizeGetter(i); - - if (_size == null || isNaN(_size)) { - throw Error("Invalid size returned for cell " + i + " of value " + _size); - } - - cellMetadata[i] = { - size: _size, - offset: offset - }; - - offset += _size; + switch (align) { + case 'start': + return maxOffset; + case 'end': + return minOffset; + case 'center': + return maxOffset - (containerSize - cellSize) / 2; + default: + return Math.max(minOffset, Math.min(maxOffset, currentOffset)); } - - return cellMetadata; } -},{}],71:[function(require,module,exports){ +},{}],86:[function(require,module,exports){ 'use strict'; /** @@ -8551,7 +11304,7 @@ var removeResizeListener = function removeResizeListener(element, fn) { if (attachEvent) element.detachEvent('onresize', fn);else { element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1); if (!element.__resizeListeners__.length) { - element.removeEventListener('scroll', scrollListener); + element.removeEventListener('scroll', scrollListener, true); element.__resizeTriggers__ = !element.removeChild(element.__resizeTriggers__); } } @@ -8561,10 +11314,10 @@ module.exports = { addResizeListener: addResizeListener, removeResizeListener: removeResizeListener }; -},{}],72:[function(require,module,exports){ +},{}],87:[function(require,module,exports){ 'use strict'; module.exports = !!(typeof window !== 'undefined' && window.document && window.document.createElement); -},{}],73:[function(require,module,exports){ +},{}],88:[function(require,module,exports){ 'use strict'; var canUseDOM = require('./inDOM'); @@ -8590,7 +11343,7 @@ module.exports = function (recalc) { return size; }; -},{"./inDOM":72}],74:[function(require,module,exports){ +},{"./inDOM":87}],89:[function(require,module,exports){ (function (global){ var now = require('performance-now') , root = typeof window === 'undefined' ? global : window @@ -8666,7 +11419,7 @@ module.exports.polyfill = function() { } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"performance-now":75}],75:[function(require,module,exports){ +},{"performance-now":90}],90:[function(require,module,exports){ (function (process){ // Generated by CoffeeScript 1.7.1 (function() { @@ -8702,7 +11455,7 @@ module.exports.polyfill = function() { }).call(this); }).call(this,require('_process')) -},{"_process":19}],76:[function(require,module,exports){ +},{"_process":19}],91:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -8728,7 +11481,7 @@ function shallowCompare(instance, nextProps, nextState) { } module.exports = shallowCompare; -},{"fbjs/lib/shallowEqual":77}],77:[function(require,module,exports){ +},{"fbjs/lib/shallowEqual":92}],92:[function(require,module,exports){ /** * Copyright (c) 2013-present, Facebook, Inc. * All rights reserved. @@ -8756,7 +11509,8 @@ function is(x, y) { if (x === y) { // Steps 1-5, 7-10 // Steps 6.b-6.e: +0 != -0 - return x !== 0 || 1 / x === 1 / y; + // Added the nonzero y check to make Flow happy, but it is redundant + return x !== 0 || y !== 0 || 1 / x === 1 / y; } else { // Step 6.a: NaN == NaN return x !== x && y !== y; diff --git a/examples/dist/bundle.js b/examples/dist/bundle.js index 2600c29a..9cb00f76 100644 --- a/examples/dist/bundle.js +++ b/examples/dist/bundle.js @@ -1,26 +1,4 @@ require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _Select = require('./Select'); + +var _Select2 = _interopRequireDefault(_Select); + +var _utilsDefaultFilterOptions = require('./utils/defaultFilterOptions'); + +var _utilsDefaultFilterOptions2 = _interopRequireDefault(_utilsDefaultFilterOptions); + +var _utilsDefaultMenuRenderer = require('./utils/defaultMenuRenderer'); + +var _utilsDefaultMenuRenderer2 = _interopRequireDefault(_utilsDefaultMenuRenderer); + +var Creatable = _react2['default'].createClass({ + displayName: 'CreatableSelect', + + propTypes: { + // See Select.propTypes.filterOptions + filterOptions: _react2['default'].PropTypes.any, + + // Searches for any matching option within the set of options. + // This function prevents duplicate options from being created. + // ({ option: Object, options: Array, labelKey: string, valueKey: string }): boolean + isOptionUnique: _react2['default'].PropTypes.func, + + // Determines if the current input text represents a valid option. + // ({ label: string }): boolean + isValidNewOption: _react2['default'].PropTypes.func, + + // See Select.propTypes.menuRenderer + menuRenderer: _react2['default'].PropTypes.any, + + // Factory to create new option. + // ({ label: string, labelKey: string, valueKey: string }): Object + newOptionCreator: _react2['default'].PropTypes.func, + + // Creates prompt/placeholder option text. + // (filterText: string): string + promptTextCreator: _react2['default'].PropTypes.func, + + // Decides if a keyDown event (eg its `keyCode`) should result in the creation of a new option. + shouldKeyDownEventCreateNewOption: _react2['default'].PropTypes.func + }, + + // Default prop methods + statics: { + isOptionUnique: isOptionUnique, + isValidNewOption: isValidNewOption, + newOptionCreator: newOptionCreator, + promptTextCreator: promptTextCreator, + shouldKeyDownEventCreateNewOption: shouldKeyDownEventCreateNewOption + }, + + getDefaultProps: function getDefaultProps() { + return { + filterOptions: _utilsDefaultFilterOptions2['default'], + isOptionUnique: isOptionUnique, + isValidNewOption: isValidNewOption, + menuRenderer: _utilsDefaultMenuRenderer2['default'], + newOptionCreator: newOptionCreator, + promptTextCreator: promptTextCreator, + shouldKeyDownEventCreateNewOption: shouldKeyDownEventCreateNewOption + }; + }, + + createNewOption: function createNewOption() { + var _props = this.props; + var isValidNewOption = _props.isValidNewOption; + var newOptionCreator = _props.newOptionCreator; + var shouldKeyDownEventCreateNewOption = _props.shouldKeyDownEventCreateNewOption; + var _select$props = this.select.props; + var labelKey = _select$props.labelKey; + var options = _select$props.options; + var valueKey = _select$props.valueKey; + + var inputValue = this.select.getInputValue(); + + if (isValidNewOption({ label: inputValue })) { + var option = newOptionCreator({ label: inputValue, labelKey: labelKey, valueKey: valueKey }); + var _isOptionUnique = this.isOptionUnique({ option: option }); + + // Don't add the same option twice. + if (_isOptionUnique) { + options.unshift(option); + + this.select.selectValue(option); + } + } + }, + + filterOptions: function filterOptions() { + var _props2 = this.props; + var filterOptions = _props2.filterOptions; + var isValidNewOption = _props2.isValidNewOption; + var promptTextCreator = _props2.promptTextCreator; + + var filteredOptions = filterOptions.apply(undefined, arguments); + + var inputValue = this.select ? this.select.getInputValue() : ''; + + if (isValidNewOption({ label: inputValue })) { + var _newOptionCreator = this.props.newOptionCreator; + var _select$props2 = this.select.props; + var labelKey = _select$props2.labelKey; + var options = _select$props2.options; + var valueKey = _select$props2.valueKey; + + var option = _newOptionCreator({ label: inputValue, labelKey: labelKey, valueKey: valueKey }); + + // TRICKY Compare to all options (not just filtered options) in case option has already been selected). + // For multi-selects, this would remove it from the filtered list. + var _isOptionUnique2 = this.isOptionUnique({ option: option, options: options }); + + if (_isOptionUnique2) { + var _prompt = promptTextCreator(inputValue); + + this._createPlaceholderOption = _newOptionCreator({ label: _prompt, labelKey: labelKey, valueKey: valueKey }); + + filteredOptions.unshift(this._createPlaceholderOption); + } + } + + return filteredOptions; + }, + + isOptionUnique: function isOptionUnique(_ref) { + var option = _ref.option; + var options = _ref.options; + + if (!this.select) { + return false; + } + + var isOptionUnique = this.props.isOptionUnique; + var _select$props3 = this.select.props; + var labelKey = _select$props3.labelKey; + var valueKey = _select$props3.valueKey; + + options = options || this.select.filterFlatOptions(); + + return isOptionUnique({ + labelKey: labelKey, + option: option, + options: options, + valueKey: valueKey + }); + }, + + menuRenderer: function menuRenderer(params) { + var menuRenderer = this.props.menuRenderer; + + return menuRenderer(_extends({}, params, { + onSelect: this.onOptionSelect + })); + }, + + onInputKeyDown: function onInputKeyDown(event) { + var shouldKeyDownEventCreateNewOption = this.props.shouldKeyDownEventCreateNewOption; + + var focusedOption = this.select.getFocusedOption(); + + if (focusedOption && focusedOption === this._createPlaceholderOption && shouldKeyDownEventCreateNewOption({ keyCode: event.keyCode })) { + this.createNewOption(); + + // Prevent decorated Select from doing anything additional with this keyDown event + event.preventDefault(); + } + }, + + onOptionSelect: function onOptionSelect(option, event) { + if (option === this._createPlaceholderOption) { + this.createNewOption(); + } else { + this.select.selectValue(option); + } + }, + + render: function render() { + var _this = this; + + var _props3 = this.props; + var newOptionCreator = _props3.newOptionCreator; + var shouldKeyDownEventCreateNewOption = _props3.shouldKeyDownEventCreateNewOption; + + var restProps = _objectWithoutProperties(_props3, ['newOptionCreator', 'shouldKeyDownEventCreateNewOption']); + + return _react2['default'].createElement(_Select2['default'], _extends({}, restProps, { + allowCreate: true, + filterOptions: this.filterOptions, + menuRenderer: this.menuRenderer, + onInputKeyDown: this.onInputKeyDown, + ref: function (ref) { + return _this.select = ref; + } + })); + } +}); + +function isOptionUnique(_ref2) { + var option = _ref2.option; + var options = _ref2.options; + var labelKey = _ref2.labelKey; + var valueKey = _ref2.valueKey; + + return options.filter(function (existingOption) { + return existingOption[labelKey] === option[labelKey] || existingOption[valueKey] === option[valueKey]; + }).length === 0; +}; + +function isValidNewOption(_ref3) { + var label = _ref3.label; + + return !!label; +}; + +function newOptionCreator(_ref4) { + var label = _ref4.label; + var labelKey = _ref4.labelKey; + var valueKey = _ref4.valueKey; + + var option = {}; + option[valueKey] = label; + option[labelKey] = label; + option.className = 'Select-create-option-placeholder'; + return option; +}; + +function promptTextCreator(label) { + return 'Create option "' + label + '"'; +} + +function shouldKeyDownEventCreateNewOption(_ref5) { + var keyCode = _ref5.keyCode; + + switch (keyCode) { + case 9: // TAB + case 13: // ENTER + case 188: + // COMMA + return true; + } + + return false; +}; + +module.exports = Creatable; + +},{"./Select":"react-select-plus","./utils/defaultFilterOptions":7,"./utils/defaultMenuRenderer":8,"react":undefined}],3:[function(require,module,exports){ 'use strict'; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } @@ -543,6 +784,149 @@ module.exports = Value; },{"classnames":undefined,"react":undefined}],7:[function(require,module,exports){ 'use strict'; +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _stripDiacritics = require('./stripDiacritics'); + +var _stripDiacritics2 = _interopRequireDefault(_stripDiacritics); + +function filterOptions(options, filterValue, excludeOptions, props) { + var _this = this; + + if (props.ignoreAccents) { + filterValue = (0, _stripDiacritics2['default'])(filterValue); + } + + if (props.ignoreCase) { + filterValue = filterValue.toLowerCase(); + } + + if (excludeOptions) excludeOptions = excludeOptions.map(function (i) { + return i[props.valueKey]; + }); + + return options.filter(function (option) { + if (excludeOptions && excludeOptions.indexOf(option[props.valueKey]) > -1) return false; + if (props.filterOption) return props.filterOption.call(_this, option, filterValue); + if (!filterValue) return true; + var valueTest = String(option[props.valueKey]); + var labelTest = String(option[props.labelKey]); + if (props.ignoreAccents) { + if (props.matchProp !== 'label') valueTest = (0, _stripDiacritics2['default'])(valueTest); + if (props.matchProp !== 'value') labelTest = (0, _stripDiacritics2['default'])(labelTest); + } + if (props.ignoreCase) { + if (props.matchProp !== 'label') valueTest = valueTest.toLowerCase(); + if (props.matchProp !== 'value') labelTest = labelTest.toLowerCase(); + } + return props.matchPos === 'start' ? props.matchProp !== 'label' && valueTest.substr(0, filterValue.length) === filterValue || props.matchProp !== 'value' && labelTest.substr(0, filterValue.length) === filterValue : props.matchProp !== 'label' && valueTest.indexOf(filterValue) >= 0 || props.matchProp !== 'value' && labelTest.indexOf(filterValue) >= 0; + }); +} + +module.exports = filterOptions; + +},{"./stripDiacritics":9}],8:[function(require,module,exports){ +'use strict'; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _classnames = require('classnames'); + +var _classnames2 = _interopRequireDefault(_classnames); + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +function isGroup(option) { + return option && Array.isArray(option.options); +} + +function menuRenderer(_ref) { + var focusedOption = _ref.focusedOption; + var instancePrefix = _ref.instancePrefix; + var labelKey = _ref.labelKey; + var onFocus = _ref.onFocus; + var onOptionRef = _ref.onOptionRef; + var onSelect = _ref.onSelect; + var optionClassName = _ref.optionClassName; + var optionComponent = _ref.optionComponent; + var optionGroupComponent = _ref.optionGroupComponent; + var optionRenderer = _ref.optionRenderer; + var options = _ref.options; + var valueArray = _ref.valueArray; + var valueKey = _ref.valueKey; + + var OptionGroup = optionGroupComponent; + var Option = optionComponent; + var renderLabel = optionRenderer || this.getOptionLabel; + + var renderOptions = function renderOptions(optionsSubset) { + return optionsSubset.map(function (option, i) { + if (isGroup(option)) { + var optionGroupClass = (0, _classnames2['default'])({ + 'Select-option-group': true + }); + + return _react2['default'].createElement( + OptionGroup, + { + className: optionGroupClass, + key: 'option-group-' + i, + label: renderLabel(option), + option: option, + optionIndex: i + }, + renderOptions(option.options) + ); + } else { + var _ret = (function () { + var isSelected = valueArray && valueArray.indexOf(option) > -1; + var isFocused = option === focusedOption; + var optionRef = isFocused ? 'focused' : null; + var optionClass = (0, _classnames2['default'])(optionClassName, { + 'Select-option': true, + 'is-selected': isSelected, + 'is-focused': isFocused, + 'is-disabled': option.disabled + }); + + return { + v: _react2['default'].createElement( + Option, + { + className: optionClass, + instancePrefix: instancePrefix, + isDisabled: option.disabled, + isFocused: isFocused, + isSelected: isSelected, + key: 'option-' + i + '-' + option[valueKey], + onFocus: onFocus, + onSelect: onSelect, + option: option, + optionIndex: i, + ref: function (ref) { + onOptionRef(ref, isFocused); + } + }, + renderLabel(option, i) + ) + }; + })(); + + if (typeof _ret === 'object') return _ret.v; + } + }); + }; + + return renderOptions(options); +} + +module.exports = menuRenderer; + +},{"classnames":undefined,"react":undefined}],9:[function(require,module,exports){ +'use strict'; + var map = [{ 'base': 'A', 'letters': /[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g }, { 'base': 'AA', 'letters': /[\uA732]/g }, { 'base': 'AE', 'letters': /[\u00C6\u01FC\u01E2]/g }, { 'base': 'AO', 'letters': /[\uA734]/g }, { 'base': 'AU', 'letters': /[\uA736]/g }, { 'base': 'AV', 'letters': /[\uA738\uA73A]/g }, { 'base': 'AY', 'letters': /[\uA73C]/g }, { 'base': 'B', 'letters': /[\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181]/g }, { 'base': 'C', 'letters': /[\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E]/g }, { 'base': 'D', 'letters': /[\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779]/g }, { 'base': 'DZ', 'letters': /[\u01F1\u01C4]/g }, { 'base': 'Dz', 'letters': /[\u01F2\u01C5]/g }, { 'base': 'E', 'letters': /[\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E]/g }, { 'base': 'F', 'letters': /[\u0046\u24BB\uFF26\u1E1E\u0191\uA77B]/g }, { 'base': 'G', 'letters': /[\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E]/g }, { 'base': 'H', 'letters': /[\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D]/g }, { 'base': 'I', 'letters': /[\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197]/g }, { 'base': 'J', 'letters': /[\u004A\u24BF\uFF2A\u0134\u0248]/g }, { 'base': 'K', 'letters': /[\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2]/g }, { 'base': 'L', 'letters': /[\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780]/g }, { 'base': 'LJ', 'letters': /[\u01C7]/g }, { 'base': 'Lj', 'letters': /[\u01C8]/g }, { 'base': 'M', 'letters': /[\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C]/g }, { 'base': 'N', 'letters': /[\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4]/g }, { 'base': 'NJ', 'letters': /[\u01CA]/g }, { 'base': 'Nj', 'letters': /[\u01CB]/g }, { 'base': 'O', 'letters': /[\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C]/g }, { 'base': 'OI', 'letters': /[\u01A2]/g }, { 'base': 'OO', 'letters': /[\uA74E]/g }, { 'base': 'OU', 'letters': /[\u0222]/g }, { 'base': 'P', 'letters': /[\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754]/g }, { 'base': 'Q', 'letters': /[\u0051\u24C6\uFF31\uA756\uA758\u024A]/g }, { 'base': 'R', 'letters': /[\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782]/g }, { 'base': 'S', 'letters': /[\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784]/g }, { 'base': 'T', 'letters': /[\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786]/g }, { 'base': 'TZ', 'letters': /[\uA728]/g }, { 'base': 'U', 'letters': /[\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244]/g }, { 'base': 'V', 'letters': /[\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245]/g }, { 'base': 'VY', 'letters': /[\uA760]/g }, { 'base': 'W', 'letters': /[\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72]/g }, { 'base': 'X', 'letters': /[\u0058\u24CD\uFF38\u1E8A\u1E8C]/g }, { 'base': 'Y', 'letters': /[\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE]/g }, { 'base': 'Z', 'letters': /[\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762]/g }, { 'base': 'a', 'letters': /[\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250]/g }, { 'base': 'aa', 'letters': /[\uA733]/g }, { 'base': 'ae', 'letters': /[\u00E6\u01FD\u01E3]/g }, { 'base': 'ao', 'letters': /[\uA735]/g }, { 'base': 'au', 'letters': /[\uA737]/g }, { 'base': 'av', 'letters': /[\uA739\uA73B]/g }, { 'base': 'ay', 'letters': /[\uA73D]/g }, { 'base': 'b', 'letters': /[\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253]/g }, { 'base': 'c', 'letters': /[\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184]/g }, { 'base': 'd', 'letters': /[\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A]/g }, { 'base': 'dz', 'letters': /[\u01F3\u01C6]/g }, { 'base': 'e', 'letters': /[\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD]/g }, { 'base': 'f', 'letters': /[\u0066\u24D5\uFF46\u1E1F\u0192\uA77C]/g }, { 'base': 'g', 'letters': /[\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F]/g }, { 'base': 'h', 'letters': /[\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265]/g }, { 'base': 'hv', 'letters': /[\u0195]/g }, { 'base': 'i', 'letters': /[\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131]/g }, { 'base': 'j', 'letters': /[\u006A\u24D9\uFF4A\u0135\u01F0\u0249]/g }, { 'base': 'k', 'letters': /[\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3]/g }, { 'base': 'l', 'letters': /[\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747]/g }, { 'base': 'lj', 'letters': /[\u01C9]/g }, { 'base': 'm', 'letters': /[\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F]/g }, { 'base': 'n', 'letters': /[\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5]/g }, { 'base': 'nj', 'letters': /[\u01CC]/g }, { 'base': 'o', 'letters': /[\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275]/g }, { 'base': 'oi', 'letters': /[\u01A3]/g }, { 'base': 'ou', 'letters': /[\u0223]/g }, { 'base': 'oo', 'letters': /[\uA74F]/g }, { 'base': 'p', 'letters': /[\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755]/g }, { 'base': 'q', 'letters': /[\u0071\u24E0\uFF51\u024B\uA757\uA759]/g }, { 'base': 'r', 'letters': /[\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783]/g }, { 'base': 's', 'letters': /[\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B]/g }, { 'base': 't', 'letters': /[\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787]/g }, { 'base': 'tz', 'letters': /[\uA729]/g }, { 'base': 'u', 'letters': /[\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289]/g }, { 'base': 'v', 'letters': /[\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C]/g }, { 'base': 'vy', 'letters': /[\uA761]/g }, { 'base': 'w', 'letters': /[\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73]/g }, { 'base': 'x', 'letters': /[\u0078\u24E7\uFF58\u1E8B\u1E8D]/g }, { 'base': 'y', 'letters': /[\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF]/g }, { 'base': 'z', 'letters': /[\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763]/g }]; module.exports = function stripDiacritics(str) { @@ -563,6 +947,8 @@ var _extends = Object.assign || function (target) { for (var i = 1; i < argument function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } +function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } + function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } var _react = require('react'); @@ -581,13 +967,13 @@ var _classnames = require('classnames'); var _classnames2 = _interopRequireDefault(_classnames); -var _blacklist = require('blacklist'); +var _utilsDefaultFilterOptions = require('./utils/defaultFilterOptions'); -var _blacklist2 = _interopRequireDefault(_blacklist); +var _utilsDefaultFilterOptions2 = _interopRequireDefault(_utilsDefaultFilterOptions); -var _utilsStripDiacritics = require('./utils/stripDiacritics'); +var _utilsDefaultMenuRenderer = require('./utils/defaultMenuRenderer'); -var _utilsStripDiacritics2 = _interopRequireDefault(_utilsStripDiacritics); +var _utilsDefaultMenuRenderer2 = _interopRequireDefault(_utilsDefaultMenuRenderer); var _Async = require('./Async'); @@ -597,6 +983,10 @@ var _Dropdown = require('./Dropdown'); var _Dropdown2 = _interopRequireDefault(_Dropdown); +var _Creatable = require('./Creatable'); + +var _Creatable2 = _interopRequireDefault(_Creatable); + var _Option = require('./Option'); var _Option2 = _interopRequireDefault(_Option); @@ -619,11 +1009,19 @@ function clone(obj) { return copy; } +function isGroup(option) { + return option && Array.isArray(option.options); +} + function stringifyValue(value) { - if (typeof value === 'object') { + if (typeof value === 'string') { + return value; + } else if (typeof value === 'object') { return JSON.stringify(value); + } else if (value || value === 0) { + return String(value); } else { - return value; + return ''; } } @@ -637,7 +1035,6 @@ var Select = _react2['default'].createClass({ propTypes: { addLabelText: _react2['default'].PropTypes.string, // placeholder displayed when you want to add a label on a multi-value input - allowCreate: _react2['default'].PropTypes.bool, // whether to allow creation of new entries 'aria-label': _react2['default'].PropTypes.string, // Aria label (for assistive tech) 'aria-labelledby': _react2['default'].PropTypes.string, // HTML ID of an element that should be used as the label (for assistive tech) autoBlur: _react2['default'].PropTypes.bool, // automatically blur the component when an option is selected @@ -660,6 +1057,7 @@ var Select = _react2['default'].createClass({ ignoreCase: _react2['default'].PropTypes.bool, // whether to perform case-insensitive filtering inputProps: _react2['default'].PropTypes.object, // custom attributes for the Input inputRenderer: _react2['default'].PropTypes.func, // returns a custom input component + instanceId: _react2['default'].PropTypes.string, // set the components instanceId isLoading: _react2['default'].PropTypes.bool, // whether the Select is loading externally or not (such as options being loaded) isOpen: _react2['default'].PropTypes.bool, // whether the Select dropdown menu is open or not joinValues: _react2['default'].PropTypes.bool, // joins multiple values into a single form field with the delimiter (legacy mode) @@ -672,14 +1070,15 @@ var Select = _react2['default'].createClass({ menuStyle: _react2['default'].PropTypes.object, // optional style to apply to the menu multi: _react2['default'].PropTypes.bool, // multi-value input name: _react2['default'].PropTypes.string, // generates a hidden tag with this field name for html forms - newOptionCreator: _react2['default'].PropTypes.func, // factory to create new options when allowCreate set noResultsText: stringOrNode, // placeholder displayed when there are no matching search results onBlur: _react2['default'].PropTypes.func, // onBlur handler: function (event) {} onBlurResetsInput: _react2['default'].PropTypes.bool, // whether input is cleared on blur onChange: _react2['default'].PropTypes.func, // onChange handler: function (newValue) {} onClose: _react2['default'].PropTypes.func, // fires when the menu is closed + onCloseResetsInput: _react2['default'].PropTypes.bool, // whether input is cleared when menu is closed through the arrow onFocus: _react2['default'].PropTypes.func, // onFocus handler: function (event) {} onInputChange: _react2['default'].PropTypes.func, // onInputChange handler: function (inputValue) {} + onInputKeyDown: _react2['default'].PropTypes.func, // input keyDown handler: function (event) {} onMenuScrollToBottom: _react2['default'].PropTypes.func, // fires when the menu is scrolled to the bottom; can be used to paginate options onOpen: _react2['default'].PropTypes.func, // fires when the menu is opened onValueClick: _react2['default'].PropTypes.func, // onClick handler for value labels: function (value, event) {} @@ -708,13 +1107,12 @@ var Select = _react2['default'].createClass({ wrapperStyle: _react2['default'].PropTypes.object }, // optional style to apply to the component wrapper - statics: { Async: _Async2['default'] }, + statics: { Async: _Async2['default'], Creatable: _Creatable2['default'] }, getDefaultProps: function getDefaultProps() { return { addLabelText: 'Add "{label}"?', autosize: true, - allowCreate: false, backspaceRemoves: true, backspaceToRemoveMessage: 'Press backspace to remove {label}', clearable: true, @@ -724,7 +1122,7 @@ var Select = _react2['default'].createClass({ disabled: false, dropdownComponent: _Dropdown2['default'], escapeClearsValue: true, - filterOptions: true, + filterOptions: _utilsDefaultFilterOptions2['default'], ignoreAccents: true, ignoreCase: true, inputProps: {}, @@ -734,9 +1132,11 @@ var Select = _react2['default'].createClass({ matchPos: 'any', matchProp: 'any', menuBuffer: 0, + menuRenderer: _utilsDefaultMenuRenderer2['default'], multi: false, noResultsText: 'No results found', onBlurResetsInput: true, + onCloseResetsInput: true, openAfterFocus: false, optionComponent: _Option2['default'], optionGroupComponent: _OptionGroup2['default'], @@ -744,7 +1144,6 @@ var Select = _react2['default'].createClass({ placeholder: 'Select...', renderInvalidValues: false, required: false, - resetValue: null, scrollMenuIntoView: true, searchable: true, simpleValue: false, @@ -758,7 +1157,6 @@ var Select = _react2['default'].createClass({ return { inputValue: '', isFocused: false, - isLoading: false, isOpen: false, isPseudoFocused: false, required: false @@ -767,7 +1165,7 @@ var Select = _react2['default'].createClass({ componentWillMount: function componentWillMount() { this._flatOptions = this.flattenOptions(this.props.options); - this._instancePrefix = 'react-select-' + ++instanceId + '-'; + this._instancePrefix = 'react-select-' + (this.props.instanceId || ++instanceId) + '-'; var valueArray = this.getValueArray(this.props.value); if (this.props.required) { @@ -799,6 +1197,7 @@ var Select = _react2['default'].createClass({ componentWillUpdate: function componentWillUpdate(nextProps, nextState) { if (nextState.isOpen !== this.state.isOpen) { + this.toggleTouchOutsideEvent(nextState.isOpen); var handler = nextState.isOpen ? nextProps.onOpen : nextProps.onClose; handler && handler(); } @@ -806,11 +1205,11 @@ var Select = _react2['default'].createClass({ componentDidUpdate: function componentDidUpdate(prevProps, prevState) { // focus to the selected option - if (this.refs.menu && this.refs.focused && this.state.isOpen && !this.hasScrolledToOption) { - var focusedOptionNode = _reactDom2['default'].findDOMNode(this.refs.focused); + if (this.menu && this.focused && this.state.isOpen && !this.hasScrolledToOption) { + var focusedOptionNode = _reactDom2['default'].findDOMNode(this.focused); var focusedOptionPreviousSibling = focusedOptionNode.previousSibling; var focusedOptionParent = focusedOptionNode.parentElement; - var menuNode = _reactDom2['default'].findDOMNode(this.refs.menu); + var menuNode = _reactDom2['default'].findDOMNode(this.menu); if (focusedOptionPreviousSibling) { menuNode.scrollTop = focusedOptionPreviousSibling.offsetTop; } else if (focusedOptionParent && focusedOptionParent === 'Select-menu') { @@ -823,18 +1222,18 @@ var Select = _react2['default'].createClass({ this.hasScrolledToOption = false; } - if (this._scrollToFocusedOptionOnUpdate && this.refs.focused && this.refs.menu) { + if (this._scrollToFocusedOptionOnUpdate && this.focused && this.menu) { this._scrollToFocusedOptionOnUpdate = false; - var focusedDOM = _reactDom2['default'].findDOMNode(this.refs.focused); - var menuDOM = _reactDom2['default'].findDOMNode(this.refs.menu); + var focusedDOM = _reactDom2['default'].findDOMNode(this.focused); + var menuDOM = _reactDom2['default'].findDOMNode(this.menu); var focusedRect = focusedDOM.getBoundingClientRect(); var menuRect = menuDOM.getBoundingClientRect(); if (focusedRect.bottom > menuRect.bottom || focusedRect.top < menuRect.top) { menuDOM.scrollTop = focusedDOM.offsetTop + focusedDOM.clientHeight - menuDOM.offsetHeight; } } - if (this.props.scrollMenuIntoView && this.refs.menuContainer) { - var menuContainerRect = this.refs.menuContainer.getBoundingClientRect(); + if (this.props.scrollMenuIntoView && this.menuContainer) { + var menuContainerRect = this.menuContainer.getBoundingClientRect(); if (window.innerHeight < menuContainerRect.bottom + this.props.menuBuffer) { window.scrollBy(0, menuContainerRect.bottom + this.props.menuBuffer - window.innerHeight); } @@ -845,9 +1244,28 @@ var Select = _react2['default'].createClass({ } }, + componentWillUnmount: function componentWillUnmount() { + document.removeEventListener('touchstart', this.handleTouchOutside); + }, + + toggleTouchOutsideEvent: function toggleTouchOutsideEvent(enabled) { + if (enabled) { + document.addEventListener('touchstart', this.handleTouchOutside); + } else { + document.removeEventListener('touchstart', this.handleTouchOutside); + } + }, + + handleTouchOutside: function handleTouchOutside(event) { + // handle touch outside on ios to dismiss menu + if (this.wrapper && !this.wrapper.contains(event.target)) { + this.closeMenu(); + } + }, + focus: function focus() { - if (!this.refs.input) return; - this.refs.input.focus(); + if (!this.input) return; + this.input.focus(); if (this.props.openAfterFocus) { this.setState({ @@ -857,8 +1275,8 @@ var Select = _react2['default'].createClass({ }, blurInput: function blurInput() { - if (!this.refs.input) return; - this.refs.input.blur(); + if (!this.input) return; + this.input.blur(); }, handleTouchMove: function handleTouchMove(event) { @@ -918,7 +1336,7 @@ var Select = _react2['default'].createClass({ // Call focus() again here to be safe. this.focus(); - var input = this.refs.input; + var input = this.input; if (typeof input.getInput === 'function') { // Get the actual DOM input if the ref is an component input = input.getInput(); @@ -970,11 +1388,19 @@ var Select = _react2['default'].createClass({ }, closeMenu: function closeMenu() { - this.setState({ - isOpen: false, - isPseudoFocused: this.state.isFocused && !this.props.multi, - inputValue: '' - }); + if (this.props.onCloseResetsInput) { + this.setState({ + isOpen: false, + isPseudoFocused: this.state.isFocused && !this.props.multi, + inputValue: '' + }); + } else { + this.setState({ + isOpen: false, + isPseudoFocused: this.state.isFocused && !this.props.multi, + inputValue: this.state.inputValue + }); + } this.hasScrolledToOption = false; }, @@ -992,7 +1418,7 @@ var Select = _react2['default'].createClass({ handleInputBlur: function handleInputBlur(event) { // The check for menu.contains(activeElement) is necessary to prevent IE11's scrollbar from closing the menu in certain contexts. - if (this.refs.menu && (this.refs.menu === document.activeElement || this.refs.menu.contains(document.activeElement))) { + if (this.menu && (this.menu === document.activeElement || this.menu.contains(document.activeElement))) { this.focus(); return; } @@ -1013,6 +1439,7 @@ var Select = _react2['default'].createClass({ handleInputChange: function handleInputChange(event) { var newInputValue = event.target.value; + if (this.state.inputValue !== event.target.value && this.props.onInputChange) { var nextState = this.props.onInputChange(newInputValue); // Note: != used deliberately here to catch undefined and null @@ -1020,6 +1447,7 @@ var Select = _react2['default'].createClass({ newInputValue = '' + nextState; } } + this.setState({ isOpen: true, isPseudoFocused: false, @@ -1029,6 +1457,14 @@ var Select = _react2['default'].createClass({ handleKeyDown: function handleKeyDown(event) { if (this.props.disabled) return; + + if (typeof this.props.onInputKeyDown === 'function') { + this.props.onInputKeyDown(event); + if (event.defaultPrevented) { + return; + } + } + switch (event.keyCode) { case 8: // backspace @@ -1084,15 +1520,6 @@ var Select = _react2['default'].createClass({ // home key this.focusStartOption(); break; - // case 188: // , - // if (this.props.allowCreate && this.props.multi) { - // event.preventDefault(); - // event.stopPropagation(); - // this.selectFocusedOption(); - // } else { - // return; - // } - // break; default: return; } @@ -1199,7 +1626,7 @@ var Select = _react2['default'].createClass({ selectValue: function selectValue(value) { var _this3 = this; - //NOTE: update value in the callback to make sure the input value is empty so that there are no sttyling issues (Chrome had issue otherwise) + //NOTE: update value in the callback to make sure the input value is empty so that there are no styling issues (Chrome had issue otherwise) this.hasScrolledToOption = false; if (this.props.multi) { this.setState({ @@ -1247,13 +1674,23 @@ var Select = _react2['default'].createClass({ } event.stopPropagation(); event.preventDefault(); - this.setValue(this.props.resetValue); + this.setValue(this.getResetValue()); this.setState({ isOpen: false, inputValue: '' }, this.focus); }, + getResetValue: function getResetValue() { + if (this.props.resetValue !== undefined) { + return this.props.resetValue; + } else if (this.props.multi) { + return []; + } else { + return null; + } + }, + focusOption: function focusOption(option) { this.setState({ focusedOption: option @@ -1345,10 +1782,15 @@ var Select = _react2['default'].createClass({ }); }, + getFocusedOption: function getFocusedOption() { + return this._focusedOption; + }, + + getInputValue: function getInputValue() { + return this.state.inputValue; + }, + selectFocusedOption: function selectFocusedOption() { - // if (this.props.allowCreate && !this.state.focusedOption) { - // return this.selectValue(this.state.inputValue); - // } if (this._focusedOption) { return this.selectValue(this._focusedOption); } @@ -1389,7 +1831,7 @@ var Select = _react2['default'].createClass({ onRemove: _this4.removeValue, value: value }, - renderLabel(value), + renderLabel(value, i), _react2['default'].createElement( 'span', { className: 'Select-aria-only' }, @@ -1414,6 +1856,8 @@ var Select = _react2['default'].createClass({ }, renderInput: function renderInput(valueArray, focusedOptionIndex) { + var _this5 = this; + if (this.props.inputRenderer) { return this.props.inputRenderer(); } else { @@ -1438,13 +1882,19 @@ var Select = _react2['default'].createClass({ onBlur: this.handleInputBlur, onChange: this.handleInputChange, onFocus: this.handleInputFocus, - ref: 'input', + ref: function ref(_ref2) { + return _this5.input = _ref2; + }, required: this.state.required, value: this.state.inputValue }); if (this.props.disabled || !this.props.searchable) { - var divProps = (0, _blacklist2['default'])(this.props.inputProps, 'inputClassName'); + var _props$inputProps = this.props.inputProps; + var inputClassName = _props$inputProps.inputClassName; + + var divProps = _objectWithoutProperties(_props$inputProps, ['inputClassName']); + return _react2['default'].createElement('div', _extends({}, divProps, { role: 'combobox', 'aria-expanded': isOpen, @@ -1454,7 +1904,9 @@ var Select = _react2['default'].createClass({ tabIndex: this.props.tabIndex || 0, onBlur: this.handleInputBlur, onFocus: this.handleInputFocus, - ref: 'input', + ref: function (ref) { + return _this5.input = ref; + }, 'aria-readonly': '' + !!this.props.disabled, style: { border: 0, width: 1, display: 'inline-block' } })); } @@ -1471,7 +1923,7 @@ var Select = _react2['default'].createClass({ }, renderClear: function renderClear() { - if (!this.props.clearable || !this.props.value || this.props.multi && !this.props.value.length || this.props.disabled || this.props.isLoading) return; + if (!this.props.clearable || !this.props.value || this.props.value === 0 || this.props.multi && !this.props.value.length || this.props.disabled || this.props.isLoading) return; return _react2['default'].createElement( 'span', { className: 'Select-clear-zone', title: this.props.multi ? this.props.clearAllText : this.props.clearValueText, @@ -1493,151 +1945,111 @@ var Select = _react2['default'].createClass({ ); }, - filterOptions: function filterOptions(options, excludeOptions) { - var _this5 = this; - - var excludeOptionValues = null; + filterFlatOptions: function filterFlatOptions(excludeOptions) { var filterValue = this.state.inputValue; - if (typeof this.props.filterOptions === 'function') { - return this.props.filterOptions.call(this, options, filterValue, excludeOptions); - } else if (this.props.filterOptions) { - var _ret = (function () { - if (_this5.props.ignoreAccents) { - filterValue = (0, _utilsStripDiacritics2['default'])(filterValue); - } - if (_this5.props.ignoreCase) { - filterValue = filterValue.toLowerCase(); - } - if (excludeOptions) excludeOptionValues = excludeOptions.map(function (i) { - return i[_this5.props.valueKey]; - }); - var includeOption = function includeOption(option) { - if (excludeOptionValues && excludeOptionValues.indexOf(option[_this5.props.valueKey]) > -1) return false; - if (_this5.props.filterOption) return _this5.props.filterOption.call(_this5, option, filterValue); - if (!filterValue) return true; - var valueTest = String(option[_this5.props.valueKey]); - var labelTest = String(option[_this5.props.labelKey]); - if (_this5.props.ignoreAccents) { - if (_this5.props.matchProp !== 'label') valueTest = (0, _utilsStripDiacritics2['default'])(valueTest); - if (_this5.props.matchProp !== 'value') labelTest = (0, _utilsStripDiacritics2['default'])(labelTest); - } - if (_this5.props.ignoreCase) { - if (_this5.props.matchProp !== 'label') valueTest = valueTest.toLowerCase(); - if (_this5.props.matchProp !== 'value') labelTest = labelTest.toLowerCase(); - } - return _this5.props.matchPos === 'start' ? _this5.props.matchProp !== 'label' && valueTest.substr(0, filterValue.length) === filterValue || _this5.props.matchProp !== 'value' && labelTest.substr(0, filterValue.length) === filterValue : _this5.props.matchProp !== 'label' && valueTest.indexOf(filterValue) >= 0 || _this5.props.matchProp !== 'value' && labelTest.indexOf(filterValue) >= 0; - }; - var filteredOptions = []; - options.forEach(function (option) { - if (_this5.isGroup(option)) { - var filteredGroup = clone(option); - filteredGroup.options = _this5.filterOptions(option.options, excludeOptions); - if (filteredGroup.options.length) { - filteredOptions.push(filteredGroup); - }; - } else if (includeOption(option)) { - filteredOptions.push(option); - }; - }); - return { - v: filteredOptions - }; - })(); - - if (typeof _ret === 'object') return _ret.v; + var flatOptions = this._flatOptions; + if (this.props.filterOptions) { + // Maintain backwards compatibility with boolean attribute + var filterOptions = typeof this.props.filterOptions === 'function' ? this.props.filterOptions : _utilsDefaultFilterOptions2['default']; + + return filterOptions(flatOptions, filterValue, excludeOptions, { + filterOption: this.props.filterOption, + ignoreAccents: this.props.ignoreAccents, + ignoreCase: this.props.ignoreCase, + labelKey: this.props.labelKey, + matchPos: this.props.matchPos, + matchProp: this.props.matchProp, + valueKey: this.props.valueKey + }); } else { - return options; + return flatOptions; } }, - isGroup: function isGroup(option) { - return option && Array.isArray(option.options); - }, - - flattenOptions: function flattenOptions(options) { - if (!options) return; + flattenOptions: function flattenOptions(options, group) { + if (!options) return []; var flatOptions = []; for (var i = 0; i < options.length; i++) { - if (this.isGroup(options[i])) { - flatOptions = flatOptions.concat(this.flattenOptions(options[i].options)); + // We clone each option with a pointer to its parent group for efficient unflattening + var optionCopy = clone(options[i]); + optionCopy.isInTree = false; + if (group) { + optionCopy.group = group; + } + if (isGroup(optionCopy)) { + flatOptions = flatOptions.concat(this.flattenOptions(optionCopy.options, optionCopy)); + optionCopy.options = []; } else { - flatOptions.push(options[i]); + flatOptions.push(optionCopy); } } return flatOptions; }, - renderMenu: function renderMenu(options, valueArray, focusedOption) { - var _this6 = this; + unflattenOptions: function unflattenOptions(flatOptions) { + var groupedOptions = []; + var parent = undefined, + child = undefined; - if (options && options.length) { - if (this.props.menuRenderer) { - return this.props.menuRenderer({ - focusedOption: focusedOption, - focusOption: this.focusOption, - labelKey: this.props.labelKey, - options: options, - selectValue: this.selectValue, - valueArray: valueArray - }); - } else { - var _ret2 = (function () { - var OptionGroup = _this6.props.optionGroupComponent; - var Option = _this6.props.optionComponent; - var renderLabel = _this6.props.optionRenderer || _this6.getOptionLabel; + // Remove all ancestor groups from the tree + flatOptions.forEach(function (option) { + option.isInTree = false; + parent = option.group; + while (parent) { + if (parent.isInTree) { + parent.options = []; + parent.isInTree = false; + } + parent = parent.group; + } + }); - return { - v: options.map(function (option, i) { - if (_this6.isGroup(option)) { - var optionGroupClass = (0, _classnames2['default'])({ - 'Select-option-group': true - }); - - return _react2['default'].createElement( - OptionGroup, - { - className: optionGroupClass, - key: 'option-group-' + i, - label: renderLabel(option), - option: option - }, - _this6.renderMenu(option.options, valueArray, focusedOption) - ); - } else { - var isSelected = valueArray && valueArray.indexOf(option) > -1; - var isFocused = option === focusedOption; - var optionRef = isFocused ? 'focused' : null; - var optionClass = (0, _classnames2['default'])(_this6.props.optionClassName, { - 'Select-option': true, - 'is-selected': isSelected, - 'is-focused': isFocused, - 'is-disabled': option.disabled - }); - - return _react2['default'].createElement( - Option, - { - instancePrefix: _this6._instancePrefix, - optionIndex: i, - className: optionClass, - isDisabled: option.disabled, - isFocused: isFocused, - key: 'option-' + i + '-' + option[_this6.props.valueKey], - onSelect: _this6.selectValue, - onFocus: _this6.focusOption, - option: option, - isSelected: isSelected, - ref: optionRef - }, - renderLabel(option) - ); - } - }) - }; - })(); + // Now reconstruct the options tree + flatOptions.forEach(function (option) { + child = option; + parent = child.group; + while (parent) { + if (!child.isInTree) { + parent.options.push(child); + child.isInTree = true; + } - if (typeof _ret2 === 'object') return _ret2.v; + child = parent; + parent = child.group; } + if (!child.isInTree) { + groupedOptions.push(child); + child.isInTree = true; + } + }); + return groupedOptions; + }, + + onOptionRef: function onOptionRef(ref, isFocused) { + if (isFocused) { + this.focused = ref; + } + }, + + renderMenu: function renderMenu(options, valueArray, focusedOption) { + if (options && options.length) { + return this.props.menuRenderer({ + focusedOption: focusedOption, + focusOption: this.focusOption, + instancePrefix: this._instancePrefix, + labelKey: this.props.labelKey, + onFocus: this.focusOption, + onOptionRef: this.onOptionRef, + onSelect: this.selectValue, + optionClassName: this.props.optionClassName, + optionComponent: this.props.optionComponent, + optionGroupComponent: this.props.optionGroupComponent, + optionRenderer: this.props.optionRenderer || this.getOptionLabel, + options: options, + selectValue: this.selectValue, + valueArray: valueArray, + valueKey: this.props.valueKey + }); } else if (this.props.noResultsText) { return _react2['default'].createElement( 'div', @@ -1650,16 +2062,18 @@ var Select = _react2['default'].createClass({ }, renderHiddenField: function renderHiddenField(valueArray) { - var _this7 = this; + var _this6 = this; if (!this.props.name) return; if (this.props.joinValues) { var value = valueArray.map(function (i) { - return stringifyValue(i[_this7.props.valueKey]); + return stringifyValue(i[_this6.props.valueKey]); }).join(this.props.delimiter); return _react2['default'].createElement('input', { type: 'hidden', - ref: 'value', + ref: function (ref) { + return _this6.value = ref; + }, name: this.props.name, value: value, disabled: this.props.disabled }); @@ -1668,9 +2082,9 @@ var Select = _react2['default'].createClass({ return _react2['default'].createElement('input', { key: 'hidden.' + index, type: 'hidden', ref: 'value' + index, - name: _this7.props.name, - value: stringifyValue(item[_this7.props.valueKey]), - disabled: _this7.props.disabled }); + name: _this6.props.name, + value: stringifyValue(item[_this6.props.valueKey]), + disabled: _this6.props.disabled }); }); }, @@ -1693,6 +2107,8 @@ var Select = _react2['default'].createClass({ }, renderOuter: function renderOuter(options, valueArray, focusedOption) { + var _this7 = this; + var Dropdown = this.props.dropdownComponent; var menu = this.renderMenu(options, valueArray, focusedOption); if (!menu) { @@ -1704,10 +2120,14 @@ var Select = _react2['default'].createClass({ null, _react2['default'].createElement( 'div', - { ref: 'menuContainer', className: 'Select-menu-outer', style: this.props.menuContainerStyle }, + { ref: function (ref) { + return _this7.menuContainer = ref; + }, className: 'Select-menu-outer', style: this.props.menuContainerStyle }, _react2['default'].createElement( 'div', - { ref: 'menu', role: 'listbox', className: 'Select-menu', id: this._instancePrefix + '-list', + { ref: function (ref) { + return _this7.menu = ref; + }, role: 'listbox', className: 'Select-menu', id: this._instancePrefix + '-list', style: this.props.menuStyle, onScroll: this.handleMenuScroll, onMouseDown: this.handleMouseDownOnMenu }, @@ -1718,9 +2138,11 @@ var Select = _react2['default'].createClass({ }, render: function render() { + var _this8 = this; + var valueArray = this.getValueArray(this.props.value); - var options = this.filterOptions(this.props.options || [], this.props.multi ? valueArray : null); - this._visibleOptions = this.flattenOptions(options); + this._visibleOptions = this.filterFlatOptions(this.props.multi ? valueArray : null); + var options = this.unflattenOptions(this._visibleOptions); var isOpen = typeof this.props.isOpen === 'boolean' ? this.props.isOpen : this.state.isOpen; if (this.props.multi && !options.length && valueArray.length && !this.state.inputValue) isOpen = false; var focusedOptionIndex = this.getFocusableOptionIndex(valueArray[0]); @@ -1754,13 +2176,17 @@ var Select = _react2['default'].createClass({ return _react2['default'].createElement( 'div', - { ref: 'wrapper', + { ref: function (ref) { + return _this8.wrapper = ref; + }, className: className, style: this.props.wrapperStyle }, this.renderHiddenField(valueArray), _react2['default'].createElement( 'div', - { ref: 'control', + { ref: function (ref) { + return _this8.control = ref; + }, className: 'Select-control', style: this.props.style, onKeyDown: this.handleKeyDown, @@ -1789,4 +2215,4 @@ var Select = _react2['default'].createClass({ exports['default'] = Select; module.exports = exports['default']; -},{"./Async":2,"./Dropdown":3,"./Option":4,"./OptionGroup":5,"./Value":6,"./utils/stripDiacritics":7,"blacklist":1,"classnames":undefined,"react":undefined,"react-dom":undefined,"react-input-autosize":undefined}]},{},[]); +},{"./Async":1,"./Creatable":2,"./Dropdown":3,"./Option":4,"./OptionGroup":5,"./Value":6,"./utils/defaultFilterOptions":7,"./utils/defaultMenuRenderer":8,"classnames":undefined,"react":undefined,"react-dom":undefined,"react-input-autosize":undefined}]},{},[]); diff --git a/examples/dist/common.js b/examples/dist/common.js index 1fd87f4c..febdd02d 100644 --- a/examples/dist/common.js +++ b/examples/dist/common.js @@ -1,4 +1,186 @@ require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + +},{}],2:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -23,7 +205,7 @@ var AutoFocusUtils = { }; module.exports = AutoFocusUtils; -},{"./ReactDOMComponentTree":41,"fbjs/lib/focusNode":151}],2:[function(require,module,exports){ +},{"./ReactDOMComponentTree":43,"fbjs/lib/focusNode":153}],3:[function(require,module,exports){ /** * Copyright 2013-present Facebook, Inc. * All rights reserved. @@ -313,8 +495,10 @@ function getNativeBeforeInputChars(topLevelType, nativeEvent) { function getFallbackBeforeInputChars(topLevelType, nativeEvent) { // If we are currently composing (IME) and using a fallback to do so, // try to extract the composed characters from the fallback object. + // If composition event is available, we extract a string only at + // compositionevent, otherwise extract it at fallback events. if (currentComposition) { - if (topLevelType === topLevelTypes.topCompositionEnd || isFallbackCompositionEnd(topLevelType, nativeEvent)) { + if (topLevelType === topLevelTypes.topCompositionEnd || !canUseCompositionEvent && isFallbackCompositionEnd(topLevelType, nativeEvent)) { var chars = currentComposition.getData(); FallbackCompositionState.release(currentComposition); currentComposition = null; @@ -412,7 +596,7 @@ var BeforeInputEventPlugin = { }; module.exports = BeforeInputEventPlugin; -},{"./EventConstants":16,"./EventPropagators":20,"./FallbackCompositionState":21,"./SyntheticCompositionEvent":98,"./SyntheticInputEvent":102,"fbjs/lib/ExecutionEnvironment":143,"fbjs/lib/keyOf":161}],3:[function(require,module,exports){ +},{"./EventConstants":17,"./EventPropagators":21,"./FallbackCompositionState":22,"./SyntheticCompositionEvent":100,"./SyntheticInputEvent":104,"fbjs/lib/ExecutionEnvironment":145,"fbjs/lib/keyOf":163}],4:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -561,7 +745,7 @@ var CSSProperty = { }; module.exports = CSSProperty; -},{}],4:[function(require,module,exports){ +},{}],5:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -767,7 +951,7 @@ var CSSPropertyOperations = { }; module.exports = CSSPropertyOperations; -},{"./CSSProperty":3,"./ReactInstrumentation":73,"./dangerousStyleValue":116,"fbjs/lib/ExecutionEnvironment":143,"fbjs/lib/camelizeStyleName":145,"fbjs/lib/hyphenateStyleName":156,"fbjs/lib/memoizeStringOnly":163,"fbjs/lib/warning":167}],5:[function(require,module,exports){ +},{"./CSSProperty":4,"./ReactInstrumentation":73,"./dangerousStyleValue":118,"fbjs/lib/ExecutionEnvironment":145,"fbjs/lib/camelizeStyleName":147,"fbjs/lib/hyphenateStyleName":158,"fbjs/lib/memoizeStringOnly":164,"fbjs/lib/warning":168}],6:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -874,7 +1058,7 @@ _assign(CallbackQueue.prototype, { PooledClass.addPoolingTo(CallbackQueue); module.exports = CallbackQueue; -},{"./PooledClass":25,"./reactProdInvariant":135,"fbjs/lib/invariant":157,"object-assign":168}],6:[function(require,module,exports){ +},{"./PooledClass":26,"./reactProdInvariant":137,"fbjs/lib/invariant":159,"object-assign":169}],7:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -932,7 +1116,7 @@ function shouldUseChangeEvent(elem) { var doesChangeEventBubble = false; if (ExecutionEnvironment.canUseDOM) { // See `handleChange` comment below - doesChangeEventBubble = isEventSupported('change') && (!('documentMode' in document) || document.documentMode > 8); + doesChangeEventBubble = isEventSupported('change') && (!document.documentMode || document.documentMode > 8); } function manualDispatchChangeEvent(nativeEvent) { @@ -998,7 +1182,7 @@ if (ExecutionEnvironment.canUseDOM) { // deleting text, so we ignore its input events. // IE10+ fire input events to often, such when a placeholder // changes or when an input with a placeholder is focused. - isInputEventSupported = isEventSupported('input') && (!('documentMode' in document) || document.documentMode > 11); + isInputEventSupported = isEventSupported('input') && (!document.documentMode || document.documentMode > 11); } /** @@ -1200,7 +1384,7 @@ var ChangeEventPlugin = { }; module.exports = ChangeEventPlugin; -},{"./EventConstants":16,"./EventPluginHub":17,"./EventPropagators":20,"./ReactDOMComponentTree":41,"./ReactUpdates":91,"./SyntheticEvent":100,"./getEventTarget":124,"./isEventSupported":131,"./isTextInputElement":132,"fbjs/lib/ExecutionEnvironment":143,"fbjs/lib/keyOf":161}],7:[function(require,module,exports){ +},{"./EventConstants":17,"./EventPluginHub":18,"./EventPropagators":21,"./ReactDOMComponentTree":43,"./ReactUpdates":93,"./SyntheticEvent":102,"./getEventTarget":126,"./isEventSupported":133,"./isTextInputElement":134,"fbjs/lib/ExecutionEnvironment":145,"fbjs/lib/keyOf":163}],8:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -1395,7 +1579,7 @@ var DOMChildrenOperations = { }; module.exports = DOMChildrenOperations; -},{"./DOMLazyTree":8,"./Danger":12,"./ReactDOMComponentTree":41,"./ReactInstrumentation":73,"./ReactMultiChildUpdateTypes":78,"./createMicrosoftUnsafeLocalFunction":115,"./setInnerHTML":137,"./setTextContent":138}],8:[function(require,module,exports){ +},{"./DOMLazyTree":9,"./Danger":13,"./ReactDOMComponentTree":43,"./ReactInstrumentation":73,"./ReactMultiChildUpdateTypes":78,"./createMicrosoftUnsafeLocalFunction":117,"./setInnerHTML":139,"./setTextContent":140}],9:[function(require,module,exports){ /** * Copyright 2015-present, Facebook, Inc. * All rights reserved. @@ -1514,7 +1698,7 @@ DOMLazyTree.queueHTML = queueHTML; DOMLazyTree.queueText = queueText; module.exports = DOMLazyTree; -},{"./DOMNamespaces":9,"./createMicrosoftUnsafeLocalFunction":115,"./setInnerHTML":137,"./setTextContent":138}],9:[function(require,module,exports){ +},{"./DOMNamespaces":10,"./createMicrosoftUnsafeLocalFunction":117,"./setInnerHTML":139,"./setTextContent":140}],10:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -1535,7 +1719,7 @@ var DOMNamespaces = { }; module.exports = DOMNamespaces; -},{}],10:[function(require,module,exports){ +},{}],11:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -1742,7 +1926,7 @@ var DOMProperty = { }; module.exports = DOMProperty; -},{"./reactProdInvariant":135,"fbjs/lib/invariant":157}],11:[function(require,module,exports){ +},{"./reactProdInvariant":137,"fbjs/lib/invariant":159}],12:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -1758,7 +1942,6 @@ module.exports = DOMProperty; var DOMProperty = require('./DOMProperty'); var ReactDOMComponentTree = require('./ReactDOMComponentTree'); -var ReactDOMInstrumentation = require('./ReactDOMInstrumentation'); var ReactInstrumentation = require('./ReactInstrumentation'); var quoteAttributeValueForBrowser = require('./quoteAttributeValueForBrowser'); @@ -1823,9 +2006,6 @@ var DOMPropertyOperations = { * @return {?string} Markup string, or null if the property was invalid. */ createMarkupForProperty: function (name, value) { - if ("production" !== 'production') { - ReactDOMInstrumentation.debugTool.onCreateMarkupForProperty(name, value); - } var propertyInfo = DOMProperty.properties.hasOwnProperty(name) ? DOMProperty.properties[name] : null; if (propertyInfo) { if (shouldIgnoreValue(propertyInfo, value)) { @@ -1898,7 +2078,6 @@ var DOMPropertyOperations = { } if ("production" !== 'production') { - ReactDOMInstrumentation.debugTool.onSetValueForProperty(node, name, value); var payload = {}; payload[name] = value; ReactInstrumentation.debugTool.onHostOperation(ReactDOMComponentTree.getInstanceFromNode(node)._debugID, 'update attribute', payload); @@ -1931,7 +2110,6 @@ var DOMPropertyOperations = { deleteValueForAttribute: function (node, name) { node.removeAttribute(name); if ("production" !== 'production') { - ReactDOMInstrumentation.debugTool.onDeleteValueForProperty(node, name); ReactInstrumentation.debugTool.onHostOperation(ReactDOMComponentTree.getInstanceFromNode(node)._debugID, 'remove attribute', name); } }, @@ -1963,7 +2141,6 @@ var DOMPropertyOperations = { } if ("production" !== 'production') { - ReactDOMInstrumentation.debugTool.onDeleteValueForProperty(node, name); ReactInstrumentation.debugTool.onHostOperation(ReactDOMComponentTree.getInstanceFromNode(node)._debugID, 'remove attribute', name); } } @@ -1971,7 +2148,7 @@ var DOMPropertyOperations = { }; module.exports = DOMPropertyOperations; -},{"./DOMProperty":10,"./ReactDOMComponentTree":41,"./ReactDOMInstrumentation":49,"./ReactInstrumentation":73,"./quoteAttributeValueForBrowser":134,"fbjs/lib/warning":167}],12:[function(require,module,exports){ +},{"./DOMProperty":11,"./ReactDOMComponentTree":43,"./ReactInstrumentation":73,"./quoteAttributeValueForBrowser":136,"fbjs/lib/warning":168}],13:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -2020,7 +2197,7 @@ var Danger = { }; module.exports = Danger; -},{"./DOMLazyTree":8,"./reactProdInvariant":135,"fbjs/lib/ExecutionEnvironment":143,"fbjs/lib/createNodesFromMarkup":148,"fbjs/lib/emptyFunction":149,"fbjs/lib/invariant":157}],13:[function(require,module,exports){ +},{"./DOMLazyTree":9,"./reactProdInvariant":137,"fbjs/lib/ExecutionEnvironment":145,"fbjs/lib/createNodesFromMarkup":150,"fbjs/lib/emptyFunction":151,"fbjs/lib/invariant":159}],14:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -2048,7 +2225,7 @@ var keyOf = require('fbjs/lib/keyOf'); var DefaultEventPluginOrder = [keyOf({ ResponderEventPlugin: null }), keyOf({ SimpleEventPlugin: null }), keyOf({ TapEventPlugin: null }), keyOf({ EnterLeaveEventPlugin: null }), keyOf({ ChangeEventPlugin: null }), keyOf({ SelectEventPlugin: null }), keyOf({ BeforeInputEventPlugin: null })]; module.exports = DefaultEventPluginOrder; -},{"fbjs/lib/keyOf":161}],14:[function(require,module,exports){ +},{"fbjs/lib/keyOf":163}],15:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -2099,7 +2276,7 @@ var DisabledInputUtils = { }; module.exports = DisabledInputUtils; -},{}],15:[function(require,module,exports){ +},{}],16:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -2205,7 +2382,7 @@ var EnterLeaveEventPlugin = { }; module.exports = EnterLeaveEventPlugin; -},{"./EventConstants":16,"./EventPropagators":20,"./ReactDOMComponentTree":41,"./SyntheticMouseEvent":104,"fbjs/lib/keyOf":161}],16:[function(require,module,exports){ +},{"./EventConstants":17,"./EventPropagators":21,"./ReactDOMComponentTree":43,"./SyntheticMouseEvent":106,"fbjs/lib/keyOf":163}],17:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -2303,7 +2480,7 @@ var EventConstants = { }; module.exports = EventConstants; -},{"fbjs/lib/keyMirror":160}],17:[function(require,module,exports){ +},{"fbjs/lib/keyMirror":162}],18:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -2361,6 +2538,12 @@ var executeDispatchesAndReleaseTopLevel = function (e) { return executeDispatchesAndRelease(e, false); }; +var getDictionaryKey = function (inst) { + // Prevents V8 performance issue: + // https://github.com/facebook/react/pull/7232 + return '.' + inst._rootNodeID; +}; + /** * This is a unified interface for event plugins to be installed and configured. * @@ -2404,7 +2587,7 @@ var EventPluginHub = { }, /** - * Stores `listener` at `listenerBank[registrationName][id]`. Is idempotent. + * Stores `listener` at `listenerBank[registrationName][key]`. Is idempotent. * * @param {object} inst The instance, which is the source of events. * @param {string} registrationName Name of listener (e.g. `onClick`). @@ -2413,8 +2596,9 @@ var EventPluginHub = { putListener: function (inst, registrationName, listener) { !(typeof listener === 'function') ? "production" !== 'production' ? invariant(false, 'Expected %s listener to be a function, instead got type %s', registrationName, typeof listener) : _prodInvariant('94', registrationName, typeof listener) : void 0; + var key = getDictionaryKey(inst); var bankForRegistrationName = listenerBank[registrationName] || (listenerBank[registrationName] = {}); - bankForRegistrationName[inst._rootNodeID] = listener; + bankForRegistrationName[key] = listener; var PluginModule = EventPluginRegistry.registrationNameModules[registrationName]; if (PluginModule && PluginModule.didPutListener) { @@ -2429,7 +2613,8 @@ var EventPluginHub = { */ getListener: function (inst, registrationName) { var bankForRegistrationName = listenerBank[registrationName]; - return bankForRegistrationName && bankForRegistrationName[inst._rootNodeID]; + var key = getDictionaryKey(inst); + return bankForRegistrationName && bankForRegistrationName[key]; }, /** @@ -2447,7 +2632,8 @@ var EventPluginHub = { var bankForRegistrationName = listenerBank[registrationName]; // TODO: This should never be null -- when is it? if (bankForRegistrationName) { - delete bankForRegistrationName[inst._rootNodeID]; + var key = getDictionaryKey(inst); + delete bankForRegistrationName[key]; } }, @@ -2457,12 +2643,13 @@ var EventPluginHub = { * @param {object} inst The instance, which is the source of events. */ deleteAllListeners: function (inst) { + var key = getDictionaryKey(inst); for (var registrationName in listenerBank) { if (!listenerBank.hasOwnProperty(registrationName)) { continue; } - if (!listenerBank[registrationName][inst._rootNodeID]) { + if (!listenerBank[registrationName][key]) { continue; } @@ -2471,7 +2658,7 @@ var EventPluginHub = { PluginModule.willDeleteListener(inst, registrationName); } - delete listenerBank[registrationName][inst._rootNodeID]; + delete listenerBank[registrationName][key]; } }, @@ -2545,7 +2732,7 @@ var EventPluginHub = { }; module.exports = EventPluginHub; -},{"./EventPluginRegistry":18,"./EventPluginUtils":19,"./ReactErrorUtils":64,"./accumulateInto":111,"./forEachAccumulated":120,"./reactProdInvariant":135,"fbjs/lib/invariant":157}],18:[function(require,module,exports){ +},{"./EventPluginRegistry":19,"./EventPluginUtils":20,"./ReactErrorUtils":64,"./accumulateInto":113,"./forEachAccumulated":122,"./reactProdInvariant":137,"fbjs/lib/invariant":159}],19:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -2793,7 +2980,7 @@ var EventPluginRegistry = { }; module.exports = EventPluginRegistry; -},{"./reactProdInvariant":135,"fbjs/lib/invariant":157}],19:[function(require,module,exports){ +},{"./reactProdInvariant":137,"fbjs/lib/invariant":159}],20:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -3023,7 +3210,7 @@ var EventPluginUtils = { }; module.exports = EventPluginUtils; -},{"./EventConstants":16,"./ReactErrorUtils":64,"./reactProdInvariant":135,"fbjs/lib/invariant":157,"fbjs/lib/warning":167}],20:[function(require,module,exports){ +},{"./EventConstants":17,"./ReactErrorUtils":64,"./reactProdInvariant":137,"fbjs/lib/invariant":159,"fbjs/lib/warning":168}],21:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -3161,7 +3348,7 @@ var EventPropagators = { }; module.exports = EventPropagators; -},{"./EventConstants":16,"./EventPluginHub":17,"./EventPluginUtils":19,"./accumulateInto":111,"./forEachAccumulated":120,"fbjs/lib/warning":167}],21:[function(require,module,exports){ +},{"./EventConstants":17,"./EventPluginHub":18,"./EventPluginUtils":20,"./accumulateInto":113,"./forEachAccumulated":122,"fbjs/lib/warning":168}],22:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -3257,7 +3444,7 @@ _assign(FallbackCompositionState.prototype, { PooledClass.addPoolingTo(FallbackCompositionState); module.exports = FallbackCompositionState; -},{"./PooledClass":25,"./getTextContentAccessor":128,"object-assign":168}],22:[function(require,module,exports){ +},{"./PooledClass":26,"./getTextContentAccessor":130,"object-assign":169}],23:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -3292,6 +3479,8 @@ var HTMLDOMPropertyConfig = { allowFullScreen: HAS_BOOLEAN_VALUE, allowTransparency: 0, alt: 0, + // specifies target context for links with `preload` type + as: 0, async: HAS_BOOLEAN_VALUE, autoComplete: 0, // autoFocus is polyfilled/normalized by AutoFocusUtils @@ -3372,11 +3561,13 @@ var HTMLDOMPropertyConfig = { optimum: 0, pattern: 0, placeholder: 0, + playsInline: HAS_BOOLEAN_VALUE, poster: 0, preload: 0, profile: 0, radioGroup: 0, readOnly: HAS_BOOLEAN_VALUE, + referrerPolicy: 0, rel: 0, required: HAS_BOOLEAN_VALUE, reversed: HAS_BOOLEAN_VALUE, @@ -3466,7 +3657,7 @@ var HTMLDOMPropertyConfig = { }; module.exports = HTMLDOMPropertyConfig; -},{"./DOMProperty":10}],23:[function(require,module,exports){ +},{"./DOMProperty":11}],24:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -3526,7 +3717,7 @@ var KeyEscapeUtils = { }; module.exports = KeyEscapeUtils; -},{}],24:[function(require,module,exports){ +},{}],25:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -3544,6 +3735,7 @@ var _prodInvariant = require('./reactProdInvariant'); var ReactPropTypes = require('./ReactPropTypes'); var ReactPropTypeLocations = require('./ReactPropTypeLocations'); +var ReactPropTypesSecret = require('./ReactPropTypesSecret'); var invariant = require('fbjs/lib/invariant'); var warning = require('fbjs/lib/warning'); @@ -3606,7 +3798,7 @@ var LinkedValueUtils = { checkPropTypes: function (tagName, props, owner) { for (var propName in propTypes) { if (propTypes.hasOwnProperty(propName)) { - var error = propTypes[propName](props, propName, tagName, ReactPropTypeLocations.prop); + var error = propTypes[propName](props, propName, tagName, ReactPropTypeLocations.prop, null, ReactPropTypesSecret); } if (error instanceof Error && !(error.message in loggedTypeFailures)) { // Only monitor this failure once because there tends to be a lot of the @@ -3662,7 +3854,7 @@ var LinkedValueUtils = { }; module.exports = LinkedValueUtils; -},{"./ReactPropTypeLocations":83,"./ReactPropTypes":84,"./reactProdInvariant":135,"fbjs/lib/invariant":157,"fbjs/lib/warning":167}],25:[function(require,module,exports){ +},{"./ReactPropTypeLocations":83,"./ReactPropTypes":84,"./ReactPropTypesSecret":85,"./reactProdInvariant":137,"fbjs/lib/invariant":159,"fbjs/lib/warning":168}],26:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -3784,7 +3976,7 @@ var PooledClass = { }; module.exports = PooledClass; -},{"./reactProdInvariant":135,"fbjs/lib/invariant":157}],26:[function(require,module,exports){ +},{"./reactProdInvariant":137,"fbjs/lib/invariant":159}],27:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -3802,6 +3994,7 @@ var _assign = require('object-assign'); var ReactChildren = require('./ReactChildren'); var ReactComponent = require('./ReactComponent'); +var ReactPureComponent = require('./ReactPureComponent'); var ReactClass = require('./ReactClass'); var ReactDOMFactories = require('./ReactDOMFactories'); var ReactElement = require('./ReactElement'); @@ -3846,6 +4039,7 @@ var React = { }, Component: ReactComponent, + PureComponent: ReactPureComponent, createElement: createElement, cloneElement: cloneElement, @@ -3872,7 +4066,7 @@ var React = { }; module.exports = React; -},{"./ReactChildren":29,"./ReactClass":30,"./ReactComponent":31,"./ReactDOMFactories":45,"./ReactElement":61,"./ReactElementValidator":62,"./ReactPropTypes":84,"./ReactVersion":92,"./onlyChild":133,"fbjs/lib/warning":167,"object-assign":168}],27:[function(require,module,exports){ +},{"./ReactChildren":30,"./ReactClass":32,"./ReactComponent":33,"./ReactDOMFactories":46,"./ReactElement":61,"./ReactElementValidator":62,"./ReactPropTypes":84,"./ReactPureComponent":86,"./ReactVersion":94,"./onlyChild":135,"fbjs/lib/warning":168,"object-assign":169}],28:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -4165,6 +4359,19 @@ var ReactBrowserEventEmitter = _assign({}, ReactEventEmitterMixin, { return ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(topLevelType, handlerBaseName, handle); }, + /** + * Protect against document.createEvent() returning null + * Some popup blocker extensions appear to do this: + * https://github.com/facebook/react/issues/6887 + */ + supportsEventPageXY: function () { + if (!document.createEvent) { + return false; + } + var ev = document.createEvent('MouseEvent'); + return ev != null && 'pageX' in ev; + }, + /** * Listens to window scroll and resize events. We cache scroll values so that * application code can access them without triggering reflows. @@ -4178,7 +4385,7 @@ var ReactBrowserEventEmitter = _assign({}, ReactEventEmitterMixin, { */ ensureScrollValueMonitoring: function () { if (hasEventPageXY === undefined) { - hasEventPageXY = document.createEvent && 'pageX' in document.createEvent('MouseEvent'); + hasEventPageXY = ReactBrowserEventEmitter.supportsEventPageXY(); } if (!hasEventPageXY && !isMonitoringScrollValue) { var refresh = ViewportMetrics.refreshScrollValues; @@ -4190,7 +4397,8 @@ var ReactBrowserEventEmitter = _assign({}, ReactEventEmitterMixin, { }); module.exports = ReactBrowserEventEmitter; -},{"./EventConstants":16,"./EventPluginRegistry":18,"./ReactEventEmitterMixin":65,"./ViewportMetrics":110,"./getVendorPrefixedEventName":129,"./isEventSupported":131,"object-assign":168}],28:[function(require,module,exports){ +},{"./EventConstants":17,"./EventPluginRegistry":19,"./ReactEventEmitterMixin":65,"./ViewportMetrics":112,"./getVendorPrefixedEventName":131,"./isEventSupported":133,"object-assign":169}],29:[function(require,module,exports){ +(function (process){ /** * Copyright 2014-present, Facebook, Inc. * All rights reserved. @@ -4212,12 +4420,27 @@ var shouldUpdateReactComponent = require('./shouldUpdateReactComponent'); var traverseAllChildren = require('./traverseAllChildren'); var warning = require('fbjs/lib/warning'); +var ReactComponentTreeHook; + +if (typeof process !== 'undefined' && process.env && "production" === 'test') { + // Temporary hack. + // Inline requires don't work well with Jest: + // https://github.com/facebook/react/issues/7240 + // Remove the inline requires when we don't need them anymore: + // https://github.com/facebook/react/pull/7178 + ReactComponentTreeHook = require('./ReactComponentTreeHook'); +} + function instantiateChild(childInstances, child, name, selfDebugID) { // We found a component instance. var keyUnique = childInstances[name] === undefined; if ("production" !== 'production') { - var ReactComponentTreeDevtool = require('./ReactComponentTreeDevtool'); - "production" !== 'production' ? warning(keyUnique, 'flattenChildren(...): Encountered two children with the same key, ' + '`%s`. Child keys must be unique; when two children share a key, only ' + 'the first child will be used.%s', KeyEscapeUtils.unescape(name), ReactComponentTreeDevtool.getStackAddendumByID(selfDebugID)) : void 0; + if (!ReactComponentTreeHook) { + ReactComponentTreeHook = require('./ReactComponentTreeHook'); + } + if (!keyUnique) { + "production" !== 'production' ? warning(false, 'flattenChildren(...): Encountered two children with the same key, ' + '`%s`. Child keys must be unique; when two children share a key, only ' + 'the first child will be used.%s', KeyEscapeUtils.unescape(name), ReactComponentTreeHook.getStackAddendumByID(selfDebugID)) : void 0; + } } if (child != null && keyUnique) { childInstances[name] = instantiateReactComponent(child, true); @@ -4238,7 +4461,7 @@ var ReactChildReconciler = { * @return {?object} A set of child instances. * @internal */ - instantiateChildren: function (nestedChildNodes, transaction, context, selfDebugID // __DEV__ only + instantiateChildren: function (nestedChildNodes, transaction, context, selfDebugID // 0 in production and for roots ) { if (nestedChildNodes == null) { return null; @@ -4265,7 +4488,8 @@ var ReactChildReconciler = { * @return {?object} A new set of child instances. * @internal */ - updateChildren: function (prevChildren, nextChildren, removedNodes, transaction, context) { + updateChildren: function (prevChildren, nextChildren, mountImages, removedNodes, transaction, hostParent, hostContainerInfo, context, selfDebugID // 0 in production and for roots + ) { // We currently don't have a way to track moves here but if we use iterators // instead of for..in we can zip the iterators and check if an item has // moved. @@ -4294,6 +4518,10 @@ var ReactChildReconciler = { // The child must be instantiated before it's mounted. var nextChildInstance = instantiateReactComponent(nextElement, true); nextChildren[name] = nextChildInstance; + // Creating mount image now ensures refs are resolved in right order + // (see https://github.com/facebook/react/pull/7101 for explanation). + var nextChildMountImage = ReactReconciler.mountComponent(nextChildInstance, transaction, hostParent, hostContainerInfo, context, selfDebugID); + mountImages.push(nextChildMountImage); } } // Unmount children that are no longer present. @@ -4325,7 +4553,8 @@ var ReactChildReconciler = { }; module.exports = ReactChildReconciler; -},{"./KeyEscapeUtils":23,"./ReactComponentTreeDevtool":34,"./ReactReconciler":86,"./instantiateReactComponent":130,"./shouldUpdateReactComponent":139,"./traverseAllChildren":140,"fbjs/lib/warning":167}],29:[function(require,module,exports){ +}).call(this,require('_process')) +},{"./KeyEscapeUtils":24,"./ReactComponentTreeHook":36,"./ReactReconciler":88,"./instantiateReactComponent":132,"./shouldUpdateReactComponent":141,"./traverseAllChildren":142,"_process":1,"fbjs/lib/warning":168}],30:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -4517,7 +4746,62 @@ var ReactChildren = { }; module.exports = ReactChildren; -},{"./PooledClass":25,"./ReactElement":61,"./traverseAllChildren":140,"fbjs/lib/emptyFunction":149}],30:[function(require,module,exports){ +},{"./PooledClass":26,"./ReactElement":61,"./traverseAllChildren":142,"fbjs/lib/emptyFunction":151}],31:[function(require,module,exports){ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactChildrenMutationWarningHook + */ + +'use strict'; + +var ReactComponentTreeHook = require('./ReactComponentTreeHook'); + +var warning = require('fbjs/lib/warning'); + +function handleElement(debugID, element) { + if (element == null) { + return; + } + if (element._shadowChildren === undefined) { + return; + } + if (element._shadowChildren === element.props.children) { + return; + } + var isMutated = false; + if (Array.isArray(element._shadowChildren)) { + if (element._shadowChildren.length === element.props.children.length) { + for (var i = 0; i < element._shadowChildren.length; i++) { + if (element._shadowChildren[i] !== element.props.children[i]) { + isMutated = true; + } + } + } else { + isMutated = true; + } + } + if (!Array.isArray(element._shadowChildren) || isMutated) { + "production" !== 'production' ? warning(false, 'Component\'s children should not be mutated.%s', ReactComponentTreeHook.getStackAddendumByID(debugID)) : void 0; + } +} + +var ReactChildrenMutationWarningHook = { + onMountComponent: function (debugID) { + handleElement(debugID, ReactComponentTreeHook.getElement(debugID)); + }, + onUpdateComponent: function (debugID) { + handleElement(debugID, ReactComponentTreeHook.getElement(debugID)); + } +}; + +module.exports = ReactChildrenMutationWarningHook; +},{"./ReactComponentTreeHook":36,"fbjs/lib/warning":168}],32:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -4905,6 +5189,13 @@ function validateMethodOverride(isAlreadyDefined, name) { */ function mixSpecIntoComponent(Constructor, spec) { if (!spec) { + if ("production" !== 'production') { + var typeofSpec = typeof spec; + var isMixinValid = typeofSpec === 'object' && spec !== null; + + "production" !== 'production' ? warning(isMixinValid, '%s: You\'re attempting to include a mixin that is either null ' + 'or not an object. Check the mixins included by the component, ' + 'as well as any mixins they include themselves. ' + 'Expected object but got %s.', Constructor.displayName || 'ReactClass', spec === null ? null : typeofSpec) : void 0; + } + return; } @@ -5243,7 +5534,7 @@ var ReactClass = { }; module.exports = ReactClass; -},{"./ReactComponent":31,"./ReactElement":61,"./ReactNoopUpdateQueue":80,"./ReactPropTypeLocationNames":82,"./ReactPropTypeLocations":83,"./reactProdInvariant":135,"fbjs/lib/emptyObject":150,"fbjs/lib/invariant":157,"fbjs/lib/keyMirror":160,"fbjs/lib/keyOf":161,"fbjs/lib/warning":167,"object-assign":168}],31:[function(require,module,exports){ +},{"./ReactComponent":33,"./ReactElement":61,"./ReactNoopUpdateQueue":80,"./ReactPropTypeLocationNames":82,"./ReactPropTypeLocations":83,"./reactProdInvariant":137,"fbjs/lib/emptyObject":152,"fbjs/lib/invariant":159,"fbjs/lib/keyMirror":162,"fbjs/lib/keyOf":163,"fbjs/lib/warning":168,"object-assign":169}],33:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -5362,7 +5653,7 @@ if ("production" !== 'production') { } module.exports = ReactComponent; -},{"./ReactNoopUpdateQueue":80,"./canDefineProperty":113,"./reactProdInvariant":135,"fbjs/lib/emptyObject":150,"fbjs/lib/invariant":157,"fbjs/lib/warning":167}],32:[function(require,module,exports){ +},{"./ReactNoopUpdateQueue":80,"./canDefineProperty":115,"./reactProdInvariant":137,"fbjs/lib/emptyObject":152,"fbjs/lib/invariant":159,"fbjs/lib/warning":168}],34:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -5388,21 +5679,12 @@ var ReactComponentBrowserEnvironment = { processChildrenUpdates: ReactDOMIDOperations.dangerouslyProcessChildrenUpdates, - replaceNodeWithMarkup: DOMChildrenOperations.dangerouslyReplaceNodeWithMarkup, - - /** - * If a particular environment requires that some resources be cleaned up, - * specify this in the injected Mixin. In the DOM, we would likely want to - * purge any cached node ID lookups. - * - * @private - */ - unmountIDFromEnvironment: function (rootNodeID) {} + replaceNodeWithMarkup: DOMChildrenOperations.dangerouslyReplaceNodeWithMarkup }; module.exports = ReactComponentBrowserEnvironment; -},{"./DOMChildrenOperations":7,"./ReactDOMIDOperations":47}],33:[function(require,module,exports){ +},{"./DOMChildrenOperations":8,"./ReactDOMIDOperations":48}],35:[function(require,module,exports){ /** * Copyright 2014-present, Facebook, Inc. * All rights reserved. @@ -5424,13 +5706,6 @@ var injected = false; var ReactComponentEnvironment = { - /** - * Optionally injectable environment dependent cleanup hook. (server vs. - * browser etc). Example: A browser system caches DOM nodes based on component - * ID and must remove that cache entry when this instance is unmounted. - */ - unmountIDFromEnvironment: null, - /** * Optionally injectable hook for swapping out mount images in the middle of * the tree. @@ -5446,7 +5721,6 @@ var ReactComponentEnvironment = { injection: { injectEnvironment: function (environment) { !!injected ? "production" !== 'production' ? invariant(false, 'ReactCompositeComponent: injectEnvironment() can only be called once.') : _prodInvariant('104') : void 0; - ReactComponentEnvironment.unmountIDFromEnvironment = environment.unmountIDFromEnvironment; ReactComponentEnvironment.replaceNodeWithMarkup = environment.replaceNodeWithMarkup; ReactComponentEnvironment.processChildrenUpdates = environment.processChildrenUpdates; injected = true; @@ -5456,7 +5730,7 @@ var ReactComponentEnvironment = { }; module.exports = ReactComponentEnvironment; -},{"./reactProdInvariant":135,"fbjs/lib/invariant":157}],34:[function(require,module,exports){ +},{"./reactProdInvariant":137,"fbjs/lib/invariant":159}],36:[function(require,module,exports){ /** * Copyright 2016-present, Facebook, Inc. * All rights reserved. @@ -5465,7 +5739,7 @@ module.exports = ReactComponentEnvironment; * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @providesModule ReactComponentTreeDevtool + * @providesModule ReactComponentTreeHook */ 'use strict'; @@ -5477,32 +5751,138 @@ var ReactCurrentOwner = require('./ReactCurrentOwner'); var invariant = require('fbjs/lib/invariant'); var warning = require('fbjs/lib/warning'); -var tree = {}; -var unmountedIDs = {}; -var rootIDs = {}; +function isNative(fn) { + // Based on isNative() from Lodash + var funcToString = Function.prototype.toString; + var hasOwnProperty = Object.prototype.hasOwnProperty; + var reIsNative = RegExp('^' + funcToString + // Take an example native function source for comparison + .call(hasOwnProperty) + // Strip regex characters so we can use it for regex + .replace(/[\\^$.*+?()[\]{}|]/g, '\\$&') + // Remove hasOwnProperty from the template to make it generic + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'); + try { + var source = funcToString.call(fn); + return reIsNative.test(source); + } catch (err) { + return false; + } +} -function updateTree(id, update) { - if (!tree[id]) { - tree[id] = { - element: null, - parentID: null, - ownerID: null, - text: null, - childIDs: [], - displayName: 'Unknown', - isMounted: false, - updateCount: 0 - }; +var canUseCollections = +// Array.from +typeof Array.from === 'function' && +// Map +typeof Map === 'function' && isNative(Map) && +// Map.prototype.keys +Map.prototype != null && typeof Map.prototype.keys === 'function' && isNative(Map.prototype.keys) && +// Set +typeof Set === 'function' && isNative(Set) && +// Set.prototype.keys +Set.prototype != null && typeof Set.prototype.keys === 'function' && isNative(Set.prototype.keys); + +var itemMap; +var rootIDSet; + +var itemByKey; +var rootByKey; + +if (canUseCollections) { + itemMap = new Map(); + rootIDSet = new Set(); +} else { + itemByKey = {}; + rootByKey = {}; +} + +var unmountedIDs = []; + +// Use non-numeric keys to prevent V8 performance issues: +// https://github.com/facebook/react/pull/7232 +function getKeyFromID(id) { + return '.' + id; +} +function getIDFromKey(key) { + return parseInt(key.substr(1), 10); +} + +function get(id) { + if (canUseCollections) { + return itemMap.get(id); + } else { + var key = getKeyFromID(id); + return itemByKey[key]; + } +} + +function remove(id) { + if (canUseCollections) { + itemMap['delete'](id); + } else { + var key = getKeyFromID(id); + delete itemByKey[key]; + } +} + +function create(id, element, parentID) { + var item = { + element: element, + parentID: parentID, + text: null, + childIDs: [], + isMounted: false, + updateCount: 0 + }; + + if (canUseCollections) { + itemMap.set(id, item); + } else { + var key = getKeyFromID(id); + itemByKey[key] = item; + } +} + +function addRoot(id) { + if (canUseCollections) { + rootIDSet.add(id); + } else { + var key = getKeyFromID(id); + rootByKey[key] = true; + } +} + +function removeRoot(id) { + if (canUseCollections) { + rootIDSet['delete'](id); + } else { + var key = getKeyFromID(id); + delete rootByKey[key]; + } +} + +function getRegisteredIDs() { + if (canUseCollections) { + return Array.from(itemMap.keys()); + } else { + return Object.keys(itemByKey).map(getIDFromKey); + } +} + +function getRootIDs() { + if (canUseCollections) { + return Array.from(rootIDSet.keys()); + } else { + return Object.keys(rootByKey).map(getIDFromKey); } - update(tree[id]); } function purgeDeep(id) { - var item = tree[id]; + var item = get(id); if (item) { var childIDs = item.childIDs; - delete tree[id]; + remove(id); childIDs.forEach(purgeDeep); } } @@ -5511,102 +5891,109 @@ function describeComponentFrame(name, source, ownerName) { return '\n in ' + name + (source ? ' (at ' + source.fileName.replace(/^.*[\\\/]/, '') + ':' + source.lineNumber + ')' : ownerName ? ' (created by ' + ownerName + ')' : ''); } +function getDisplayName(element) { + if (element == null) { + return '#empty'; + } else if (typeof element === 'string' || typeof element === 'number') { + return '#text'; + } else if (typeof element.type === 'string') { + return element.type; + } else { + return element.type.displayName || element.type.name || 'Unknown'; + } +} + function describeID(id) { - var name = ReactComponentTreeDevtool.getDisplayName(id); - var element = ReactComponentTreeDevtool.getElement(id); - var ownerID = ReactComponentTreeDevtool.getOwnerID(id); + var name = ReactComponentTreeHook.getDisplayName(id); + var element = ReactComponentTreeHook.getElement(id); + var ownerID = ReactComponentTreeHook.getOwnerID(id); var ownerName; if (ownerID) { - ownerName = ReactComponentTreeDevtool.getDisplayName(ownerID); + ownerName = ReactComponentTreeHook.getDisplayName(ownerID); } - "production" !== 'production' ? warning(element, 'ReactComponentTreeDevtool: Missing React element for debugID %s when ' + 'building stack', id) : void 0; + "production" !== 'production' ? warning(element, 'ReactComponentTreeHook: Missing React element for debugID %s when ' + 'building stack', id) : void 0; return describeComponentFrame(name, element && element._source, ownerName); } -var ReactComponentTreeDevtool = { - onSetDisplayName: function (id, displayName) { - updateTree(id, function (item) { - return item.displayName = displayName; - }); - }, +var ReactComponentTreeHook = { onSetChildren: function (id, nextChildIDs) { - updateTree(id, function (item) { - item.childIDs = nextChildIDs; - - nextChildIDs.forEach(function (nextChildID) { - var nextChild = tree[nextChildID]; - !nextChild ? "production" !== 'production' ? invariant(false, 'Expected devtool events to fire for the child before its parent includes it in onSetChildren().') : _prodInvariant('68') : void 0; - !(nextChild.displayName != null) ? "production" !== 'production' ? invariant(false, 'Expected onSetDisplayName() to fire for the child before its parent includes it in onSetChildren().') : _prodInvariant('69') : void 0; - !(nextChild.childIDs != null || nextChild.text != null) ? "production" !== 'production' ? invariant(false, 'Expected onSetChildren() or onSetText() to fire for the child before its parent includes it in onSetChildren().') : _prodInvariant('70') : void 0; - !nextChild.isMounted ? "production" !== 'production' ? invariant(false, 'Expected onMountComponent() to fire for the child before its parent includes it in onSetChildren().') : _prodInvariant('71') : void 0; - if (nextChild.parentID == null) { - nextChild.parentID = id; - // TODO: This shouldn't be necessary but mounting a new root during in - // componentWillMount currently causes not-yet-mounted components to - // be purged from our tree data so their parent ID is missing. - } - !(nextChild.parentID === id) ? "production" !== 'production' ? invariant(false, 'Expected onSetParent() and onSetChildren() to be consistent (%s has parents %s and %s).', nextChildID, nextChild.parentID, id) : _prodInvariant('72', nextChildID, nextChild.parentID, id) : void 0; - }); - }); - }, - onSetOwner: function (id, ownerID) { - updateTree(id, function (item) { - return item.ownerID = ownerID; - }); - }, - onSetParent: function (id, parentID) { - updateTree(id, function (item) { - return item.parentID = parentID; - }); - }, - onSetText: function (id, text) { - updateTree(id, function (item) { - return item.text = text; - }); + var item = get(id); + item.childIDs = nextChildIDs; + + for (var i = 0; i < nextChildIDs.length; i++) { + var nextChildID = nextChildIDs[i]; + var nextChild = get(nextChildID); + !nextChild ? "production" !== 'production' ? invariant(false, 'Expected hook events to fire for the child before its parent includes it in onSetChildren().') : _prodInvariant('140') : void 0; + !(nextChild.childIDs != null || typeof nextChild.element !== 'object' || nextChild.element == null) ? "production" !== 'production' ? invariant(false, 'Expected onSetChildren() to fire for a container child before its parent includes it in onSetChildren().') : _prodInvariant('141') : void 0; + !nextChild.isMounted ? "production" !== 'production' ? invariant(false, 'Expected onMountComponent() to fire for the child before its parent includes it in onSetChildren().') : _prodInvariant('71') : void 0; + if (nextChild.parentID == null) { + nextChild.parentID = id; + // TODO: This shouldn't be necessary but mounting a new root during in + // componentWillMount currently causes not-yet-mounted components to + // be purged from our tree data so their parent ID is missing. + } + !(nextChild.parentID === id) ? "production" !== 'production' ? invariant(false, 'Expected onBeforeMountComponent() parent and onSetChildren() to be consistent (%s has parents %s and %s).', nextChildID, nextChild.parentID, id) : _prodInvariant('142', nextChildID, nextChild.parentID, id) : void 0; + } }, - onBeforeMountComponent: function (id, element) { - updateTree(id, function (item) { - return item.element = element; - }); + onBeforeMountComponent: function (id, element, parentID) { + create(id, element, parentID); }, onBeforeUpdateComponent: function (id, element) { - updateTree(id, function (item) { - return item.element = element; - }); + var item = get(id); + if (!item || !item.isMounted) { + // We may end up here as a result of setState() in componentWillUnmount(). + // In this case, ignore the element. + return; + } + item.element = element; }, onMountComponent: function (id) { - updateTree(id, function (item) { - return item.isMounted = true; - }); - }, - onMountRootComponent: function (id) { - rootIDs[id] = true; + var item = get(id); + item.isMounted = true; + var isRoot = item.parentID === 0; + if (isRoot) { + addRoot(id); + } }, onUpdateComponent: function (id) { - updateTree(id, function (item) { - return item.updateCount++; - }); + var item = get(id); + if (!item || !item.isMounted) { + // We may end up here as a result of setState() in componentWillUnmount(). + // In this case, ignore the element. + return; + } + item.updateCount++; }, onUnmountComponent: function (id) { - updateTree(id, function (item) { - return item.isMounted = false; - }); - unmountedIDs[id] = true; - delete rootIDs[id]; + var item = get(id); + if (item) { + // We need to check if it exists. + // `item` might not exist if it is inside an error boundary, and a sibling + // error boundary child threw while mounting. Then this instance never + // got a chance to mount, but it still gets an unmounting event during + // the error boundary cleanup. + item.isMounted = false; + var isRoot = item.parentID === 0; + if (isRoot) { + removeRoot(id); + } + } + unmountedIDs.push(id); }, purgeUnmountedComponents: function () { - if (ReactComponentTreeDevtool._preventPurging) { + if (ReactComponentTreeHook._preventPurging) { // Should only be used for testing. return; } - for (var id in unmountedIDs) { + for (var i = 0; i < unmountedIDs.length; i++) { + var id = unmountedIDs[i]; purgeDeep(id); } - unmountedIDs = {}; + unmountedIDs.length = 0; }, isMounted: function (id) { - var item = tree[id]; + var item = get(id); return item ? item.isMounted : false; }, getCurrentStackAddendum: function (topElement) { @@ -5621,61 +6008,72 @@ var ReactComponentTreeDevtool = { var currentOwner = ReactCurrentOwner.current; var id = currentOwner && currentOwner._debugID; - info += ReactComponentTreeDevtool.getStackAddendumByID(id); + info += ReactComponentTreeHook.getStackAddendumByID(id); return info; }, getStackAddendumByID: function (id) { var info = ''; while (id) { info += describeID(id); - id = ReactComponentTreeDevtool.getParentID(id); + id = ReactComponentTreeHook.getParentID(id); } return info; }, getChildIDs: function (id) { - var item = tree[id]; + var item = get(id); return item ? item.childIDs : []; }, getDisplayName: function (id) { - var item = tree[id]; - return item ? item.displayName : 'Unknown'; + var element = ReactComponentTreeHook.getElement(id); + if (!element) { + return null; + } + return getDisplayName(element); }, getElement: function (id) { - var item = tree[id]; + var item = get(id); return item ? item.element : null; }, getOwnerID: function (id) { - var item = tree[id]; - return item ? item.ownerID : null; + var element = ReactComponentTreeHook.getElement(id); + if (!element || !element._owner) { + return null; + } + return element._owner._debugID; }, getParentID: function (id) { - var item = tree[id]; + var item = get(id); return item ? item.parentID : null; }, getSource: function (id) { - var item = tree[id]; + var item = get(id); var element = item ? item.element : null; var source = element != null ? element._source : null; return source; }, getText: function (id) { - var item = tree[id]; - return item ? item.text : null; + var element = ReactComponentTreeHook.getElement(id); + if (typeof element === 'string') { + return element; + } else if (typeof element === 'number') { + return '' + element; + } else { + return null; + } }, getUpdateCount: function (id) { - var item = tree[id]; + var item = get(id); return item ? item.updateCount : 0; }, - getRootIDs: function () { - return Object.keys(rootIDs); - }, - getRegisteredIDs: function () { - return Object.keys(tree); - } + + + getRegisteredIDs: getRegisteredIDs, + + getRootIDs: getRootIDs }; -module.exports = ReactComponentTreeDevtool; -},{"./ReactCurrentOwner":36,"./reactProdInvariant":135,"fbjs/lib/invariant":157,"fbjs/lib/warning":167}],35:[function(require,module,exports){ +module.exports = ReactComponentTreeHook; +},{"./ReactCurrentOwner":38,"./reactProdInvariant":137,"fbjs/lib/invariant":159,"fbjs/lib/warning":168}],37:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -5703,12 +6101,18 @@ var ReactPropTypeLocations = require('./ReactPropTypeLocations'); var ReactReconciler = require('./ReactReconciler'); var checkReactTypeSpec = require('./checkReactTypeSpec'); - var emptyObject = require('fbjs/lib/emptyObject'); var invariant = require('fbjs/lib/invariant'); +var shallowEqual = require('fbjs/lib/shallowEqual'); var shouldUpdateReactComponent = require('./shouldUpdateReactComponent'); var warning = require('fbjs/lib/warning'); +var CompositeTypes = { + ImpureClass: 0, + PureClass: 1, + StatelessFunctional: 2 +}; + function StatelessComponent(Component) {} StatelessComponent.prototype.render = function () { var Component = ReactInstanceMap.get(this)._currentElement.type; @@ -5724,30 +6128,29 @@ function warnIfInvalidElement(Component, element) { } } -function invokeComponentDidMountWithTimer() { - var publicInstance = this._instance; - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'componentDidMount'); - } - publicInstance.componentDidMount(); - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'componentDidMount'); - } +function shouldConstruct(Component) { + return !!(Component.prototype && Component.prototype.isReactComponent); } -function invokeComponentDidUpdateWithTimer(prevProps, prevState, prevContext) { - var publicInstance = this._instance; - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'componentDidUpdate'); - } - publicInstance.componentDidUpdate(prevProps, prevState, prevContext); - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'componentDidUpdate'); - } +function isPureComponent(Component) { + return !!(Component.prototype && Component.prototype.isPureReactComponent); } -function shouldConstruct(Component) { - return Component.prototype && Component.prototype.isReactComponent; +// Separated into a function to contain deoptimizations caused by try/finally. +function measureLifeCyclePerf(fn, debugID, timerType) { + if (debugID === 0) { + // Top-level wrappers (see ReactMount) and empty components (see + // ReactDOMEmptyComponent) are invisible to hooks and devtools. + // Both are implementation details that should go away in the future. + return fn(); + } + + ReactInstrumentation.debugTool.onBeginLifeCycleTimer(debugID, timerType); + try { + return fn(); + } finally { + ReactInstrumentation.debugTool.onEndLifeCycleTimer(debugID, timerType); + } } /** @@ -5799,7 +6202,8 @@ var ReactCompositeComponentMixin = { */ construct: function (element) { this._currentElement = element; - this._rootNodeID = null; + this._rootNodeID = 0; + this._compositeType = null; this._instance = null; this._hostParent = null; this._hostContainerInfo = null; @@ -5840,6 +6244,8 @@ var ReactCompositeComponentMixin = { * @internal */ mountComponent: function (transaction, hostParent, hostContainerInfo, context) { + var _this = this; + this._context = context; this._mountOrder = nextMountID++; this._hostParent = hostParent; @@ -5853,15 +6259,23 @@ var ReactCompositeComponentMixin = { var updateQueue = transaction.getUpdateQueue(); // Initialize the public class - var inst = this._constructComponent(publicProps, publicContext, updateQueue); + var doConstruct = shouldConstruct(Component); + var inst = this._constructComponent(doConstruct, publicProps, publicContext, updateQueue); var renderedElement; // Support functional components - if (!shouldConstruct(Component) && (inst == null || inst.render == null)) { + if (!doConstruct && (inst == null || inst.render == null)) { renderedElement = inst; warnIfInvalidElement(Component, renderedElement); !(inst === null || inst === false || ReactElement.isValidElement(inst)) ? "production" !== 'production' ? invariant(false, '%s(...): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.', Component.displayName || Component.name || 'Component') : _prodInvariant('105', Component.displayName || Component.name || 'Component') : void 0; inst = new StatelessComponent(Component); + this._compositeType = CompositeTypes.StatelessFunctional; + } else { + if (isPureComponent(Component)) { + this._compositeType = CompositeTypes.PureClass; + } else { + this._compositeType = CompositeTypes.ImpureClass; + } } if ("production" !== 'production') { @@ -5921,7 +6335,11 @@ var ReactCompositeComponentMixin = { if (inst.componentDidMount) { if ("production" !== 'production') { - transaction.getReactMountReady().enqueue(invokeComponentDidMountWithTimer, this); + transaction.getReactMountReady().enqueue(function () { + measureLifeCyclePerf(function () { + return inst.componentDidMount(); + }, _this._debugID, 'componentDidMount'); + }); } else { transaction.getReactMountReady().enqueue(inst.componentDidMount, inst); } @@ -5930,50 +6348,41 @@ var ReactCompositeComponentMixin = { return markup; }, - _constructComponent: function (publicProps, publicContext, updateQueue) { + _constructComponent: function (doConstruct, publicProps, publicContext, updateQueue) { if ("production" !== 'production') { ReactCurrentOwner.current = this; try { - return this._constructComponentWithoutOwner(publicProps, publicContext, updateQueue); + return this._constructComponentWithoutOwner(doConstruct, publicProps, publicContext, updateQueue); } finally { ReactCurrentOwner.current = null; } } else { - return this._constructComponentWithoutOwner(publicProps, publicContext, updateQueue); + return this._constructComponentWithoutOwner(doConstruct, publicProps, publicContext, updateQueue); } }, - _constructComponentWithoutOwner: function (publicProps, publicContext, updateQueue) { + _constructComponentWithoutOwner: function (doConstruct, publicProps, publicContext, updateQueue) { var Component = this._currentElement.type; - var instanceOrElement; - if (shouldConstruct(Component)) { - if ("production" !== 'production') { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'ctor'); - } - } - instanceOrElement = new Component(publicProps, publicContext, updateQueue); + + if (doConstruct) { if ("production" !== 'production') { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'ctor'); - } + return measureLifeCyclePerf(function () { + return new Component(publicProps, publicContext, updateQueue); + }, this._debugID, 'ctor'); + } else { + return new Component(publicProps, publicContext, updateQueue); } + } + + // This can still be an instance in case of factory components + // but we'll count this as time spent rendering as the more common case. + if ("production" !== 'production') { + return measureLifeCyclePerf(function () { + return Component(publicProps, publicContext, updateQueue); + }, this._debugID, 'render'); } else { - // This can still be an instance in case of factory components - // but we'll count this as time spent rendering as the more common case. - if ("production" !== 'production') { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'render'); - } - } - instanceOrElement = Component(publicProps, publicContext, updateQueue); - if ("production" !== 'production') { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'render'); - } - } + return Component(publicProps, publicContext, updateQueue); } - return instanceOrElement; }, performInitialMountWithErrorHandling: function (renderedElement, hostParent, hostContainerInfo, transaction, context) { @@ -5982,11 +6391,6 @@ var ReactCompositeComponentMixin = { try { markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context); } catch (e) { - if ("production" !== 'production') { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onError(); - } - } // Roll back to checkpoint, handle error (which may add items to the transaction), and take a new checkpoint transaction.rollback(checkpoint); this._instance.unstable_handleError(e); @@ -6007,17 +6411,19 @@ var ReactCompositeComponentMixin = { performInitialMount: function (renderedElement, hostParent, hostContainerInfo, transaction, context) { var inst = this._instance; + + var debugID = 0; + if ("production" !== 'production') { + debugID = this._debugID; + } + if (inst.componentWillMount) { if ("production" !== 'production') { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'componentWillMount'); - } - } - inst.componentWillMount(); - if ("production" !== 'production') { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'componentWillMount'); - } + measureLifeCyclePerf(function () { + return inst.componentWillMount(); + }, debugID, 'componentWillMount'); + } else { + inst.componentWillMount(); } // When mounting, calls to `setState` by `componentWillMount` will set // `this._pendingStateQueue` without triggering a re-render. @@ -6036,17 +6442,13 @@ var ReactCompositeComponentMixin = { var child = this._instantiateReactComponent(renderedElement, nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */ ); this._renderedComponent = child; - if ("production" !== 'production') { - if (child._debugID !== 0 && this._debugID !== 0) { - ReactInstrumentation.debugTool.onSetParent(child._debugID, this._debugID); - } - } - var markup = ReactReconciler.mountComponent(child, transaction, hostParent, hostContainerInfo, this._processChildContext(context)); + var markup = ReactReconciler.mountComponent(child, transaction, hostParent, hostContainerInfo, this._processChildContext(context), debugID); if ("production" !== 'production') { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onSetChildren(this._debugID, child._debugID !== 0 ? [child._debugID] : []); + if (debugID !== 0) { + var childDebugIDs = child._debugID !== 0 ? [child._debugID] : []; + ReactInstrumentation.debugTool.onSetChildren(debugID, childDebugIDs); } } @@ -6067,24 +6469,22 @@ var ReactCompositeComponentMixin = { if (!this._renderedComponent) { return; } + var inst = this._instance; if (inst.componentWillUnmount && !inst._calledComponentWillUnmount) { inst._calledComponentWillUnmount = true; - if ("production" !== 'production') { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'componentWillUnmount'); - } - } + if (safely) { var name = this.getName() + '.componentWillUnmount()'; ReactErrorUtils.invokeGuardedCallback(name, inst.componentWillUnmount.bind(inst)); } else { - inst.componentWillUnmount(); - } - if ("production" !== 'production') { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'componentWillUnmount'); + if ("production" !== 'production') { + measureLifeCyclePerf(function () { + return inst.componentWillUnmount(); + }, this._debugID, 'componentWillUnmount'); + } else { + inst.componentWillUnmount(); } } } @@ -6108,7 +6508,7 @@ var ReactCompositeComponentMixin = { // These fields do not really need to be reset since this object is no // longer accessible. this._context = null; - this._rootNodeID = null; + this._rootNodeID = 0; this._topLevelWrapper = null; // Delete the reference from the instance to this internal representation @@ -6171,13 +6571,21 @@ var ReactCompositeComponentMixin = { _processChildContext: function (currentContext) { var Component = this._currentElement.type; var inst = this._instance; - if ("production" !== 'production') { - ReactInstrumentation.debugTool.onBeginProcessingChildContext(); - } - var childContext = inst.getChildContext && inst.getChildContext(); - if ("production" !== 'production') { - ReactInstrumentation.debugTool.onEndProcessingChildContext(); + var childContext; + + if (inst.getChildContext) { + if ("production" !== 'production') { + ReactInstrumentation.debugTool.onBeginProcessingChildContext(); + try { + childContext = inst.getChildContext(); + } finally { + ReactInstrumentation.debugTool.onEndProcessingChildContext(); + } + } else { + childContext = inst.getChildContext(); + } } + if (childContext) { !(typeof Component.childContextTypes === 'object') ? "production" !== 'production' ? invariant(false, '%s.getChildContext(): childContextTypes must be defined in order to use getChildContext().', this.getName() || 'ReactCompositeComponent') : _prodInvariant('107', this.getName() || 'ReactCompositeComponent') : void 0; if ("production" !== 'production') { @@ -6250,7 +6658,6 @@ var ReactCompositeComponentMixin = { var willReceive = false; var nextContext; - var nextProps; // Determine if the context has changed or not if (this._context === nextUnmaskedContext) { @@ -6260,7 +6667,8 @@ var ReactCompositeComponentMixin = { willReceive = true; } - nextProps = nextParentElement.props; + var prevProps = prevParentElement.props; + var nextProps = nextParentElement.props; // Not a simple state update but a props update if (prevParentElement !== nextParentElement) { @@ -6272,31 +6680,29 @@ var ReactCompositeComponentMixin = { // immediately reconciled instead of waiting for the next batch. if (willReceive && inst.componentWillReceiveProps) { if ("production" !== 'production') { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'componentWillReceiveProps'); - } - } - inst.componentWillReceiveProps(nextProps, nextContext); - if ("production" !== 'production') { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'componentWillReceiveProps'); - } + measureLifeCyclePerf(function () { + return inst.componentWillReceiveProps(nextProps, nextContext); + }, this._debugID, 'componentWillReceiveProps'); + } else { + inst.componentWillReceiveProps(nextProps, nextContext); } } var nextState = this._processPendingState(nextProps, nextContext); var shouldUpdate = true; - if (!this._pendingForceUpdate && inst.shouldComponentUpdate) { - if ("production" !== 'production') { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'shouldComponentUpdate'); + if (!this._pendingForceUpdate) { + if (inst.shouldComponentUpdate) { + if ("production" !== 'production') { + shouldUpdate = measureLifeCyclePerf(function () { + return inst.shouldComponentUpdate(nextProps, nextState, nextContext); + }, this._debugID, 'shouldComponentUpdate'); + } else { + shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext); } - } - shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext); - if ("production" !== 'production') { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'shouldComponentUpdate'); + } else { + if (this._compositeType === CompositeTypes.PureClass) { + shouldUpdate = !shallowEqual(prevProps, nextProps) || !shallowEqual(inst.state, nextState); } } } @@ -6358,6 +6764,8 @@ var ReactCompositeComponentMixin = { * @private */ _performComponentUpdate: function (nextElement, nextProps, nextState, nextContext, transaction, unmaskedContext) { + var _this2 = this; + var inst = this._instance; var hasComponentDidUpdate = Boolean(inst.componentDidUpdate); @@ -6372,15 +6780,11 @@ var ReactCompositeComponentMixin = { if (inst.componentWillUpdate) { if ("production" !== 'production') { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'componentWillUpdate'); - } - } - inst.componentWillUpdate(nextProps, nextState, nextContext); - if ("production" !== 'production') { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'componentWillUpdate'); - } + measureLifeCyclePerf(function () { + return inst.componentWillUpdate(nextProps, nextState, nextContext); + }, this._debugID, 'componentWillUpdate'); + } else { + inst.componentWillUpdate(nextProps, nextState, nextContext); } } @@ -6394,7 +6798,9 @@ var ReactCompositeComponentMixin = { if (hasComponentDidUpdate) { if ("production" !== 'production') { - transaction.getReactMountReady().enqueue(invokeComponentDidUpdateWithTimer.bind(this, prevProps, prevState, prevContext), this); + transaction.getReactMountReady().enqueue(function () { + measureLifeCyclePerf(inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext), _this2._debugID, 'componentDidUpdate'); + }); } else { transaction.getReactMountReady().enqueue(inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext), inst); } @@ -6411,6 +6817,12 @@ var ReactCompositeComponentMixin = { var prevComponentInstance = this._renderedComponent; var prevRenderedElement = prevComponentInstance._currentElement; var nextRenderedElement = this._renderValidatedComponent(); + + var debugID = 0; + if ("production" !== 'production') { + debugID = this._debugID; + } + if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) { ReactReconciler.receiveComponent(prevComponentInstance, nextRenderedElement, transaction, this._processChildContext(context)); } else { @@ -6422,17 +6834,13 @@ var ReactCompositeComponentMixin = { var child = this._instantiateReactComponent(nextRenderedElement, nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */ ); this._renderedComponent = child; - if ("production" !== 'production') { - if (child._debugID !== 0 && this._debugID !== 0) { - ReactInstrumentation.debugTool.onSetParent(child._debugID, this._debugID); - } - } - var nextMarkup = ReactReconciler.mountComponent(child, transaction, this._hostParent, this._hostContainerInfo, this._processChildContext(context)); + var nextMarkup = ReactReconciler.mountComponent(child, transaction, this._hostParent, this._hostContainerInfo, this._processChildContext(context), debugID); if ("production" !== 'production') { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onSetChildren(this._debugID, child._debugID !== 0 ? [child._debugID] : []); + if (debugID !== 0) { + var childDebugIDs = child._debugID !== 0 ? [child._debugID] : []; + ReactInstrumentation.debugTool.onSetChildren(debugID, childDebugIDs); } } @@ -6454,17 +6862,14 @@ var ReactCompositeComponentMixin = { */ _renderValidatedComponentWithoutOwnerOrContext: function () { var inst = this._instance; + var renderedComponent; if ("production" !== 'production') { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'render'); - } - } - var renderedComponent = inst.render(); - if ("production" !== 'production') { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'render'); - } + renderedComponent = measureLifeCyclePerf(function () { + return inst.render(); + }, this._debugID, 'render'); + } else { + renderedComponent = inst.render(); } if ("production" !== 'production') { @@ -6484,11 +6889,15 @@ var ReactCompositeComponentMixin = { */ _renderValidatedComponent: function () { var renderedComponent; - ReactCurrentOwner.current = this; - try { + if ("production" !== 'production' || this._compositeType !== CompositeTypes.StatelessFunctional) { + ReactCurrentOwner.current = this; + try { + renderedComponent = this._renderValidatedComponentWithoutOwnerOrContext(); + } finally { + ReactCurrentOwner.current = null; + } + } else { renderedComponent = this._renderValidatedComponentWithoutOwnerOrContext(); - } finally { - ReactCurrentOwner.current = null; } !( // TODO: An `isValidNode` function would probably be more appropriate @@ -6511,7 +6920,7 @@ var ReactCompositeComponentMixin = { var publicComponentInstance = component.getPublicInstance(); if ("production" !== 'production') { var componentName = component && component.getName ? component.getName() : 'a component'; - "production" !== 'production' ? warning(publicComponentInstance != null, 'Stateless function components cannot be given refs ' + '(See ref "%s" in %s created by %s). ' + 'Attempts to access this ref will fail.', ref, componentName, this.getName()) : void 0; + "production" !== 'production' ? warning(publicComponentInstance != null || component._compositeType !== CompositeTypes.StatelessFunctional, 'Stateless function components cannot be given refs ' + '(See ref "%s" in %s created by %s). ' + 'Attempts to access this ref will fail.', ref, componentName, this.getName()) : void 0; } var refs = inst.refs === emptyObject ? inst.refs = {} : inst.refs; refs[ref] = publicComponentInstance; @@ -6551,7 +6960,7 @@ var ReactCompositeComponentMixin = { */ getPublicInstance: function () { var inst = this._instance; - if (inst instanceof StatelessComponent) { + if (this._compositeType === CompositeTypes.StatelessFunctional) { return null; } return inst; @@ -6569,7 +6978,7 @@ var ReactCompositeComponent = { }; module.exports = ReactCompositeComponent; -},{"./ReactComponentEnvironment":33,"./ReactCurrentOwner":36,"./ReactElement":61,"./ReactErrorUtils":64,"./ReactInstanceMap":72,"./ReactInstrumentation":73,"./ReactNodeTypes":79,"./ReactPropTypeLocations":83,"./ReactReconciler":86,"./checkReactTypeSpec":114,"./reactProdInvariant":135,"./shouldUpdateReactComponent":139,"fbjs/lib/emptyObject":150,"fbjs/lib/invariant":157,"fbjs/lib/warning":167,"object-assign":168}],36:[function(require,module,exports){ +},{"./ReactComponentEnvironment":35,"./ReactCurrentOwner":38,"./ReactElement":61,"./ReactErrorUtils":64,"./ReactInstanceMap":72,"./ReactInstrumentation":73,"./ReactNodeTypes":79,"./ReactPropTypeLocations":83,"./ReactReconciler":88,"./checkReactTypeSpec":116,"./reactProdInvariant":137,"./shouldUpdateReactComponent":141,"fbjs/lib/emptyObject":152,"fbjs/lib/invariant":159,"fbjs/lib/shallowEqual":167,"fbjs/lib/warning":168,"object-assign":169}],38:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -6601,7 +7010,7 @@ var ReactCurrentOwner = { }; module.exports = ReactCurrentOwner; -},{}],37:[function(require,module,exports){ +},{}],39:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -6631,7 +7040,7 @@ var warning = require('fbjs/lib/warning'); ReactDefaultInjection.inject(); -var React = { +var ReactDOM = { findDOMNode: findDOMNode, render: ReactMount.render, unmountComponentAtNode: ReactMount.unmountComponentAtNode, @@ -6702,8 +7111,17 @@ if ("production" !== 'production') { } } -module.exports = React; -},{"./ReactDOMComponentTree":41,"./ReactDefaultInjection":60,"./ReactMount":76,"./ReactReconciler":86,"./ReactUpdates":91,"./ReactVersion":92,"./findDOMNode":118,"./getHostComponentFromComposite":125,"./renderSubtreeIntoContainer":136,"fbjs/lib/ExecutionEnvironment":143,"fbjs/lib/warning":167}],38:[function(require,module,exports){ +if ("production" !== 'production') { + var ReactInstrumentation = require('./ReactInstrumentation'); + var ReactDOMUnknownPropertyHook = require('./ReactDOMUnknownPropertyHook'); + var ReactDOMNullInputValuePropHook = require('./ReactDOMNullInputValuePropHook'); + + ReactInstrumentation.debugTool.addHook(ReactDOMUnknownPropertyHook); + ReactInstrumentation.debugTool.addHook(ReactDOMNullInputValuePropHook); +} + +module.exports = ReactDOM; +},{"./ReactDOMComponentTree":43,"./ReactDOMNullInputValuePropHook":50,"./ReactDOMUnknownPropertyHook":57,"./ReactDefaultInjection":60,"./ReactInstrumentation":73,"./ReactMount":76,"./ReactReconciler":88,"./ReactUpdates":93,"./ReactVersion":94,"./findDOMNode":120,"./getHostComponentFromComposite":127,"./renderSubtreeIntoContainer":138,"fbjs/lib/ExecutionEnvironment":145,"fbjs/lib/warning":168}],40:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -6728,7 +7146,7 @@ var ReactDOMButton = { }; module.exports = ReactDOMButton; -},{"./DisabledInputUtils":14}],39:[function(require,module,exports){ +},{"./DisabledInputUtils":15}],41:[function(require,module,exports){ /** * Copyright 2013-present, Facebook, Inc. * All rights reserved. @@ -6757,7 +7175,6 @@ var EventConstants = require('./EventConstants'); var EventPluginHub = require('./EventPluginHub'); var EventPluginRegistry = require('./EventPluginRegistry'); var ReactBrowserEventEmitter = require('./ReactBrowserEventEmitter'); -var ReactComponentBrowserEnvironment = require('./ReactComponentBrowserEnvironment'); var ReactDOMButton = require('./ReactDOMButton'); var ReactDOMComponentFlags = require('./ReactDOMComponentFlags'); var ReactDOMComponentTree = require('./ReactDOMComponentTree'); @@ -6927,12 +7344,13 @@ function optionPostMount() { ReactDOMOption.postMountWrapper(inst); } -var setContentChildForInstrumentation = emptyFunction; +var setAndValidateContentChildDev = emptyFunction; if ("production" !== 'production') { - setContentChildForInstrumentation = function (content) { + setAndValidateContentChildDev = function (content) { var hasExistingContent = this._contentDebugID != null; var debugID = this._debugID; - var contentDebugID = debugID + '#text'; + // This ID represents the inlined child that has no backing instance: + var contentDebugID = -debugID; if (content == null) { if (hasExistingContent) { @@ -6942,18 +7360,13 @@ if ("production" !== 'production') { return; } + validateDOMNesting(null, String(content), this, this._ancestorInfo); this._contentDebugID = contentDebugID; - var text = '' + content; - - ReactInstrumentation.debugTool.onSetDisplayName(contentDebugID, '#text'); - ReactInstrumentation.debugTool.onSetParent(contentDebugID, debugID); - ReactInstrumentation.debugTool.onSetText(contentDebugID, text); - if (hasExistingContent) { ReactInstrumentation.debugTool.onBeforeUpdateComponent(contentDebugID, content); ReactInstrumentation.debugTool.onUpdateComponent(contentDebugID); } else { - ReactInstrumentation.debugTool.onBeforeMountComponent(contentDebugID, content); + ReactInstrumentation.debugTool.onBeforeMountComponent(contentDebugID, content, debugID); ReactInstrumentation.debugTool.onMountComponent(contentDebugID); ReactInstrumentation.debugTool.onSetChildren(debugID, [contentDebugID]); } @@ -7114,15 +7527,15 @@ function ReactDOMComponent(element) { this._previousStyleCopy = null; this._hostNode = null; this._hostParent = null; - this._rootNodeID = null; - this._domID = null; + this._rootNodeID = 0; + this._domID = 0; this._hostContainerInfo = null; this._wrapperState = null; this._topLevelWrapper = null; this._flags = 0; if ("production" !== 'production') { this._ancestorInfo = null; - setContentChildForInstrumentation.call(this, null); + setAndValidateContentChildDev.call(this, null); } } @@ -7136,7 +7549,7 @@ ReactDOMComponent.Mixin = { * * @internal * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction - * @param {?ReactDOMComponent} the containing DOM component instance + * @param {?ReactDOMComponent} the parent component instance * @param {?object} info about the host container * @param {object} context * @return {string} The computed markup. @@ -7222,7 +7635,7 @@ ReactDOMComponent.Mixin = { if (parentInfo) { // parentInfo should always be present except for the top-level // component when server rendering - validateDOMNesting(this._tag, this, parentInfo); + validateDOMNesting(this._tag, null, this, parentInfo); } this._ancestorInfo = validateDOMNesting.updatedAncestorInfo(parentInfo, this._tag, this); } @@ -7391,7 +7804,7 @@ ReactDOMComponent.Mixin = { // TODO: Validate that text is allowed as a child of this node ret = escapeTextContentForBrowser(contentToUse); if ("production" !== 'production') { - setContentChildForInstrumentation.call(this, contentToUse); + setAndValidateContentChildDev.call(this, contentToUse); } } else if (childrenToUse != null) { var mountImages = this.mountChildren(childrenToUse, transaction, context); @@ -7428,7 +7841,7 @@ ReactDOMComponent.Mixin = { if (contentToUse != null) { // TODO: Validate that text is allowed as a child of this node if ("production" !== 'production') { - setContentChildForInstrumentation.call(this, contentToUse); + setAndValidateContentChildDev.call(this, contentToUse); } DOMLazyTree.queueText(lazyTree, contentToUse); } else if (childrenToUse != null) { @@ -7474,7 +7887,6 @@ ReactDOMComponent.Mixin = { nextProps = ReactDOMButton.getHostProps(this, nextProps); break; case 'input': - ReactDOMInput.updateWrapper(this); lastProps = ReactDOMInput.getHostProps(this, lastProps); nextProps = ReactDOMInput.getHostProps(this, nextProps); break; @@ -7487,7 +7899,6 @@ ReactDOMComponent.Mixin = { nextProps = ReactDOMSelect.getHostProps(this, nextProps); break; case 'textarea': - ReactDOMTextarea.updateWrapper(this); lastProps = ReactDOMTextarea.getHostProps(this, lastProps); nextProps = ReactDOMTextarea.getHostProps(this, nextProps); break; @@ -7497,10 +7908,21 @@ ReactDOMComponent.Mixin = { this._updateDOMProperties(lastProps, nextProps, transaction); this._updateDOMChildren(lastProps, nextProps, transaction, context); - if (this._tag === 'select') { - // value update needs to occur after