1import React, { useCallback, useMemo } from 'react'; 2import { SegmentAsync } from '@grafana/ui'; 3import { actions } from '../state/actions'; 4import { GraphiteSegment } from '../types'; 5import { SelectableValue } from '@grafana/data'; 6import { getAltSegmentsSelectables } from '../state/providers'; 7import { debounce } from 'lodash'; 8import { GraphiteQueryEditorState } from '../state/store'; 9import { useDispatch } from '../state/context'; 10 11type Props = { 12 segment: GraphiteSegment; 13 metricIndex: number; 14 state: GraphiteQueryEditorState; 15}; 16 17/** 18 * Represents a single metric node in the metric path at the given index. Allows to change the metric name to one of the 19 * provided options or a custom value. 20 * 21 * Options for tag names and metric names are reloaded while user is typing with backend taking care of auto-complete 22 * (auto-complete cannot be implemented in front-end because backend returns only limited number of entries) 23 * 24 * getAltSegmentsSelectables() also returns list of tags for segment with index=0. Once a tag is selected the editor 25 * enters tag-adding mode (see SeriesSection and GraphiteQueryModel.seriesByTagUsed). 26 */ 27export function MetricSegment({ metricIndex, segment, state }: Props) { 28 const dispatch = useDispatch(); 29 const loadOptions = useCallback( 30 (value: string | undefined) => { 31 return getAltSegmentsSelectables(state, metricIndex, value || ''); 32 }, 33 [state, metricIndex] 34 ); 35 const debouncedLoadOptions = useMemo(() => debounce(loadOptions, 200, { leading: true }), [loadOptions]); 36 37 const onSegmentChanged = useCallback( 38 (selectableValue: SelectableValue<GraphiteSegment | string>) => { 39 // selectableValue.value is always defined because emptyValues are not allowed in SegmentAsync by default 40 dispatch(actions.segmentValueChanged({ segment: selectableValue.value!, index: metricIndex })); 41 }, 42 [dispatch, metricIndex] 43 ); 44 45 // segmentValueChanged action will destroy SegmentAsync immediately if a tag is selected. To give time 46 // for the clean up the action is debounced. 47 const onSegmentChangedDebounced = useMemo(() => debounce(onSegmentChanged, 100), [onSegmentChanged]); 48 49 return ( 50 <SegmentAsync<GraphiteSegment | string> 51 value={segment.value} 52 inputMinWidth={150} 53 allowCustomValue={true} 54 loadOptions={debouncedLoadOptions} 55 reloadOptionsOnChange={true} 56 onChange={onSegmentChangedDebounced} 57 /> 58 ); 59} 60