@grafana/ui
Version:
Grafana Components Library
230 lines (227 loc) • 7.84 kB
JavaScript
import { jsx, jsxs } from 'react/jsx-runtime';
import { css } from '@emotion/css';
import RCCascader from '@rc-component/cascader';
import { memo, useMemo, useCallback, useState } from 'react';
import { t } from '@grafana/i18n';
import { useStyles2 } from '../../themes/ThemeContext.mjs';
import { Icon } from '../Icon/Icon.mjs';
import { IconButton } from '../IconButton/IconButton.mjs';
import { Input } from '../Input/Input.mjs';
import { Stack } from '../Layout/Stack/Stack.mjs';
import { Select } from '../Select/Select.mjs';
import { onChangeCascader } from './optionMappings.mjs';
import { getCascaderStyles } from './styles.mjs';
"use strict";
const disableDivFocus = css({
"&:focus": {
outline: "none"
}
});
const DEFAULT_SEPARATOR = " / ";
const flattenOptions = (options, optionPath = [], separator) => {
let selectOptions = [];
for (const option of options) {
const cpy = [...optionPath];
cpy.push(option);
if (!option.items || option.items.length === 0) {
selectOptions.push({
singleLabel: cpy[cpy.length - 1].label,
label: cpy.map((o) => o.label).join(separator || DEFAULT_SEPARATOR),
value: cpy.map((o) => o.value)
});
} else {
selectOptions = [...selectOptions, ...flattenOptions(option.items, cpy, separator)];
}
}
return selectOptions;
};
const Cascader = memo(
({
separator,
placeholder,
options,
changeOnSelect = true,
onSelect,
width,
initialValue,
allowCustomValue,
formatCreateLabel,
displayAllSelectedLevels,
onBlur,
autoFocus,
alwaysOpen,
hideActiveLevelLabel,
disabled,
id,
isClearable,
"data-testid": dataTestId
}) => {
const searchableOptions = useMemo(() => flattenOptions(options, [], separator), [options, separator]);
const getInitialValue = useCallback(
(searchableOptions2, initValue) => {
if (!initValue) {
return {
initialRCValue: { value: [], label: "" },
initialActiveLabel: ""
};
}
for (const option of searchableOptions2) {
const optionPath = option.value || [];
if (optionPath[optionPath.length - 1] === initValue) {
const label = displayAllSelectedLevels ? option.label : option.singleLabel || "";
return {
initialRCValue: { value: optionPath, label },
initialActiveLabel: label
};
}
}
if (allowCustomValue) {
return {
initialRCValue: { value: [], label: initValue },
initialActiveLabel: initValue
};
}
return {
initialRCValue: { value: [], label: "" },
initialActiveLabel: ""
};
},
[allowCustomValue, displayAllSelectedLevels]
);
const { initialRCValue, initialActiveLabel } = useMemo(
() => getInitialValue(searchableOptions, initialValue),
[getInitialValue, initialValue, searchableOptions]
);
const [isSearching, setIsSearching] = useState(false);
const [focusCascade, setFocusCascade] = useState(false);
const [rcValue, setRcValue] = useState(initialRCValue);
const [activeLabel, setActiveLabel] = useState(initialActiveLabel);
const [inputValue, setInputValue] = useState("");
const styles = useStyles2(getCascaderStyles);
const handleChange = (value, selectedOptions) => {
const activeLabel2 = hideActiveLevelLabel ? "" : displayAllSelectedLevels ? selectedOptions.map((option) => option.label).join(separator || DEFAULT_SEPARATOR) : selectedOptions[selectedOptions.length - 1].label;
setRcValue({ value, label: activeLabel2 });
setFocusCascade(true);
setActiveLabel(activeLabel2);
setIsSearching(false);
setInputValue(activeLabel2);
onSelect(selectedOptions[selectedOptions.length - 1].value);
};
const handleSelect = (obj) => {
const valueArray = obj.value || [];
const activeLabel2 = displayAllSelectedLevels ? obj.label : obj.singleLabel || "";
setActiveLabel(activeLabel2);
setInputValue(activeLabel2);
setRcValue({ value: valueArray, label: activeLabel2 });
setIsSearching(false);
setFocusCascade(false);
onSelect(valueArray[valueArray.length - 1]);
};
const handleCreateOption = (value) => {
setActiveLabel(value);
setInputValue(value);
setRcValue({ value: [], label: value });
setIsSearching(false);
onSelect(value);
};
const handleBlur = () => {
setIsSearching(false);
setFocusCascade(false);
if (activeLabel === "") {
setRcValue({ value: [], label: "" });
}
onBlur == null ? void 0 : onBlur();
};
const handleBlurCascade = () => {
setFocusCascade(false);
onBlur == null ? void 0 : onBlur();
};
const handleInputKeyDown = (e) => {
if (["ArrowDown", "ArrowUp", "Enter", "ArrowLeft", "ArrowRight"].includes(e.key)) {
return;
}
const selectionStart = e.currentTarget.selectionStart;
const selectionEnd = e.currentTarget.selectionEnd;
let inputValue2 = e.currentTarget.value;
if (selectionStart !== selectionEnd) {
inputValue2 = inputValue2.substring(0, selectionStart != null ? selectionStart : 0) + inputValue2.substring(selectionEnd != null ? selectionEnd : 0);
}
setFocusCascade(false);
setIsSearching(true);
setInputValue(inputValue2);
};
const handleSelectInputChange = (value) => {
setInputValue(value);
};
return /* @__PURE__ */ jsx("div", { "data-testid": dataTestId, children: isSearching ? /* @__PURE__ */ jsx(
Select,
{
allowCustomValue,
placeholder,
autoFocus: !focusCascade,
onChange: handleSelect,
onBlur: handleBlur,
options: searchableOptions,
onCreateOption: handleCreateOption,
formatCreateLabel,
width,
onInputChange: handleSelectInputChange,
disabled,
inputValue,
inputId: id
}
) : /* @__PURE__ */ jsx(
RCCascader,
{
onChange: onChangeCascader(handleChange),
options,
changeOnSelect,
value: rcValue.value,
fieldNames: { label: "label", value: "value", children: "items" },
expandIcon: null,
open: alwaysOpen,
disabled,
popupClassName: styles.dropdown,
children: /* @__PURE__ */ jsx("div", { className: disableDivFocus, children: /* @__PURE__ */ jsx(
Input,
{
autoFocus,
width,
placeholder,
onBlur: handleBlurCascade,
value: activeLabel,
onFocus: (e) => {
e.currentTarget.select();
},
onKeyDown: handleInputKeyDown,
onChange: () => {
},
suffix: /* @__PURE__ */ jsxs(Stack, { gap: 0.5, children: [
isClearable && activeLabel !== "" && /* @__PURE__ */ jsx(
IconButton,
{
name: "times",
"aria-label": t("grafana-ui.cascader.clear-button", "Clear selection"),
onClick: (e) => {
e.preventDefault();
e.stopPropagation();
setRcValue({ value: [], label: "" });
setActiveLabel("");
setInputValue("");
onSelect("");
}
}
),
/* @__PURE__ */ jsx(Icon, { name: focusCascade ? "angle-up" : "angle-down" })
] }),
disabled,
id
}
) })
}
) });
}
);
Cascader.displayName = "Cascader";
export { Cascader };
//# sourceMappingURL=Cascader.mjs.map