diff --git a/superset/assets/branding/Full Lockup With Text.svg b/superset/assets/branding/FullLockupWithText.svg similarity index 100% rename from superset/assets/branding/Full Lockup With Text.svg rename to superset/assets/branding/FullLockupWithText.svg diff --git a/superset/assets/branding/Full Lockup With Text@2x.png b/superset/assets/branding/FullLockupWithText@2x.png similarity index 100% rename from superset/assets/branding/Full Lockup With Text@2x.png rename to superset/assets/branding/FullLockupWithText@2x.png diff --git a/superset/assets/branding/Full Lockup Without Text@1x.svg b/superset/assets/branding/FullLockupWithoutText@1x.svg similarity index 100% rename from superset/assets/branding/Full Lockup Without Text@1x.svg rename to superset/assets/branding/FullLockupWithoutText@1x.svg diff --git a/superset/assets/branding/Full Lockup Without Text@2x.png b/superset/assets/branding/FullLockupWithoutText@2x.png similarity index 100% rename from superset/assets/branding/Full Lockup Without Text@2x.png rename to superset/assets/branding/FullLockupWithoutText@2x.png diff --git a/superset/assets/branding/Solo Mark.png b/superset/assets/branding/SoloMark.png similarity index 100% rename from superset/assets/branding/Solo Mark.png rename to superset/assets/branding/SoloMark.png diff --git a/superset/assets/branding/Solo Mark@1x.svg b/superset/assets/branding/SoloMark@1x.svg similarity index 100% rename from superset/assets/branding/Solo Mark@1x.svg rename to superset/assets/branding/SoloMark@1x.svg diff --git a/superset/assets/javascripts/components/OnPasteSelect.jsx b/superset/assets/javascripts/components/OnPasteSelect.jsx new file mode 100644 index 0000000000000..b043bf317d175 --- /dev/null +++ b/superset/assets/javascripts/components/OnPasteSelect.jsx @@ -0,0 +1,87 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Select from 'react-select'; + +export default class OnPasteSelect extends React.Component { + onPaste(evt) { + if (!this.props.multi) { + return; + } + evt.preventDefault(); + const clipboard = evt.clipboardData.getData('Text'); + if (!clipboard) { + return; + } + const regex = `[${this.props.separator}]+`; + const values = clipboard.split(new RegExp(regex)).map(v => v.trim()); + const validator = this.props.isValidNewOption; + const selected = this.props.value || []; + const existingOptions = {}; + const existing = {}; + this.props.options.forEach((v) => { + existingOptions[v[this.props.valueKey]] = 1; + }); + let options = []; + selected.forEach((v) => { + options.push({ [this.props.labelKey]: v, [this.props.valueKey]: v }); + existing[v] = 1; + }); + options = options.concat(values + .filter((v) => { + const notExists = !existing[v]; + existing[v] = 1; + return notExists && (validator ? validator({ [this.props.labelKey]: v }) : !!v); + }) + .map((v) => { + const opt = { [this.props.labelKey]: v, [this.props.valueKey]: v }; + if (!existingOptions[v]) { + this.props.options.unshift(opt); + } + return opt; + }), + ); + if (options.length) { + if (this.props.onChange) { + this.props.onChange(options); + } + } + } + render() { + const SelectComponent = this.props.selectWrap; + const refFunc = (ref) => { + if (this.props.ref) { + this.props.ref(ref); + } + this.pasteInput = ref; + }; + const inputProps = { onPaste: this.onPaste.bind(this) }; + return ( + + ); + } +} + +OnPasteSelect.propTypes = { + separator: PropTypes.string.isRequired, + selectWrap: PropTypes.func.isRequired, + ref: PropTypes.func, + onChange: PropTypes.func.isRequired, + valueKey: PropTypes.string.isRequired, + labelKey: PropTypes.string.isRequired, + options: PropTypes.array, + multi: PropTypes.bool.isRequired, + value: PropTypes.any, + isValidNewOption: PropTypes.func, +}; +OnPasteSelect.defaultProps = { + separator: ',', + selectWrap: Select, + valueKey: 'value', + labelKey: 'label', + options: [], + multi: false, +}; diff --git a/superset/assets/javascripts/components/VirtualizedRendererWrap.jsx b/superset/assets/javascripts/components/VirtualizedRendererWrap.jsx new file mode 100644 index 0000000000000..32821124bd19b --- /dev/null +++ b/superset/assets/javascripts/components/VirtualizedRendererWrap.jsx @@ -0,0 +1,56 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +export default function VirtualizedRendererWrap(renderer) { + function WrapperRenderer({ + focusedOption, + focusOption, + key, + option, + selectValue, + style, + valueArray, + }) { + if (!option) { + return null; + } + const className = ['VirtualizedSelectOption']; + if (option === focusedOption) { + className.push('VirtualizedSelectFocusedOption'); + } + if (option.disabled) { + className.push('VirtualizedSelectDisabledOption'); + } + if (valueArray && valueArray.indexOf(option) >= 0) { + className.push('VirtualizedSelectSelectedOption'); + } + if (option.className) { + className.push(option.className); + } + const events = option.disabled ? {} : { + onClick: () => selectValue(option), + onMouseEnter: () => focusOption(option), + }; + return ( +
+ {renderer(option)} +
+ ); + } + WrapperRenderer.propTypes = { + focusedOption: PropTypes.object.isRequired, + focusOption: PropTypes.func.isRequired, + key: PropTypes.string, + option: PropTypes.object, + selectValue: PropTypes.func.isRequired, + style: PropTypes.object, + valueArray: PropTypes.array, + }; + return WrapperRenderer; +} diff --git a/superset/assets/javascripts/explore/components/controls/SelectControl.jsx b/superset/assets/javascripts/explore/components/controls/SelectControl.jsx index 51381d64d63dd..a82995d70e4fa 100644 --- a/superset/assets/javascripts/explore/components/controls/SelectControl.jsx +++ b/superset/assets/javascripts/explore/components/controls/SelectControl.jsx @@ -1,8 +1,11 @@ import React from 'react'; import PropTypes from 'prop-types'; +import VirtualizedSelect from 'react-virtualized-select'; import Select, { Creatable } from 'react-select'; import ControlHeader from '../ControlHeader'; import { t } from '../../../locales'; +import VirtualizedRendererWrap from '../../../components/VirtualizedRendererWrap'; +import OnPasteSelect from '../../../components/OnPasteSelect'; const propTypes = { choices: PropTypes.array, @@ -37,55 +40,6 @@ const defaultProps = { valueKey: 'value', }; -// Handle `onPaste` so that users may paste in -// options as comma-delimited, slightly modified from -// https://github.com/JedWatson/react-select/issues/1672 -function pasteSelect(props) { - let pasteInput; - return ( -