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 (
-