UNPKG

@grafana/ui

Version:
452 lines (444 loc) • 16.4 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var jsxRuntime = require('react/jsx-runtime'); var React = require('react'); var ReactSelect = require('react-select'); var ReactAsyncSelect = require('react-select/async'); var AsyncCreatable = require('react-select/async-creatable'); var Creatable = require('react-select/creatable'); var data = require('@grafana/data'); var e2eSelectors = require('@grafana/e2e-selectors'); var i18n = require('@grafana/i18n'); var ThemeContext = require('../../themes/ThemeContext.cjs'); var FieldContext = require('../Forms/FieldContext.cjs'); var Icon = require('../Icon/Icon.cjs'); var Portal = require('../Portal/Portal.cjs'); var CustomInput = require('./CustomInput.cjs'); var DropdownIndicator = require('./DropdownIndicator.cjs'); var IndicatorsContainer = require('./IndicatorsContainer.cjs'); var InputControl = require('./InputControl.cjs'); var MultiValue = require('./MultiValue.cjs'); var SelectContainer = require('./SelectContainer.cjs'); var SelectMenu = require('./SelectMenu.cjs'); var SelectOptionGroup = require('./SelectOptionGroup.cjs'); var SelectOptionGroupHeader = require('./SelectOptionGroupHeader.cjs'); var SingleValue = require('./SingleValue.cjs'); var ValueContainer = require('./ValueContainer.cjs'); var getSelectStyles = require('./getSelectStyles.cjs'); var resetSelectStyles = require('./resetSelectStyles.cjs'); var types = require('./types.cjs'); var utils = require('./utils.cjs'); function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; } function _interopNamespaceCompat(e) { if (e && typeof e === 'object' && 'default' in e) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var React__namespace = /*#__PURE__*/_interopNamespaceCompat(React); var ReactSelect__default = /*#__PURE__*/_interopDefaultCompat(ReactSelect); var ReactAsyncSelect__default = /*#__PURE__*/_interopDefaultCompat(ReactAsyncSelect); var AsyncCreatable__default = /*#__PURE__*/_interopDefaultCompat(AsyncCreatable); var Creatable__default = /*#__PURE__*/_interopDefaultCompat(Creatable); "use strict"; const CustomControl = (props) => { const { children, innerProps, selectProps: { menuIsOpen, onMenuClose, onMenuOpen }, isFocused, isMulti, getValue, innerRef } = props; const selectProps = props.selectProps; if (selectProps.renderControl) { return React__namespace.createElement(selectProps.renderControl, { isOpen: menuIsOpen, value: isMulti ? getValue() : getValue()[0], ref: innerRef, onClick: menuIsOpen ? onMenuClose : onMenuOpen, onBlur: onMenuClose, disabled: !!selectProps.disabled, invalid: !!selectProps.invalid }); } return /* @__PURE__ */ jsxRuntime.jsx( InputControl.InputControl, { ref: innerRef, innerProps, prefix: selectProps.prefix, focused: isFocused, invalid: !!selectProps.invalid, disabled: !!selectProps.disabled, children } ); }; function determineToggleAllState(selectedValue, options) { if (options.length === selectedValue.length) { return types.ToggleAllState.allSelected; } else if (selectedValue.length === 0) { return types.ToggleAllState.noneSelected; } else { return types.ToggleAllState.indeterminate; } } function SelectBase({ allowCustomValue = false, allowCreateWhileLoading = false, "aria-label": ariaLabel, "data-testid": dataTestid, autoFocus = false, backspaceRemovesValue = true, blurInputOnSelect, cacheOptions, className, closeMenuOnSelect = true, components, createOptionPosition = "last", defaultOptions, defaultValue, disabled: disabledProp = false, filterOption, formatCreateLabel, getOptionLabel, getOptionValue, inputValue, invalid: invalidProp, isClearable = false, id, isLoading = false, isMulti = false, inputId: inputIdProp, isOpen, isOptionDisabled, isSearchable = true, loadOptions, loadingMessage = "Loading options...", maxMenuHeight = 300, minMenuHeight, maxVisibleValues, menuPlacement = "auto", menuPosition = "fixed", menuShouldPortal = true, noOptionsMessage = i18n.t("grafana-ui.select.no-options-label", "No options found"), onBlur, onChange, onCloseMenu, onCreateOption, onInputChange, onKeyDown, onMenuScrollToBottom, onMenuScrollToTop, onOpenMenu, onFocus, toggleAllOptions, openMenuOnFocus = false, options = [], placeholder = i18n.t("grafana-ui.select.placeholder", "Choose"), prefix, renderControl, showAllSelectedWhenOpen = true, tabSelectsValue = true, value, virtualized = false, noMultiValueWrap, width, isValidNewOption, formatOptionLabel, hideSelectedOptions, selectRef, ...rest }) { const theme = ThemeContext.useTheme2(); const styles = getSelectStyles.getSelectStyles(theme); const fieldContext = FieldContext.useFieldContext(); const inputId = inputIdProp != null ? inputIdProp : fieldContext.id; const disabled = disabledProp != null ? disabledProp : fieldContext.disabled; const invalid = invalidProp != null ? invalidProp : fieldContext.invalid; const reactSelectRef = React.useRef(null); const [closeToBottom, setCloseToBottom] = React.useState(false); const selectStyles = resetSelectStyles.useCustomSelectStyles(theme, width); const [hasInputValue, setHasInputValue] = React.useState(!!inputValue); const [open, setOpen] = React.useState(!!isOpen); React.useImperativeHandle(selectRef, () => reactSelectRef.current, []); const handleMenuOpen = React.useCallback(() => { setOpen(true); onOpenMenu == null ? void 0 : onOpenMenu(); }, [onOpenMenu]); const handleMenuClose = React.useCallback(() => { setOpen(false); onCloseMenu == null ? void 0 : onCloseMenu(); }, [onCloseMenu]); const handleKeyDown = React.useCallback( (event) => { if (event.key === "Escape" && open) { event.stopPropagation(); } onKeyDown == null ? void 0 : onKeyDown(event); }, [onKeyDown, open] ); React.useEffect(() => { if (loadOptions && isOpen && reactSelectRef.current && reactSelectRef.current.controlRef && menuPlacement === "auto") { const distance = window.innerHeight - reactSelectRef.current.controlRef.getBoundingClientRect().bottom; setCloseToBottom(distance < maxMenuHeight); } }, [maxMenuHeight, menuPlacement, loadOptions, isOpen]); const onChangeWithEmpty = React.useCallback( (value2, action) => { if (isMulti && (value2 === void 0 || value2 === null)) { return onChange([], action); } onChange(value2, action); }, [isMulti, onChange] ); let ReactSelectComponent = ReactSelect__default.default; const creatableProps = {}; let asyncSelectProps = {}; let selectedValue; if (isMulti && loadOptions) { selectedValue = value; } else { if (isMulti && value && Array.isArray(value) && !loadOptions) { selectedValue = value.map((v) => { var _a; const selectableValue = utils.findSelectedValue((_a = v.value) != null ? _a : v, options); if (selectableValue) { return selectableValue; } return typeof v === "string" ? data.toOption(v) : v; }); } else if (loadOptions) { const hasValue = defaultValue || value; selectedValue = hasValue ? [hasValue] : []; } else { selectedValue = utils.cleanValue(value, options); } } const commonSelectProps = { "aria-label": ariaLabel, "data-testid": dataTestid != null ? dataTestid : e2eSelectors.selectors.components.Select.container, autoFocus, backspaceRemovesValue, blurInputOnSelect, captureMenuScroll: onMenuScrollToBottom || onMenuScrollToTop, closeMenuOnSelect, // We don't want to close if we're actually scrolling the menu // So only close if none of the parents are the select menu itself defaultValue, // Also passing disabled, as this is the new Select API, and I want to use this prop instead of react-select's one disabled, // react-select always tries to filter the options even at first menu open, which is a problem for performance // in large lists. So we set it to not try to filter the options if there is no input value. filterOption: hasInputValue ? filterOption : null, getOptionLabel, getOptionValue, hideSelectedOptions, inputValue, invalid, isClearable, id, // Passing isDisabled as react-select accepts this prop isDisabled: disabled, isLoading, isMulti, inputId, isOptionDisabled, isSearchable, maxMenuHeight, minMenuHeight, maxVisibleValues, menuIsOpen: isOpen, menuPlacement: menuPlacement === "auto" && closeToBottom ? "top" : menuPlacement, menuPosition: menuShouldPortal ? "fixed" : menuPosition, menuShouldBlockScroll: true, menuPortalTarget: menuShouldPortal && Portal.getPortalContainer(), menuShouldScrollIntoView: false, onBlur, onChange: onChangeWithEmpty, onInputChange: (val, actionMeta) => { var _a; const newValue = (_a = onInputChange == null ? void 0 : onInputChange(val, actionMeta)) != null ? _a : val; const newHasValue = !!newValue; if (newHasValue !== hasInputValue) { setHasInputValue(newHasValue); } return newValue; }, onKeyDown: handleKeyDown, onMenuClose: handleMenuClose, onMenuOpen: handleMenuOpen, onMenuScrollToBottom, onMenuScrollToTop, onFocus, formatOptionLabel, openMenuOnFocus, options: virtualized ? utils.omitDescriptions(options) : options, placeholder, prefix, renderControl, showAllSelectedWhenOpen, tabSelectsValue, value: isMulti ? selectedValue : selectedValue == null ? void 0 : selectedValue[0], noMultiValueWrap }; if (allowCustomValue) { ReactSelectComponent = Creatable__default.default; creatableProps.allowCreateWhileLoading = allowCreateWhileLoading; creatableProps.formatCreateLabel = formatCreateLabel != null ? formatCreateLabel : defaultFormatCreateLabel; creatableProps.onCreateOption = onCreateOption; creatableProps.createOptionPosition = createOptionPosition; creatableProps.isValidNewOption = isValidNewOption; } if (loadOptions) { ReactSelectComponent = allowCustomValue ? AsyncCreatable__default.default : ReactAsyncSelect__default.default; asyncSelectProps = { loadOptions, cacheOptions, defaultOptions }; } const SelectMenuComponent = virtualized ? SelectMenu.VirtualizedSelectMenu : SelectMenu.SelectMenu; let toggleAllState = types.ToggleAllState.noneSelected; if ((toggleAllOptions == null ? void 0 : toggleAllOptions.enabled) && Array.isArray(selectedValue)) { if (toggleAllOptions == null ? void 0 : toggleAllOptions.determineToggleAllState) { toggleAllState = toggleAllOptions.determineToggleAllState(selectedValue, options); } else { toggleAllState = determineToggleAllState(selectedValue, options); } } const toggleAll = React.useCallback(() => { let toSelect = toggleAllState === types.ToggleAllState.noneSelected ? options : []; if (toggleAllOptions == null ? void 0 : toggleAllOptions.optionsFilter) { toSelect = toggleAllState === types.ToggleAllState.noneSelected ? options.filter(toggleAllOptions.optionsFilter) : options.filter((opt) => !toggleAllOptions.optionsFilter(opt)); } onChange(toSelect, { action: "select-option", option: {} }); }, [options, toggleAllOptions, onChange, toggleAllState]); return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsx( ReactSelectComponent, { ref: reactSelectRef, components: { MenuList: SelectMenuComponent, Group: SelectOptionGroup.SelectOptionGroup, GroupHeading: SelectOptionGroupHeader.SelectOptionGroupHeader, ValueContainer: ValueContainer.ValueContainer, IndicatorsContainer: CustomIndicatorsContainer, IndicatorSeparator, Control: CustomControl, Option: SelectMenu.SelectMenuOptions, ClearIndicator, LoadingIndicator() { return null; }, LoadingMessage() { return /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles.loadingMessage, children: loadingMessage }); }, NoOptionsMessage() { return /* @__PURE__ */ jsxRuntime.jsx( "div", { className: styles.loadingMessage, "aria-label": i18n.t("grafana-ui.select.empty-options", "No options provided"), children: noOptionsMessage } ); }, DropdownIndicator: DropdownIndicator.DropdownIndicator, SingleValue(props) { return /* @__PURE__ */ jsxRuntime.jsx(SingleValue.SingleValue, { ...props, isDisabled: disabled }); }, SelectContainer: SelectContainer.SelectContainer, MultiValueContainer: MultiValue.MultiValueContainer, MultiValueRemove: !disabled ? MultiValue.MultiValueRemove : () => null, Input: CustomInput.CustomInput, ...components }, toggleAllOptions: (toggleAllOptions == null ? void 0 : toggleAllOptions.enabled) && { state: toggleAllState, selectAllClicked: toggleAll, selectedCount: Array.isArray(selectedValue) ? selectedValue.length : void 0 }, styles: selectStyles, className, autoWidth: width === "auto", ...commonSelectProps, ...creatableProps, ...asyncSelectProps, ...rest } ) }); } function ClearIndicator({ clearValue, ...rest }) { const theme = ThemeContext.useTheme2(); const styles = getSelectStyles.getSelectStyles(theme); return /* @__PURE__ */ jsxRuntime.jsx( Icon.Icon, { name: "times", role: "button", "aria-label": i18n.t("grafana-ui.select.clear-value", "Clear value"), className: styles.singleValueRemove, tabIndex: 0, onMouseDown: (e) => { e.preventDefault(); e.stopPropagation(); clearValue(); }, onKeyDown: (e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); e.stopPropagation(); clearValue(); } } } ); } function defaultFormatCreateLabel(input) { return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "8px", alignItems: "center" }, children: [ /* @__PURE__ */ jsxRuntime.jsx("div", { children: input }), /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flexGrow: 1 } }), /* @__PURE__ */ jsxRuntime.jsx("div", { className: "muted small", style: { display: "flex", gap: "8px", alignItems: "center" }, children: /* @__PURE__ */ jsxRuntime.jsx(i18n.Trans, { i18nKey: "grafana-ui.select.default-create-label", children: "Hit enter to add" }) }) ] }); } function CustomIndicatorsContainer(props) { const { showAllSelectedWhenOpen, maxVisibleValues, menuIsOpen } = props.selectProps; const value = props.getValue(); if (maxVisibleValues !== void 0 && Array.isArray(props.children)) { const selectedValuesCount = value.length; if (selectedValuesCount > maxVisibleValues && !(showAllSelectedWhenOpen && menuIsOpen)) { const indicatorChildren = [...props.children]; indicatorChildren.splice( -1, 0, /* @__PURE__ */ jsxRuntime.jsx("span", { id: "excess-values", children: `(+${selectedValuesCount - maxVisibleValues})` }, "excess-values") ); return /* @__PURE__ */ jsxRuntime.jsx(IndicatorsContainer.IndicatorsContainer, { ...props, children: indicatorChildren }); } } return /* @__PURE__ */ jsxRuntime.jsx(IndicatorsContainer.IndicatorsContainer, { ...props }); } function IndicatorSeparator() { return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {}); } exports.SelectBase = SelectBase; //# sourceMappingURL=SelectBase.cjs.map