@grafana/ui
Version:
Grafana Components Library
452 lines (444 loc) • 16.4 kB
JavaScript
'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