@grafana/ui
Version:
Grafana Components Library
230 lines (228 loc) • 7.44 kB
JavaScript
import { jsx, jsxs } from 'react/jsx-runtime';
import { css } from '@emotion/css';
import memoize from 'micro-memoize';
import RCCascader from 'rc-cascader';
import { PureComponent } from 'react';
import '@grafana/data';
import { withTheme2 } from '../../themes/ThemeContext.mjs';
import '@emotion/react';
import 'tinycolor2';
import '../../utils/skeleton.mjs';
import { t } from '../../utils/i18n.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';
const disableDivFocus = css({
"&:focus": {
outline: "none"
}
});
const DEFAULT_SEPARATOR = " / ";
class UnthemedCascader extends PureComponent {
constructor(props) {
super(props);
this.flattenOptions = (options, optionPath = []) => {
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(this.props.separator || DEFAULT_SEPARATOR),
value: cpy.map((o) => o.value)
});
} else {
selectOptions = [...selectOptions, ...this.flattenOptions(option.items, cpy)];
}
}
return selectOptions;
};
this.getSearchableOptions = memoize((options) => this.flattenOptions(options));
//For rc-cascader
this.onChange = (value, selectedOptions) => {
const activeLabel = this.props.hideActiveLevelLabel ? "" : this.props.displayAllSelectedLevels ? selectedOptions.map((option) => option.label).join(this.props.separator || DEFAULT_SEPARATOR) : selectedOptions[selectedOptions.length - 1].label;
const state = {
rcValue: { value, label: activeLabel },
focusCascade: true,
activeLabel,
isSearching: false,
inputValue: activeLabel
};
this.setState(state);
this.props.onSelect(selectedOptions[selectedOptions.length - 1].value);
};
//For select
this.onSelect = (obj) => {
const valueArray = obj.value || [];
const activeLabel = this.props.displayAllSelectedLevels ? obj.label : obj.singleLabel || "";
const state = {
activeLabel,
inputValue: activeLabel,
rcValue: { value: valueArray, label: activeLabel },
isSearching: false,
focusCascade: false
};
this.setState(state);
this.props.onSelect(valueArray[valueArray.length - 1]);
};
this.onCreateOption = (value) => {
this.setState({
activeLabel: value,
inputValue: value,
rcValue: [],
isSearching: false
});
this.props.onSelect(value);
};
this.onBlur = () => {
var _a, _b;
this.setState({
isSearching: false,
focusCascade: false
});
if (this.state.activeLabel === "") {
this.setState({
rcValue: []
});
}
(_b = (_a = this.props).onBlur) == null ? void 0 : _b.call(_a);
};
this.onBlurCascade = () => {
var _a, _b;
this.setState({
focusCascade: false
});
(_b = (_a = this.props).onBlur) == null ? void 0 : _b.call(_a);
};
this.onInputKeyDown = (e) => {
if (["ArrowDown", "ArrowUp", "Enter", "ArrowLeft", "ArrowRight"].includes(e.key)) {
return;
}
const { activeLabel } = this.state;
this.setState({
focusCascade: false,
isSearching: true,
inputValue: activeLabel
});
};
this.onSelectInputChange = (value) => {
this.setState({
inputValue: value
});
};
const searchableOptions = this.getSearchableOptions(props.options);
const { rcValue, activeLabel } = this.setInitialValue(searchableOptions, props.initialValue);
this.state = {
isSearching: false,
focusCascade: false,
rcValue,
activeLabel,
inputValue: ""
};
}
setInitialValue(searchableOptions, initValue) {
if (!initValue) {
return { rcValue: [], activeLabel: "" };
}
for (const option of searchableOptions) {
const optionPath = option.value || [];
if (optionPath[optionPath.length - 1] === initValue) {
return {
rcValue: optionPath,
activeLabel: this.props.displayAllSelectedLevels ? option.label : option.singleLabel || ""
};
}
}
if (this.props.allowCustomValue) {
return { rcValue: [], activeLabel: initValue };
}
return { rcValue: [], activeLabel: "" };
}
render() {
const {
allowCustomValue,
formatCreateLabel,
placeholder,
width,
changeOnSelect,
options,
disabled,
id,
isClearable,
theme
} = this.props;
const { focusCascade, isSearching, rcValue, activeLabel, inputValue } = this.state;
const searchableOptions = this.getSearchableOptions(options);
const styles = getCascaderStyles(theme);
return /* @__PURE__ */ jsx("div", { children: isSearching ? /* @__PURE__ */ jsx(
Select,
{
allowCustomValue,
placeholder,
autoFocus: !focusCascade,
onChange: this.onSelect,
onBlur: this.onBlur,
options: searchableOptions,
onCreateOption: this.onCreateOption,
formatCreateLabel,
width,
onInputChange: this.onSelectInputChange,
disabled,
inputValue,
inputId: id
}
) : /* @__PURE__ */ jsx(
RCCascader,
{
onChange: onChangeCascader(this.onChange),
options,
changeOnSelect,
value: rcValue.value,
fieldNames: { label: "label", value: "value", children: "items" },
expandIcon: null,
open: this.props.alwaysOpen,
disabled,
dropdownClassName: styles.dropdown,
children: /* @__PURE__ */ jsx("div", { className: disableDivFocus, children: /* @__PURE__ */ jsx(
Input,
{
autoFocus: this.props.autoFocus,
width,
placeholder,
onBlur: this.onBlurCascade,
value: activeLabel,
onKeyDown: this.onInputKeyDown,
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();
this.setState({ rcValue: [], activeLabel: "", inputValue: "" });
this.props.onSelect("");
}
}
),
/* @__PURE__ */ jsx(Icon, { name: focusCascade ? "angle-up" : "angle-down" })
] }),
disabled,
id
}
) })
}
) });
}
}
UnthemedCascader.defaultProps = { changeOnSelect: true };
withTheme2(UnthemedCascader);
//# sourceMappingURL=Cascader.mjs.map