diff --git a/guide/README.md b/guide/README.md index 9dea08f9..cfa10914 100644 --- a/guide/README.md +++ b/guide/README.md @@ -157,6 +157,17 @@ You can access to select2 as follows this.refs.tags.el ``` +### Full version + +Some options are only available with [`Select2.full.js`](https://select2.org/getting-started/builds-and-modules). +To use that version instead: + +```js +import Select2 from 'react-select2-wrapper/lib/components/Select2.full'; +```` + +Everything else will be the same as before. + ## Themes Default theme in [css/select2.css](../css/select2.css) diff --git a/src/components/Select2.full.js b/src/components/Select2.full.js new file mode 100644 index 00000000..37d28915 --- /dev/null +++ b/src/components/Select2.full.js @@ -0,0 +1,3 @@ +import 'select2/dist/js/select2.full'; + +export default from './shared'; diff --git a/src/components/Select2.js b/src/components/Select2.js index fb413839..635e1255 100644 --- a/src/components/Select2.js +++ b/src/components/Select2.js @@ -1,217 +1,3 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import ReactDOM from 'react-dom'; -import shallowEqualFuzzy from 'shallow-equal-fuzzy'; -import $ from 'jquery'; import 'select2'; -const namespace = 'react-select2-wrapper'; - -export default class Select2 extends Component { - static propTypes = { - defaultValue: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.array, - PropTypes.string, - ]), - value: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.array, - PropTypes.string, - ]), - data: PropTypes.array, - events: PropTypes.array, - options: PropTypes.object, - multiple: PropTypes.bool, - onOpen: PropTypes.func, - onClose: PropTypes.func, - onSelect: PropTypes.func, - onChange: PropTypes.func, - onUnselect: PropTypes.func, - }; - - static defaultProps = { - data: [], - events: [ - [`change.${namespace}`, 'onChange'], - [`select2:open.${namespace}`, 'onOpen'], - [`select2:close.${namespace}`, 'onClose'], - [`select2:select.${namespace}`, 'onSelect'], - [`select2:unselect.${namespace}`, 'onUnselect'], - ], - options: {}, - multiple: false, - }; - - constructor(props) { - super(props); - this.el = null; - this.forceUpdateValue = false; - this.initialRender = true; - } - - componentDidMount() { - this.initSelect2(this.props); - this.updateValue(); - } - - componentWillReceiveProps(nextProps) { - this.initialRender = false; - this.updSelect2(nextProps); - } - - componentDidUpdate() { - this.updateValue(); - } - - componentWillUnmount() { - this.destroySelect2(); - } - - initSelect2(props) { - const { options } = props; - - this.el = $(ReactDOM.findDOMNode(this)); - // fix for updating selected value when data is changing - if (this.forceUpdateValue) { - this.updateSelect2Value(null); - } - this.el.select2(this.prepareOptions(options)); - this.attachEventHandlers(props); - } - - updSelect2(props) { - const prevProps = this.props; - - if (!shallowEqualFuzzy(prevProps.data, props.data)) { - this.forceUpdateValue = true; - this.destroySelect2(false); - this.initSelect2(props); - return; - } - - const { options } = props; - if (!shallowEqualFuzzy(prevProps.options, options)) { - this.el.select2(this.prepareOptions(options)); - } - - const handlerChanged = e => prevProps[e[1]] !== props[e[1]]; - if (props.events.some(handlerChanged)) { - this.detachEventHandlers(); - this.attachEventHandlers(props); - } - } - - updateSelect2Value(value) { - this.el.off(`change.${namespace}`).val(value).trigger('change'); - - const onChange = this.props.onChange; - if (onChange) { - this.el.on(`change.${namespace}`, onChange); - } - } - - updateValue() { - const { value, defaultValue, multiple } = this.props; - const newValue = this.prepareValue(value, defaultValue); - const currentValue = multiple ? this.el.val() || [] : this.el.val(); - - if (!this.fuzzyValuesEqual(currentValue, newValue) || this.forceUpdateValue) { - this.updateSelect2Value(newValue); - if (!this.initialRender) { - this.el.trigger('change'); - } - this.forceUpdateValue = false; - } - } - - fuzzyValuesEqual(currentValue, newValue) { - return (currentValue === null && newValue === '') || - shallowEqualFuzzy(currentValue, newValue); - } - - destroySelect2(withCallbacks = true) { - if (withCallbacks) { - this.detachEventHandlers(); - } - - this.el.select2('destroy'); - this.el = null; - } - - attachEventHandlers(props) { - props.events.forEach(event => { - if (typeof props[event[1]] !== 'undefined') { - this.el.on(event[0], props[event[1]]); - } - }); - } - - detachEventHandlers() { - this.props.events.forEach(event => { - if (typeof this.props[event[1]] !== 'undefined') { - this.el.off(event[0]); - } - }); - } - - prepareValue(value, defaultValue) { - const issetValue = typeof value !== 'undefined' && value !== null; - const issetDefaultValue = typeof defaultValue !== 'undefined'; - - if (!issetValue && issetDefaultValue) { - return defaultValue; - } - return value; - } - - prepareOptions(options) { - const opt = options; - if (typeof opt.dropdownParent === 'string') { - opt.dropdownParent = $(opt.dropdownParent); - } - return opt; - } - - isObject(value) { - const type = typeof value; - return type === 'function' || (value && type === 'object') || false; - } - - makeOption(item) { - if (this.isObject(item)) { - const { id, text, ...itemParams } = item; - return (); - } - - return (); - } - - render() { - const { data, value, ...props } = this.props; - - delete props.options; - delete props.events; - delete props.onOpen; - delete props.onClose; - delete props.onSelect; - delete props.onChange; - delete props.onUnselect; - - return ( - - ); - } -} +export default from './shared'; diff --git a/src/components/shared.js b/src/components/shared.js new file mode 100644 index 00000000..2d10b889 --- /dev/null +++ b/src/components/shared.js @@ -0,0 +1,216 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import ReactDOM from 'react-dom'; +import shallowEqualFuzzy from 'shallow-equal-fuzzy'; +import $ from 'jquery'; + +const namespace = 'react-select2-wrapper'; + +export default class Select2 extends Component { + static propTypes = { + defaultValue: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.array, + PropTypes.string, + ]), + value: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.array, + PropTypes.string, + ]), + data: PropTypes.array, + events: PropTypes.array, + options: PropTypes.object, + multiple: PropTypes.bool, + onOpen: PropTypes.func, + onClose: PropTypes.func, + onSelect: PropTypes.func, + onChange: PropTypes.func, + onUnselect: PropTypes.func, + }; + + static defaultProps = { + data: [], + events: [ + [`change.${namespace}`, 'onChange'], + [`select2:open.${namespace}`, 'onOpen'], + [`select2:close.${namespace}`, 'onClose'], + [`select2:select.${namespace}`, 'onSelect'], + [`select2:unselect.${namespace}`, 'onUnselect'], + ], + options: {}, + multiple: false, + }; + + constructor(props) { + super(props); + this.el = null; + this.forceUpdateValue = false; + this.initialRender = true; + } + + componentDidMount() { + this.initSelect2(this.props); + this.updateValue(); + } + + componentWillReceiveProps(nextProps) { + this.initialRender = false; + this.updSelect2(nextProps); + } + + componentDidUpdate() { + this.updateValue(); + } + + componentWillUnmount() { + this.destroySelect2(); + } + + initSelect2(props) { + const { options } = props; + + this.el = $(ReactDOM.findDOMNode(this)); + // fix for updating selected value when data is changing + if (this.forceUpdateValue) { + this.updateSelect2Value(null); + } + this.el.select2(this.prepareOptions(options)); + this.attachEventHandlers(props); + } + + updSelect2(props) { + const prevProps = this.props; + + if (!shallowEqualFuzzy(prevProps.data, props.data)) { + this.forceUpdateValue = true; + this.destroySelect2(false); + this.initSelect2(props); + return; + } + + const { options } = props; + if (!shallowEqualFuzzy(prevProps.options, options)) { + this.el.select2(this.prepareOptions(options)); + } + + const handlerChanged = e => prevProps[e[1]] !== props[e[1]]; + if (props.events.some(handlerChanged)) { + this.detachEventHandlers(); + this.attachEventHandlers(props); + } + } + + updateSelect2Value(value) { + this.el.off(`change.${namespace}`).val(value).trigger('change'); + + const onChange = this.props.onChange; + if (onChange) { + this.el.on(`change.${namespace}`, onChange); + } + } + + updateValue() { + const { value, defaultValue, multiple } = this.props; + const newValue = this.prepareValue(value, defaultValue); + const currentValue = multiple ? this.el.val() || [] : this.el.val(); + + if (!this.fuzzyValuesEqual(currentValue, newValue) || this.forceUpdateValue) { + this.updateSelect2Value(newValue); + if (!this.initialRender) { + this.el.trigger('change'); + } + this.forceUpdateValue = false; + } + } + + fuzzyValuesEqual(currentValue, newValue) { + return (currentValue === null && newValue === '') || + shallowEqualFuzzy(currentValue, newValue); + } + + destroySelect2(withCallbacks = true) { + if (withCallbacks) { + this.detachEventHandlers(); + } + + this.el.select2('destroy'); + this.el = null; + } + + attachEventHandlers(props) { + props.events.forEach(event => { + if (typeof props[event[1]] !== 'undefined') { + this.el.on(event[0], props[event[1]]); + } + }); + } + + detachEventHandlers() { + this.props.events.forEach(event => { + if (typeof this.props[event[1]] !== 'undefined') { + this.el.off(event[0]); + } + }); + } + + prepareValue(value, defaultValue) { + const issetValue = typeof value !== 'undefined' && value !== null; + const issetDefaultValue = typeof defaultValue !== 'undefined'; + + if (!issetValue && issetDefaultValue) { + return defaultValue; + } + return value; + } + + prepareOptions(options) { + const opt = options; + if (typeof opt.dropdownParent === 'string') { + opt.dropdownParent = $(opt.dropdownParent); + } + return opt; + } + + isObject(value) { + const type = typeof value; + return type === 'function' || (value && type === 'object') || false; + } + + makeOption(item) { + if (this.isObject(item)) { + const { id, text, ...itemParams } = item; + return (); + } + + return (); + } + + render() { + const { data, value, ...props } = this.props; + + delete props.options; + delete props.events; + delete props.onOpen; + delete props.onClose; + delete props.onSelect; + delete props.onChange; + delete props.onUnselect; + + return ( + + ); + } +}