mirror of https://github.com/grafana/grafana.git
				
				
				
			Core: move dimensions out of geomap into app/features (#37946)
This commit is contained in:
		
							parent
							
								
									d93d989a5a
								
							
						
					
					
						commit
						2ff4b028c8
					
				| 
						 | 
				
			
			@ -1,9 +1,9 @@
 | 
			
		|||
import React, { useCallback } from 'react';
 | 
			
		||||
import { FieldConfigEditorProps, StringFieldConfigSettings } from '@grafana/data';
 | 
			
		||||
import { StandardEditorProps, StringFieldConfigSettings } from '@grafana/data';
 | 
			
		||||
import { Input } from '../Input/Input';
 | 
			
		||||
import { TextArea } from '../TextArea/TextArea';
 | 
			
		||||
 | 
			
		||||
export const StringValueEditor: React.FC<FieldConfigEditorProps<string, StringFieldConfigSettings>> = ({
 | 
			
		||||
export const StringValueEditor: React.FC<StandardEditorProps<string, StringFieldConfigSettings>> = ({
 | 
			
		||||
  value,
 | 
			
		||||
  onChange,
 | 
			
		||||
  item,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,13 +1,13 @@
 | 
			
		|||
import { DataFrame, getFieldColorModeForField, getScaleCalculator, GrafanaTheme2 } from '@grafana/data';
 | 
			
		||||
import { ColorDimensionConfig, DimensionSupplier } from './types';
 | 
			
		||||
import { findField } from './utils';
 | 
			
		||||
import { findField, getLastNotNullFieldValue } from './utils';
 | 
			
		||||
 | 
			
		||||
//---------------------------------------------------------
 | 
			
		||||
// Color dimension
 | 
			
		||||
//---------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
export function getColorDimension(
 | 
			
		||||
  frame: DataFrame,
 | 
			
		||||
  frame: DataFrame | undefined,
 | 
			
		||||
  config: ColorDimensionConfig,
 | 
			
		||||
  theme: GrafanaTheme2
 | 
			
		||||
): DimensionSupplier<string> {
 | 
			
		||||
| 
						 | 
				
			
			@ -17,6 +17,7 @@ export function getColorDimension(
 | 
			
		|||
    return {
 | 
			
		||||
      isAssumed: Boolean(config.field?.length) || !config.fixed,
 | 
			
		||||
      fixed: v,
 | 
			
		||||
      value: () => v,
 | 
			
		||||
      get: (i) => v,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -25,6 +26,7 @@ export function getColorDimension(
 | 
			
		|||
    const fixed = mode.getCalculator(field, theme)(0, 0);
 | 
			
		||||
    return {
 | 
			
		||||
      fixed,
 | 
			
		||||
      value: () => fixed,
 | 
			
		||||
      get: (i) => fixed,
 | 
			
		||||
      field,
 | 
			
		||||
    };
 | 
			
		||||
| 
						 | 
				
			
			@ -36,5 +38,6 @@ export function getColorDimension(
 | 
			
		|||
      return scale(val).color;
 | 
			
		||||
    },
 | 
			
		||||
    field,
 | 
			
		||||
    value: () => scale(getLastNotNullFieldValue(field)).color,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -5,7 +5,7 @@ import { Select, ColorPicker, useStyles2 } from '@grafana/ui';
 | 
			
		|||
import {
 | 
			
		||||
  useFieldDisplayNames,
 | 
			
		||||
  useSelectOptions,
 | 
			
		||||
} from '../../../../../../../packages/grafana-ui/src/components/MatchersUI/utils';
 | 
			
		||||
} from '../../../../../packages/grafana-ui/src/components/MatchersUI/utils';
 | 
			
		||||
import { css } from '@emotion/css';
 | 
			
		||||
 | 
			
		||||
const fixedColorOption: SelectableValue<string> = {
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,43 @@
 | 
			
		|||
import React, { useState, useEffect } from 'react';
 | 
			
		||||
import { Select } from '@grafana/ui';
 | 
			
		||||
import { SelectableValue } from '@grafana/data';
 | 
			
		||||
import { getBackendSrv } from '@grafana/runtime';
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
  value: string;
 | 
			
		||||
  onChange: (v: string) => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const IconSelector: React.FC<Props> = ({ value, onChange }) => {
 | 
			
		||||
  const [icons, setIcons] = useState<SelectableValue[]>(value ? [{ value, label: value }] : []);
 | 
			
		||||
  const [icon, setIcon] = useState<string>();
 | 
			
		||||
  const iconRoot = (window as any).__grafana_public_path__ + 'img/icons/unicons/';
 | 
			
		||||
  const onChangeIcon = (value: string) => {
 | 
			
		||||
    onChange(value);
 | 
			
		||||
    setIcon(value);
 | 
			
		||||
  };
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    getBackendSrv()
 | 
			
		||||
      .get(`${iconRoot}/index.json`)
 | 
			
		||||
      .then((data) => {
 | 
			
		||||
        setIcons(
 | 
			
		||||
          data.files.map((icon: string) => ({
 | 
			
		||||
            value: icon,
 | 
			
		||||
            label: icon,
 | 
			
		||||
          }))
 | 
			
		||||
        );
 | 
			
		||||
      });
 | 
			
		||||
  }, [iconRoot]);
 | 
			
		||||
  return (
 | 
			
		||||
    <Select
 | 
			
		||||
      menuShouldPortal
 | 
			
		||||
      options={icons}
 | 
			
		||||
      value={icon}
 | 
			
		||||
      onChange={(selectedValue) => {
 | 
			
		||||
        onChangeIcon(selectedValue.value!);
 | 
			
		||||
      }}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default IconSelector;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,115 @@
 | 
			
		|||
import React, { FC, useCallback } from 'react';
 | 
			
		||||
import {
 | 
			
		||||
  FieldNamePickerConfigSettings,
 | 
			
		||||
  StandardEditorProps,
 | 
			
		||||
  StandardEditorsRegistryItem,
 | 
			
		||||
  StringFieldConfigSettings,
 | 
			
		||||
} from '@grafana/data';
 | 
			
		||||
import { ResourceDimensionConfig, ResourceDimensionMode, ResourceDimensionOptions } from '../types';
 | 
			
		||||
import { InlineField, InlineFieldRow, RadioButtonGroup, StringValueEditor } from '@grafana/ui';
 | 
			
		||||
import { FieldNamePicker } from '../../../../../packages/grafana-ui/src/components/MatchersUI/FieldNamePicker';
 | 
			
		||||
import IconSelector from './IconSelector';
 | 
			
		||||
 | 
			
		||||
const resourceOptions = [
 | 
			
		||||
  { label: 'Fixed', value: ResourceDimensionMode.Fixed, description: 'Fixed value' },
 | 
			
		||||
  { label: 'Field', value: ResourceDimensionMode.Field, description: 'Use a string field result' },
 | 
			
		||||
  //  { label: 'Mapping', value: ResourceDimensionMode.Mapping, description: 'Map the results of a value to an svg' },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const dummyFieldSettings: StandardEditorsRegistryItem<string, FieldNamePickerConfigSettings> = {
 | 
			
		||||
  settings: {},
 | 
			
		||||
} as any;
 | 
			
		||||
 | 
			
		||||
const dummyImageStringSettings: StandardEditorsRegistryItem<string, StringFieldConfigSettings> = {
 | 
			
		||||
  settings: {
 | 
			
		||||
    placeholder: 'Enter image URL',
 | 
			
		||||
  },
 | 
			
		||||
} as any;
 | 
			
		||||
 | 
			
		||||
export const ResourceDimensionEditor: FC<
 | 
			
		||||
  StandardEditorProps<ResourceDimensionConfig, ResourceDimensionOptions, any>
 | 
			
		||||
> = (props) => {
 | 
			
		||||
  const { value, context, onChange, item } = props;
 | 
			
		||||
  const resourceType = item.settings?.resourceType ?? 'icon';
 | 
			
		||||
  const labelWidth = 9;
 | 
			
		||||
 | 
			
		||||
  const onModeChange = useCallback(
 | 
			
		||||
    (mode) => {
 | 
			
		||||
      onChange({
 | 
			
		||||
        ...value,
 | 
			
		||||
        mode,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    [onChange, value]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const onFieldChange = useCallback(
 | 
			
		||||
    (field) => {
 | 
			
		||||
      onChange({
 | 
			
		||||
        ...value,
 | 
			
		||||
        field,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    [onChange, value]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const onFixedChange = useCallback(
 | 
			
		||||
    (fixed) => {
 | 
			
		||||
      onChange({
 | 
			
		||||
        ...value,
 | 
			
		||||
        fixed,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    [onChange, value]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const mode = value?.mode ?? ResourceDimensionMode.Fixed;
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <InlineFieldRow>
 | 
			
		||||
        <InlineField label="Source" labelWidth={labelWidth} grow={true}>
 | 
			
		||||
          <RadioButtonGroup value={mode} options={resourceOptions} onChange={onModeChange} fullWidth />
 | 
			
		||||
        </InlineField>
 | 
			
		||||
      </InlineFieldRow>
 | 
			
		||||
      {mode !== ResourceDimensionMode.Fixed && (
 | 
			
		||||
        <InlineFieldRow>
 | 
			
		||||
          <InlineField label="Field" labelWidth={labelWidth} grow={true}>
 | 
			
		||||
            <FieldNamePicker
 | 
			
		||||
              context={context}
 | 
			
		||||
              value={value.field ?? ''}
 | 
			
		||||
              onChange={onFieldChange}
 | 
			
		||||
              item={dummyFieldSettings}
 | 
			
		||||
            />
 | 
			
		||||
          </InlineField>
 | 
			
		||||
        </InlineFieldRow>
 | 
			
		||||
      )}
 | 
			
		||||
      {mode === ResourceDimensionMode.Fixed && (
 | 
			
		||||
        <InlineFieldRow>
 | 
			
		||||
          {resourceType === 'icon' && (
 | 
			
		||||
            <InlineField label="Icon" labelWidth={labelWidth} grow={true}>
 | 
			
		||||
              <IconSelector value={value?.fixed} onChange={onFixedChange} />
 | 
			
		||||
            </InlineField>
 | 
			
		||||
          )}
 | 
			
		||||
          {resourceType === 'image' && (
 | 
			
		||||
            <InlineField label="Image" labelWidth={labelWidth} grow={true}>
 | 
			
		||||
              <StringValueEditor
 | 
			
		||||
                context={context}
 | 
			
		||||
                value={value?.fixed}
 | 
			
		||||
                onChange={onFixedChange}
 | 
			
		||||
                item={dummyImageStringSettings}
 | 
			
		||||
              />
 | 
			
		||||
            </InlineField>
 | 
			
		||||
          )}
 | 
			
		||||
        </InlineFieldRow>
 | 
			
		||||
      )}
 | 
			
		||||
      {mode === ResourceDimensionMode.Mapping && (
 | 
			
		||||
        <InlineFieldRow>
 | 
			
		||||
          <InlineField label="Mappings" labelWidth={labelWidth} grow={true}>
 | 
			
		||||
            <div>TODO mappings editor!</div>
 | 
			
		||||
          </InlineField>
 | 
			
		||||
        </InlineFieldRow>
 | 
			
		||||
      )}
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -5,8 +5,8 @@ import { InlineField, InlineFieldRow, Select, useStyles2 } from '@grafana/ui';
 | 
			
		|||
import {
 | 
			
		||||
  useFieldDisplayNames,
 | 
			
		||||
  useSelectOptions,
 | 
			
		||||
} from '../../../../../../../packages/grafana-ui/src/components/MatchersUI/utils';
 | 
			
		||||
import { NumberInput } from '../../components/NumberInput';
 | 
			
		||||
} from '../../../../../packages/grafana-ui/src/components/MatchersUI/utils';
 | 
			
		||||
import { NumberInput } from './NumberInput';
 | 
			
		||||
import { css } from '@emotion/css';
 | 
			
		||||
import { validateScaleOptions, validateScaleConfig } from '../scale';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,107 @@
 | 
			
		|||
import React, { FC, useCallback } from 'react';
 | 
			
		||||
import {
 | 
			
		||||
  FieldNamePickerConfigSettings,
 | 
			
		||||
  StandardEditorProps,
 | 
			
		||||
  StandardEditorsRegistryItem,
 | 
			
		||||
  StringFieldConfigSettings,
 | 
			
		||||
} from '@grafana/data';
 | 
			
		||||
import { TextDimensionConfig, TextDimensionMode, TextDimensionOptions } from '../types';
 | 
			
		||||
import { InlineField, InlineFieldRow, RadioButtonGroup, StringValueEditor } from '@grafana/ui';
 | 
			
		||||
import { FieldNamePicker } from '../../../../../packages/grafana-ui/src/components/MatchersUI/FieldNamePicker';
 | 
			
		||||
 | 
			
		||||
const textOptions = [
 | 
			
		||||
  { label: 'Fixed', value: TextDimensionMode.Fixed, description: 'Fixed value' },
 | 
			
		||||
  { label: 'Field', value: TextDimensionMode.Field, description: 'Display field value' },
 | 
			
		||||
  { label: 'Template', value: TextDimensionMode.Template, description: 'use template text' },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const dummyFieldSettings: StandardEditorsRegistryItem<string, FieldNamePickerConfigSettings> = {
 | 
			
		||||
  settings: {},
 | 
			
		||||
} as any;
 | 
			
		||||
 | 
			
		||||
const dummyStringSettings: StandardEditorsRegistryItem<string, StringFieldConfigSettings> = {
 | 
			
		||||
  settings: {},
 | 
			
		||||
} as any;
 | 
			
		||||
 | 
			
		||||
export const TextDimensionEditor: FC<StandardEditorProps<TextDimensionConfig, TextDimensionOptions, any>> = (props) => {
 | 
			
		||||
  const { value, context, onChange } = props;
 | 
			
		||||
  const labelWidth = 9;
 | 
			
		||||
 | 
			
		||||
  const onModeChange = useCallback(
 | 
			
		||||
    (mode) => {
 | 
			
		||||
      onChange({
 | 
			
		||||
        ...value,
 | 
			
		||||
        mode,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    [onChange, value]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const onFieldChange = useCallback(
 | 
			
		||||
    (field) => {
 | 
			
		||||
      onChange({
 | 
			
		||||
        ...value,
 | 
			
		||||
        field,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    [onChange, value]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const onFixedChange = useCallback(
 | 
			
		||||
    (fixed) => {
 | 
			
		||||
      onChange({
 | 
			
		||||
        ...value,
 | 
			
		||||
        fixed,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    [onChange, value]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const mode = value?.mode ?? TextDimensionMode.Fixed;
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <InlineFieldRow>
 | 
			
		||||
        <InlineField label="Source" labelWidth={labelWidth} grow={true}>
 | 
			
		||||
          <RadioButtonGroup value={mode} options={textOptions} onChange={onModeChange} fullWidth />
 | 
			
		||||
        </InlineField>
 | 
			
		||||
      </InlineFieldRow>
 | 
			
		||||
      {mode !== TextDimensionMode.Fixed && (
 | 
			
		||||
        <InlineFieldRow>
 | 
			
		||||
          <InlineField label="Field" labelWidth={labelWidth} grow={true}>
 | 
			
		||||
            <FieldNamePicker
 | 
			
		||||
              context={context}
 | 
			
		||||
              value={value.field ?? ''}
 | 
			
		||||
              onChange={onFieldChange}
 | 
			
		||||
              item={dummyFieldSettings}
 | 
			
		||||
            />
 | 
			
		||||
          </InlineField>
 | 
			
		||||
        </InlineFieldRow>
 | 
			
		||||
      )}
 | 
			
		||||
      {mode === TextDimensionMode.Fixed && (
 | 
			
		||||
        <InlineFieldRow>
 | 
			
		||||
          <InlineField label={'Value'} labelWidth={labelWidth} grow={true}>
 | 
			
		||||
            <StringValueEditor
 | 
			
		||||
              context={context}
 | 
			
		||||
              value={value?.fixed}
 | 
			
		||||
              onChange={onFixedChange}
 | 
			
		||||
              item={dummyStringSettings}
 | 
			
		||||
            />
 | 
			
		||||
          </InlineField>
 | 
			
		||||
        </InlineFieldRow>
 | 
			
		||||
      )}
 | 
			
		||||
      {mode === TextDimensionMode.Template && (
 | 
			
		||||
        <InlineFieldRow>
 | 
			
		||||
          <InlineField label="Template" labelWidth={labelWidth} grow={true}>
 | 
			
		||||
            <StringValueEditor // This could be a code editor
 | 
			
		||||
              context={context}
 | 
			
		||||
              value={value?.fixed}
 | 
			
		||||
              onChange={onFixedChange}
 | 
			
		||||
              item={dummyStringSettings}
 | 
			
		||||
            />
 | 
			
		||||
          </InlineField>
 | 
			
		||||
        </InlineFieldRow>
 | 
			
		||||
      )}
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
export * from './ColorDimensionEditor';
 | 
			
		||||
export * from './IconSelector';
 | 
			
		||||
export * from './ResourceDimensionEditor';
 | 
			
		||||
export * from './ScaleDimensionEditor';
 | 
			
		||||
export * from './TextDimensionEditor';
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
export * from './types';
 | 
			
		||||
 | 
			
		||||
export * from './color';
 | 
			
		||||
export * from './scale';
 | 
			
		||||
export * from './text';
 | 
			
		||||
export * from './utils';
 | 
			
		||||
export * from './resource';
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,49 @@
 | 
			
		|||
import { DataFrame } from '@grafana/data';
 | 
			
		||||
import { DimensionSupplier, ResourceDimensionConfig, ResourceDimensionMode } from './types';
 | 
			
		||||
import { findField, getLastNotNullFieldValue } from './utils';
 | 
			
		||||
 | 
			
		||||
//---------------------------------------------------------
 | 
			
		||||
// Resource dimension
 | 
			
		||||
//---------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
export function getResourceDimension(
 | 
			
		||||
  frame: DataFrame | undefined,
 | 
			
		||||
  config: ResourceDimensionConfig
 | 
			
		||||
): DimensionSupplier<string> {
 | 
			
		||||
  const mode = config.mode ?? ResourceDimensionMode.Fixed;
 | 
			
		||||
  if (mode === ResourceDimensionMode.Fixed) {
 | 
			
		||||
    const v = config.fixed!;
 | 
			
		||||
    return {
 | 
			
		||||
      isAssumed: !Boolean(v),
 | 
			
		||||
      fixed: v,
 | 
			
		||||
      value: () => v,
 | 
			
		||||
      get: (i) => v,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const field = findField(frame, config.field);
 | 
			
		||||
  if (!field) {
 | 
			
		||||
    const v = '';
 | 
			
		||||
    return {
 | 
			
		||||
      isAssumed: true,
 | 
			
		||||
      fixed: v,
 | 
			
		||||
      value: () => v,
 | 
			
		||||
      get: (i) => v,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (mode === ResourceDimensionMode.Mapping) {
 | 
			
		||||
    const mapper = (v: any) => `${v}`;
 | 
			
		||||
    return {
 | 
			
		||||
      field,
 | 
			
		||||
      get: (i) => mapper(field.values.get(i)),
 | 
			
		||||
      value: () => mapper(getLastNotNullFieldValue(field)),
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    field,
 | 
			
		||||
    get: field.values.get,
 | 
			
		||||
    value: () => getLastNotNullFieldValue(field),
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,19 +1,23 @@
 | 
			
		|||
import { DataFrame } from '@grafana/data';
 | 
			
		||||
import { getMinMaxAndDelta } from '../../../../../../packages/grafana-data/src/field/scale';
 | 
			
		||||
import { getMinMaxAndDelta } from '../../../../packages/grafana-data/src/field/scale';
 | 
			
		||||
import { ScaleDimensionConfig, DimensionSupplier, ScaleDimensionOptions } from './types';
 | 
			
		||||
import { findField } from './utils';
 | 
			
		||||
import { findField, getLastNotNullFieldValue } from './utils';
 | 
			
		||||
 | 
			
		||||
//---------------------------------------------------------
 | 
			
		||||
// Scale dimension
 | 
			
		||||
//---------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
export function getScaledDimension(frame: DataFrame, config: ScaleDimensionConfig): DimensionSupplier<number> {
 | 
			
		||||
export function getScaledDimension(
 | 
			
		||||
  frame: DataFrame | undefined,
 | 
			
		||||
  config: ScaleDimensionConfig
 | 
			
		||||
): DimensionSupplier<number> {
 | 
			
		||||
  const field = findField(frame, config.field);
 | 
			
		||||
  if (!field) {
 | 
			
		||||
    const v = config.fixed ?? 0;
 | 
			
		||||
    return {
 | 
			
		||||
      isAssumed: Boolean(config.field?.length) || !config.fixed,
 | 
			
		||||
      fixed: v,
 | 
			
		||||
      value: () => v,
 | 
			
		||||
      get: () => v,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -23,12 +27,12 @@ export function getScaledDimension(frame: DataFrame, config: ScaleDimensionConfi
 | 
			
		|||
  if (values.length < 1 || delta <= 0 || info.delta <= 0) {
 | 
			
		||||
    return {
 | 
			
		||||
      fixed: config.min,
 | 
			
		||||
      value: () => config.min,
 | 
			
		||||
      get: () => config.min,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    get: (i) => {
 | 
			
		||||
  const get = (i: number) => {
 | 
			
		||||
    const value = field.values.get(i);
 | 
			
		||||
    let percent = 0;
 | 
			
		||||
    if (value !== -Infinity) {
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +44,11 @@ export function getScaledDimension(frame: DataFrame, config: ScaleDimensionConfi
 | 
			
		|||
      percent = 0;
 | 
			
		||||
    }
 | 
			
		||||
    return config.min + percent * delta;
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    get,
 | 
			
		||||
    value: () => get(getLastNotNullFieldValue(field)),
 | 
			
		||||
    field,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,57 @@
 | 
			
		|||
import { DataFrame, formattedValueToString } from '@grafana/data';
 | 
			
		||||
import { DimensionSupplier, TextDimensionConfig, TextDimensionMode } from './types';
 | 
			
		||||
import { findField, getLastNotNullFieldValue } from './utils';
 | 
			
		||||
 | 
			
		||||
//---------------------------------------------------------
 | 
			
		||||
// Resource dimension
 | 
			
		||||
//---------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
export function getTextDimension(frame: DataFrame | undefined, config: TextDimensionConfig): DimensionSupplier<string> {
 | 
			
		||||
  let v = config.fixed;
 | 
			
		||||
  const mode = config.mode ?? TextDimensionMode.Fixed;
 | 
			
		||||
  if (mode === TextDimensionMode.Fixed) {
 | 
			
		||||
    return {
 | 
			
		||||
      isAssumed: !Boolean(v),
 | 
			
		||||
      fixed: v,
 | 
			
		||||
      value: () => v,
 | 
			
		||||
      get: (i) => v,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const field = findField(frame, config.field);
 | 
			
		||||
  if (mode === TextDimensionMode.Template) {
 | 
			
		||||
    const disp = (v: any) => {
 | 
			
		||||
      return `TEMPLATE[${config.fixed} // ${v}]`;
 | 
			
		||||
    };
 | 
			
		||||
    if (!field) {
 | 
			
		||||
      v = disp('');
 | 
			
		||||
      return {
 | 
			
		||||
        isAssumed: true,
 | 
			
		||||
        fixed: v,
 | 
			
		||||
        value: () => v,
 | 
			
		||||
        get: (i) => v,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
    return {
 | 
			
		||||
      field,
 | 
			
		||||
      get: (i) => disp(field.values.get(i)),
 | 
			
		||||
      value: () => disp(getLastNotNullFieldValue(field)),
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!field) {
 | 
			
		||||
    return {
 | 
			
		||||
      isAssumed: true,
 | 
			
		||||
      fixed: v,
 | 
			
		||||
      value: () => v,
 | 
			
		||||
      get: (i) => v,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let disp = (v: any) => formattedValueToString(field.display!(v));
 | 
			
		||||
  return {
 | 
			
		||||
    field,
 | 
			
		||||
    get: (i) => disp(field.values.get(i)),
 | 
			
		||||
    value: () => disp(getLastNotNullFieldValue(field)),
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -17,10 +17,15 @@ export interface DimensionSupplier<T = any> {
 | 
			
		|||
  field?: Field;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Explicit value -- if == null, then need a value pr index
 | 
			
		||||
   * Explicit value -- if == null, then need a value for each index
 | 
			
		||||
   */
 | 
			
		||||
  fixed?: T;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * A single value -- typically last
 | 
			
		||||
   */
 | 
			
		||||
  value: () => T;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Supplier for the dimension value
 | 
			
		||||
   */
 | 
			
		||||
| 
						 | 
				
			
			@ -41,5 +46,36 @@ export interface ScaleDimensionOptions {
 | 
			
		|||
  hideRange?: boolean; // false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface TextDimensionOptions {
 | 
			
		||||
  // anything?
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum TextDimensionMode {
 | 
			
		||||
  Fixed = 'fixed',
 | 
			
		||||
  Field = 'field',
 | 
			
		||||
  Template = 'template',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface TextDimensionConfig extends BaseDimensionConfig<string> {
 | 
			
		||||
  mode: TextDimensionMode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** Use the color value from field configs */
 | 
			
		||||
export interface ColorDimensionConfig extends BaseDimensionConfig<string> {}
 | 
			
		||||
 | 
			
		||||
/** Places that use the value */
 | 
			
		||||
export interface ResourceDimensionOptions {
 | 
			
		||||
  resourceType: 'icon' | 'image';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum ResourceDimensionMode {
 | 
			
		||||
  Fixed = 'fixed',
 | 
			
		||||
  Field = 'field',
 | 
			
		||||
  Mapping = 'mapping',
 | 
			
		||||
  // pattern? uses field in the pattern
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** Get the path to a resource (URL) */
 | 
			
		||||
export interface ResourceDimensionConfig extends BaseDimensionConfig<string> {
 | 
			
		||||
  mode: ResourceDimensionMode;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,38 @@
 | 
			
		|||
import { DataFrame, Field, getFieldDisplayName, ReducerID } from '@grafana/data';
 | 
			
		||||
 | 
			
		||||
export function findField(frame?: DataFrame, name?: string): Field | undefined {
 | 
			
		||||
  if (!frame || !name?.length) {
 | 
			
		||||
    return undefined;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (const field of frame.fields) {
 | 
			
		||||
    if (name === field.name) {
 | 
			
		||||
      return field;
 | 
			
		||||
    }
 | 
			
		||||
    const disp = getFieldDisplayName(field, frame);
 | 
			
		||||
    if (name === disp) {
 | 
			
		||||
      return field;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return undefined;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getLastNotNullFieldValue<T>(field: Field): T {
 | 
			
		||||
  const calcs = field.state?.calcs;
 | 
			
		||||
  if (calcs) {
 | 
			
		||||
    const v = calcs[ReducerID.lastNotNull];
 | 
			
		||||
    if (v != null) {
 | 
			
		||||
      return v as T;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const data = field.values;
 | 
			
		||||
  let idx = data.length - 1;
 | 
			
		||||
  while (idx >= 0) {
 | 
			
		||||
    const v = data.get(idx--);
 | 
			
		||||
    if (v != null) {
 | 
			
		||||
      return v;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return undefined as any;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,18 +0,0 @@
 | 
			
		|||
import { DataFrame, Field, getFieldDisplayName } from '@grafana/data';
 | 
			
		||||
 | 
			
		||||
export function findField(frame: DataFrame, name?: string): Field | undefined {
 | 
			
		||||
  if (!name?.length) {
 | 
			
		||||
    return undefined;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (const field of frame.fields) {
 | 
			
		||||
    if (name === field.name) {
 | 
			
		||||
      return field;
 | 
			
		||||
    }
 | 
			
		||||
    const disp = getFieldDisplayName(field, frame);
 | 
			
		||||
    if (name === disp) {
 | 
			
		||||
      return field;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return undefined;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -3,7 +3,7 @@ import { StandardEditorProps, SelectableValue } from '@grafana/data';
 | 
			
		|||
import { Button, InlineField, InlineFieldRow, Select, VerticalGroup } from '@grafana/ui';
 | 
			
		||||
import { GeomapPanelOptions, MapViewConfig } from '../types';
 | 
			
		||||
import { centerPointRegistry, MapCenterID } from '../view';
 | 
			
		||||
import { NumberInput } from '../components/NumberInput';
 | 
			
		||||
import { NumberInput } from 'app/features/dimensions/editors/NumberInput';
 | 
			
		||||
import { lastGeomapPanelInstance } from '../GeomapPanel';
 | 
			
		||||
import { toLonLat } from 'ol/proj';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,7 @@ import { Label, stylesFactory } from '@grafana/ui';
 | 
			
		|||
import { formattedValueToString, getFieldColorModeForField, GrafanaTheme } from '@grafana/data';
 | 
			
		||||
import { css } from '@emotion/css';
 | 
			
		||||
import { config } from 'app/core/config';
 | 
			
		||||
import { DimensionSupplier } from '../../dims/types';
 | 
			
		||||
import { DimensionSupplier } from 'app/features/dimensions';
 | 
			
		||||
import { getMinMaxAndDelta } from '../../../../../../../packages/grafana-data/src/field/scale';
 | 
			
		||||
 | 
			
		||||
export interface MarkersLegendProps {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,9 +11,8 @@ import Feature from 'ol/Feature';
 | 
			
		|||
import * as layer from 'ol/layer';
 | 
			
		||||
import * as source from 'ol/source';
 | 
			
		||||
import { dataFrameToPoints, getLocationMatchers } from '../../utils/location';
 | 
			
		||||
import { ScaleDimensionConfig, } from '../../dims/types';
 | 
			
		||||
import { ScaleDimensionEditor } from '../../dims/editors/ScaleDimensionEditor';
 | 
			
		||||
import { getScaledDimension } from '../../dims/scale';
 | 
			
		||||
import { ScaleDimensionConfig, getScaledDimension } from 'app/features/dimensions';
 | 
			
		||||
import { ScaleDimensionEditor } from 'app/features/dimensions/editors';
 | 
			
		||||
 | 
			
		||||
// Configuration options for Heatmap overlays
 | 
			
		||||
export interface HeatmapConfig {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,5 @@
 | 
			
		|||
 | 
			
		||||
import React, { ReactNode } from 'react';
 | 
			
		||||
import { MapLayerRegistryItem, MapLayerOptions, PanelData, GrafanaTheme2, FrameGeometrySourceMode } from '@grafana/data';
 | 
			
		||||
import Map from 'ol/Map';
 | 
			
		||||
import Feature from 'ol/Feature';
 | 
			
		||||
| 
						 | 
				
			
			@ -6,17 +8,12 @@ import * as source from 'ol/source';
 | 
			
		|||
 | 
			
		||||
import tinycolor from 'tinycolor2';
 | 
			
		||||
import { dataFrameToPoints, getLocationMatchers } from '../../utils/location';
 | 
			
		||||
import { ColorDimensionConfig, ScaleDimensionConfig, } from '../../dims/types';
 | 
			
		||||
import { getScaledDimension, } from '../../dims/scale';
 | 
			
		||||
import { getColorDimension, } from '../../dims/color';
 | 
			
		||||
import { ScaleDimensionEditor } from '../../dims/editors/ScaleDimensionEditor';
 | 
			
		||||
import { ColorDimensionEditor } from '../../dims/editors/ColorDimensionEditor';
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { ColorDimensionConfig, ScaleDimensionConfig, getScaledDimension, getColorDimension } from 'app/features/dimensions';
 | 
			
		||||
import { ScaleDimensionEditor, ColorDimensionEditor } from 'app/features/dimensions/editors';
 | 
			
		||||
import { ObservablePropsWrapper } from '../../components/ObservablePropsWrapper';
 | 
			
		||||
import { ReplaySubject } from 'rxjs';
 | 
			
		||||
import { MarkersLegend, MarkersLegendProps } from './MarkersLegend';
 | 
			
		||||
import { ReactNode } from 'react';
 | 
			
		||||
import { circleMarker, markerMakers } from '../../utils/regularShapes';
 | 
			
		||||
import { ReplaySubject } from 'rxjs';
 | 
			
		||||
 | 
			
		||||
// Configuration options for Circle overlays
 | 
			
		||||
export interface MarkersConfig {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue