mirror of
https://github.com/prymitive/karma
synced 2026-05-07 03:26:52 +00:00
177 lines
4.7 KiB
TypeScript
177 lines
4.7 KiB
TypeScript
import React, {
|
|
FC,
|
|
Ref,
|
|
CSSProperties,
|
|
useRef,
|
|
useState,
|
|
useCallback,
|
|
} from "react";
|
|
|
|
import { observer } from "mobx-react-lite";
|
|
|
|
import { useFloating, shift, flip, offset } from "@floating-ui/react-dom";
|
|
|
|
import type { OnChangeValue } from "react-select";
|
|
import AsyncSelect from "react-select/async";
|
|
|
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
import { faCaretDown } from "@fortawesome/free-solid-svg-icons/faCaretDown";
|
|
|
|
import type { AlertStore } from "Stores/AlertStore";
|
|
import type { Settings } from "Stores/Settings";
|
|
import type { APIGridT } from "Models/APITypes";
|
|
import { StringToOption, OptionT } from "Common/Select";
|
|
import { DropdownSlide } from "Components/Animations/DropdownSlide";
|
|
import { ThemeContext } from "Components/Theme";
|
|
import { useOnClickOutside } from "Hooks/useOnClickOutside";
|
|
|
|
const specialLabels: OptionT[] = [
|
|
{ label: "Automatic selection", value: "@auto", wasCreated: false },
|
|
{ label: "@alertmanager", value: "@alertmanager", wasCreated: false },
|
|
{ label: "@cluster", value: "@cluster", wasCreated: false },
|
|
{ label: "@receiver", value: "@receiver", wasCreated: false },
|
|
];
|
|
|
|
const NullContainer: FC = () => null;
|
|
|
|
const GridLabelNameSelect: FC<{
|
|
alertStore: AlertStore;
|
|
settingsStore: Settings;
|
|
grid: APIGridT;
|
|
onClose: () => void;
|
|
}> = ({ alertStore, settingsStore, grid, onClose }) => {
|
|
const loadOptions = (
|
|
inputValue: string,
|
|
callback: (options: OptionT[]) => void
|
|
) => {
|
|
const autoEnabled =
|
|
settingsStore.multiGridConfig.config.gridLabel === "@auto";
|
|
const options = [
|
|
...specialLabels.filter(
|
|
(val) =>
|
|
val.value !== "@auto" || (val.value === "@auto" && !autoEnabled)
|
|
),
|
|
...alertStore.data.labelNames
|
|
.filter(
|
|
(labelName) =>
|
|
autoEnabled === true ||
|
|
(autoEnabled === false && labelName !== grid.labelName)
|
|
)
|
|
.sort()
|
|
.map((key) => StringToOption(key)),
|
|
];
|
|
|
|
callback(options);
|
|
};
|
|
|
|
const context = React.useContext(ThemeContext);
|
|
|
|
return (
|
|
<AsyncSelect
|
|
styles={context.reactSelectStyles}
|
|
classNamePrefix="react-select"
|
|
loadOptions={loadOptions}
|
|
defaultOptions
|
|
onChange={(option: OnChangeValue<OptionT, false>) => {
|
|
settingsStore.multiGridConfig.setGridLabel((option as OptionT).value);
|
|
onClose();
|
|
}}
|
|
menuIsOpen={true}
|
|
components={{
|
|
ClearIndicator: NullContainer,
|
|
IndicatorSeparator: null,
|
|
DropdownIndicator: null,
|
|
ValueContainer: NullContainer,
|
|
Control: NullContainer,
|
|
}}
|
|
/>
|
|
);
|
|
};
|
|
|
|
const Dropdown: FC<{
|
|
x: number | null;
|
|
y: number | null;
|
|
floating: Ref<HTMLDivElement> | null;
|
|
strategy: CSSProperties["position"];
|
|
alertStore: AlertStore;
|
|
settingsStore: Settings;
|
|
grid: APIGridT;
|
|
onClose: () => void;
|
|
}> = ({
|
|
x,
|
|
y,
|
|
floating,
|
|
strategy,
|
|
alertStore,
|
|
settingsStore,
|
|
grid,
|
|
onClose,
|
|
}) => {
|
|
return (
|
|
<div
|
|
className="dropdown-menu d-block shadow components-grid-label-select-menu border-0 p-0 m-0"
|
|
ref={floating}
|
|
style={{
|
|
fontSize: "1rem",
|
|
fontWeight: "normal",
|
|
position: strategy,
|
|
top: y ?? "",
|
|
left: x ?? "",
|
|
}}
|
|
>
|
|
<GridLabelNameSelect
|
|
alertStore={alertStore}
|
|
settingsStore={settingsStore}
|
|
grid={grid}
|
|
onClose={onClose}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const GridLabelSelect: FC<{
|
|
alertStore: AlertStore;
|
|
settingsStore: Settings;
|
|
grid: APIGridT;
|
|
}> = observer(({ alertStore, settingsStore, grid }) => {
|
|
const [isVisible, setIsVisible] = useState<boolean>(false);
|
|
const hide = useCallback(() => setIsVisible(false), []);
|
|
const toggle = useCallback(() => {
|
|
setIsVisible(!isVisible);
|
|
}, [isVisible]);
|
|
const ref = useRef<HTMLDivElement | null>(null);
|
|
useOnClickOutside(ref, hide, isVisible);
|
|
|
|
const { x, y, reference, floating, strategy } = useFloating({
|
|
placement: "bottom",
|
|
middleware: [shift(), flip(), offset(5)],
|
|
});
|
|
|
|
return (
|
|
<div ref={ref} className="components-label badge ps-1 pe-2">
|
|
<span
|
|
ref={reference}
|
|
onClick={toggle}
|
|
className="border-0 rounded-0 bg-inherit cursor-pointer px-1 py-0 components-grid-label-select-dropdown"
|
|
data-toggle="dropdown"
|
|
>
|
|
<FontAwesomeIcon className="text-muted" icon={faCaretDown} />
|
|
</span>
|
|
<DropdownSlide in={isVisible} unmountOnExit>
|
|
<Dropdown
|
|
alertStore={alertStore}
|
|
settingsStore={settingsStore}
|
|
grid={grid}
|
|
onClose={toggle}
|
|
x={x}
|
|
y={y}
|
|
floating={floating}
|
|
strategy={strategy}
|
|
/>
|
|
</DropdownSlide>
|
|
</div>
|
|
);
|
|
});
|
|
|
|
export { GridLabelSelect };
|