UNPKG

@grafana/ui

Version:
230 lines (227 loc) • 7.84 kB
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