// Libraries import classNames from 'classnames'; import React, { PureComponent } from 'react'; // Ignoring because I couldn't get @types/react-select work with Torkel's fork // @ts-ignore import { default as ReactSelect, components } from '@torkelo/react-select'; // @ts-ignore import Creatable from '@torkelo/react-select/creatable'; // @ts-ignore import { CreatableProps } from 'react-select'; // @ts-ignore import { default as ReactAsyncSelect } from '@torkelo/react-select/async'; // Components import { SelectOption } from './SelectOption'; import { SelectOptionGroup } from '../../../Select/SelectOptionGroup'; import { SingleValue } from '../../../Select/SingleValue'; import { SelectCommonProps, SelectAsyncProps } from '../../../Select/types'; import IndicatorsContainer from './IndicatorsContainer'; import NoOptionsMessage from './NoOptionsMessage'; import resetSelectStyles from '../../../Select/resetSelectStyles'; import { CustomScrollbar } from '../../../CustomScrollbar/CustomScrollbar'; import { PopoverContent, Tooltip } from '../../../Tooltip/Tooltip'; import { SelectableValue } from '@grafana/data'; /** * Changes in new selects: * - noOptionsMessage & loadingMessage is of string type * - isDisabled is renamed to disabled */ type LegacyCommonProps = Omit, 'noOptionsMessage' | 'disabled' | 'value'>; interface AsyncProps extends LegacyCommonProps, Omit, 'loadingMessage'> { loadingMessage?: () => string; noOptionsMessage?: () => string; tooltipContent?: PopoverContent; isDisabled?: boolean; value?: SelectableValue; } interface LegacySelectProps extends LegacyCommonProps { tooltipContent?: PopoverContent; noOptionsMessage?: () => string; isDisabled?: boolean; value?: SelectableValue; } export const MenuList = (props: any) => { return ( {props.children} ); }; export class Select extends PureComponent> { static defaultProps: Partial> = { className: '', isDisabled: false, isSearchable: true, isClearable: false, isMulti: false, openMenuOnFocus: false, autoFocus: false, isLoading: false, backspaceRemovesValue: true, maxMenuHeight: 300, tabSelectsValue: true, allowCustomValue: false, components: { Option: SelectOption, SingleValue, IndicatorsContainer, MenuList, Group: SelectOptionGroup, }, }; render() { const { defaultValue, getOptionLabel, getOptionValue, onChange, options, placeholder, width, value, className, isDisabled, isLoading, isSearchable, isClearable, backspaceRemovesValue, isMulti, autoFocus, openMenuOnFocus, onBlur, maxMenuHeight, noOptionsMessage, isOpen, components, tooltipContent, tabSelectsValue, onCloseMenu, onOpenMenu, allowCustomValue, formatCreateLabel, } = this.props; let widthClass = ''; if (width) { widthClass = 'width-' + width; } let SelectComponent: ReactSelect | Creatable = ReactSelect; const creatableOptions: any = {}; if (allowCustomValue) { SelectComponent = Creatable; creatableOptions.formatCreateLabel = formatCreateLabel ?? ((input: string) => input); } const selectClassNames = classNames('gf-form-input', 'gf-form-input--form-dropdown', widthClass, className); const selectComponents = { ...Select.defaultProps.components, ...components }; return ( {(onOpenMenuInternal, onCloseMenuInternal) => { return ( ); }} ); } } export class AsyncSelect extends PureComponent> { static defaultProps: Partial> = { className: '', components: {}, loadingMessage: () => 'Loading...', isDisabled: false, isClearable: false, isMulti: false, isSearchable: true, backspaceRemovesValue: true, autoFocus: false, openMenuOnFocus: false, maxMenuHeight: 300, }; render() { const { defaultValue, getOptionLabel, getOptionValue, onChange, placeholder, width, value, className, loadOptions, defaultOptions, isLoading, loadingMessage, noOptionsMessage, isDisabled, isSearchable, isClearable, backspaceRemovesValue, autoFocus, onBlur, openMenuOnFocus, maxMenuHeight, isMulti, tooltipContent, onCloseMenu, onOpenMenu, isOpen, } = this.props; let widthClass = ''; if (width) { widthClass = 'width-' + width; } const selectClassNames = classNames('gf-form-input', 'gf-form-input--form-dropdown', widthClass, className); return ( {(onOpenMenuInternal, onCloseMenuInternal) => { return ( loadingMessage} noOptionsMessage={noOptionsMessage} isDisabled={isDisabled} isSearchable={isSearchable} isClearable={isClearable} autoFocus={autoFocus} onBlur={onBlur} openMenuOnFocus={openMenuOnFocus} maxMenuHeight={maxMenuHeight} isMulti={isMulti} backspaceRemovesValue={backspaceRemovesValue} /> ); }} ); } } export interface TooltipWrapperProps { children: (onOpenMenu: () => void, onCloseMenu: () => void) => React.ReactNode; onOpenMenu?: () => void; onCloseMenu?: () => void; isOpen?: boolean; tooltipContent?: PopoverContent; } export interface TooltipWrapperState { isOpenInternal: boolean; } export class WrapInTooltip extends PureComponent { state: TooltipWrapperState = { isOpenInternal: false, }; onOpenMenu = () => { const { onOpenMenu } = this.props; if (onOpenMenu) { onOpenMenu(); } this.setState({ isOpenInternal: true }); }; onCloseMenu = () => { const { onCloseMenu } = this.props; if (onCloseMenu) { onCloseMenu(); } this.setState({ isOpenInternal: false }); }; render() { const { children, isOpen, tooltipContent } = this.props; const { isOpenInternal } = this.state; let showTooltip: boolean | undefined = undefined; if (isOpenInternal || isOpen) { showTooltip = false; } if (tooltipContent) { return (
{/* div needed for tooltip */} {children(this.onOpenMenu, this.onCloseMenu)}
); } else { return
{children(this.onOpenMenu, this.onCloseMenu)}
; } } } export default Select;