import { css } from '@emotion/css'; import React, { useCallback, useEffect, useRef } from 'react'; import { Draggable } from 'react-beautiful-dnd'; import { GrafanaTheme2, MappingType, SpecialValueMatch, SelectableValue, ValueMappingResult } from '@grafana/data'; import { useStyles2, Icon, Select, HorizontalGroup, ColorPicker, IconButton, Input, Button } from '@grafana/ui'; import { ResourcePickerSize, ResourceFolderName, MediaType } from '../../types'; import { ResourcePicker } from '../ResourcePicker'; export interface ValueMappingEditRowModel { type: MappingType; from?: number; to?: number; pattern?: string; key?: string; isNew?: boolean; specialMatch?: SpecialValueMatch; result: ValueMappingResult; id: string; } interface Props { mapping: ValueMappingEditRowModel; index: number; onChange: (index: number, mapping: ValueMappingEditRowModel) => void; onRemove: (index: number) => void; onDuplicate: (index: number) => void; showIconPicker?: boolean; } export function ValueMappingEditRow({ mapping, index, onChange, onRemove, onDuplicate, showIconPicker }: Props) { const { key, result, id } = mapping; const styles = useStyles2(getStyles); const inputRef = useRef(null); const update = useCallback( (fn: (item: ValueMappingEditRowModel) => void) => { const copy = { ...mapping, result: { ...mapping.result, }, }; fn(copy); onChange(index, copy); }, [mapping, index, onChange] ); useEffect(() => { if (inputRef.current && mapping.isNew) { inputRef.current.focus(); update((mapping) => { mapping.isNew = false; }); } }, [mapping, inputRef, update]); const onChangeColor = (color: string) => { update((mapping) => { mapping.result.color = color; }); }; const onClearColor = () => { update((mapping) => { mapping.result.color = undefined; }); }; const onChangeIcon = (icon?: string) => { update((mapping) => { mapping.result.icon = icon; }); }; const onClearIcon = () => { update((mapping) => { mapping.result.icon = undefined; }); }; const onUpdateMatchValue = (event: React.FormEvent) => { update((mapping) => { mapping.key = event.currentTarget.value; }); }; const onChangeText = (event: React.FormEvent) => { update((mapping) => { mapping.result.text = event.currentTarget.value; }); }; const onChangeFrom = (event: React.FormEvent) => { update((mapping) => { mapping.from = parseFloat(event.currentTarget.value); }); }; const onChangeTo = (event: React.FormEvent) => { update((mapping) => { mapping.to = parseFloat(event.currentTarget.value); }); }; const onChangePattern = (event: React.FormEvent) => { update((mapping) => { mapping.pattern = event.currentTarget.value; }); }; const onChangeSpecialMatch = (sel: SelectableValue) => { update((mapping) => { mapping.specialMatch = sel.value; }); }; const specialMatchOptions: Array> = [ { label: 'Null', value: SpecialValueMatch.Null, description: 'Matches null and undefined values' }, { label: 'NaN', value: SpecialValueMatch.NaN, description: 'Matches against Number.NaN (not a number)' }, { label: 'Null + NaN', value: SpecialValueMatch.NullAndNaN, description: 'Matches null, undefined and NaN' }, { label: 'True', value: SpecialValueMatch.True, description: 'Boolean true values' }, { label: 'False', value: SpecialValueMatch.False, description: 'Boolean false values' }, { label: 'Empty', value: SpecialValueMatch.Empty, description: 'Empty string' }, ]; return ( {(provided) => (
{mapping.type} {mapping.type === MappingType.ValueToText && ( )} {mapping.type === MappingType.RangeToText && (
)} {mapping.type === MappingType.RegexToText && ( )} {mapping.type === MappingType.SpecialValue && ( {result.color && ( )} {!result.color && ( {(props) => ( )} )} {showIconPicker && ( {result.icon && ( )} )} onDuplicate(index)} data-testid="duplicate-value-mapping" /> onRemove(index)} data-testid="remove-value-mapping" /> )}
); } const getStyles = (theme: GrafanaTheme2) => ({ dragRow: css({ position: 'relative', }), dragHandle: css({ cursor: 'grab', // create focus ring around the whole row when the drag handle is tab-focused // needs position: relative on the drag row to work correctly '&:focus-visible&:after': { bottom: 0, content: '""', left: 0, position: 'absolute', right: 0, top: 0, outline: `2px solid ${theme.colors.primary.main}`, outlineOffset: '-2px', }, }), rangeInputWrapper: css({ display: 'flex', '> div:first-child': { marginRight: theme.spacing(2), }, }), regexInputWrapper: css({ display: 'flex', '> div:first-child': { marginRight: theme.spacing(2), }, }), typeColumn: css({ textTransform: 'capitalize', textAlign: 'center', width: '1%', }), textAlignCenter: css({ textAlign: 'center', }), });