Files
grafana/public/app/features/dashboard/components/PanelEditor/OverrideEditor.tsx
2020-02-14 13:46:08 +01:00

178 lines
4.8 KiB
TypeScript

import React, { useCallback } from 'react';
import {
ConfigOverrideRule,
DataFrame,
DynamicConfigValue,
FieldConfigEditorRegistry,
standardFieldConfigEditorRegistry,
VariableSuggestionsScope,
SelectableValue,
GrafanaTheme,
} from '@grafana/data';
import { fieldMatchersUI, stylesFactory, useTheme, ValuePicker, selectThemeVariant } from '@grafana/ui';
import { DynamicConfigValueEditor } from './DynamicConfigValueEditor';
import { OverrideHeader } from './OverrideHeader';
import { getDataLinksVariableSuggestions } from '../../../panel/panellinks/link_srv';
import { css } from 'emotion';
interface OverrideEditorProps {
data: DataFrame[];
override: ConfigOverrideRule;
onChange: (config: ConfigOverrideRule) => void;
onRemove: () => void;
customPropertiesRegistry?: FieldConfigEditorRegistry;
configPropertiesOptions: Array<SelectableValue<string>>;
}
export const OverrideEditor: React.FC<OverrideEditorProps> = ({
data,
override,
onChange,
onRemove,
customPropertiesRegistry,
configPropertiesOptions,
}) => {
const theme = useTheme();
const onMatcherConfigChange = useCallback(
(matcherConfig: any) => {
override.matcher.options = matcherConfig;
onChange(override);
},
[override, onChange]
);
const onDynamicConfigValueChange = useCallback(
(index: number, value: DynamicConfigValue) => {
override.properties[index].value = value;
onChange(override);
},
[override, onChange]
);
const onDynamicConfigValueRemove = useCallback(
(index: number) => {
override.properties.splice(index, 1);
onChange(override);
},
[override, onChange]
);
const onDynamicConfigValueAdd = useCallback(
(prop: string, custom?: boolean) => {
const propertyConfig: DynamicConfigValue = {
prop,
custom,
};
if (override.properties) {
override.properties.push(propertyConfig);
} else {
override.properties = [propertyConfig];
}
onChange(override);
},
[override, onChange]
);
const matcherUi = fieldMatchersUI.get(override.matcher.id);
const styles = getStyles(theme);
return (
<div className={styles.wrapper}>
<div className={styles.headerWrapper}>
<OverrideHeader onRemove={onRemove} title={matcherUi.name} description={matcherUi.description} />
<div className={styles.matcherUi}>
<matcherUi.component
matcher={matcherUi.matcher}
data={data}
options={override.matcher.options}
onChange={option => onMatcherConfigChange(option)}
/>
</div>
</div>
<div>
{override.properties.map((p, j) => {
const reg = p.custom ? customPropertiesRegistry : standardFieldConfigEditorRegistry;
const item = reg?.getIfExists(p.prop);
if (!item) {
return <div>Unknown property: {p.prop}</div>;
}
return (
<div key={`${p.prop}/${j}`}>
<DynamicConfigValueEditor
onChange={value => onDynamicConfigValueChange(j, value)}
onRemove={() => onDynamicConfigValueRemove(j)}
property={p}
editorsRegistry={reg}
context={{
data,
getSuggestions: (scope?: VariableSuggestionsScope) => getDataLinksVariableSuggestions(data, scope),
}}
/>
</div>
);
})}
<div className={styles.propertyPickerWrapper}>
<ValuePicker
label="Set config property"
icon="plus"
options={configPropertiesOptions}
variant={'link'}
onChange={o => {
onDynamicConfigValueAdd(o.value, o.custom);
}}
/>
</div>
</div>
</div>
);
};
const getStyles = stylesFactory((theme: GrafanaTheme) => {
const borderColor = selectThemeVariant(
{
light: theme.colors.gray85,
dark: theme.colors.dark9,
},
theme.type
);
const headerBg = selectThemeVariant(
{
light: theme.colors.white,
dark: theme.colors.dark1,
},
theme.type
);
const shadow = selectThemeVariant(
{
light: theme.colors.gray85,
dark: theme.colors.black,
},
theme.type
);
return {
wrapper: css`
border: 1px dashed ${borderColor};
margin-bottom: ${theme.spacing.md};
transition: box-shadow 0.5s cubic-bezier(0.19, 1, 0.22, 1);
box-shadow: none;
&:hover {
box-shadow: 0 0 10px ${shadow};
}
`,
headerWrapper: css`
background: ${headerBg};
padding: ${theme.spacing.xs} 0;
`,
matcherUi: css`
padding: ${theme.spacing.sm};
`,
propertyPickerWrapper: css`
border-top: 1px solid ${borderColor};
`,
};
});