[Metrics UI] Update position of legend and it's controls
This commit is contained in:
parent
c0d6a612d6
commit
4e0e3752ba
|
@ -47,6 +47,7 @@ export const BottomDrawer: React.FC<{
|
|||
return (
|
||||
<BottomActionContainer ref={isOpen ? measureRef : null} isOpen={isOpen} outerWidth={width}>
|
||||
<BottomActionTopBar ref={isOpen ? null : measureRef}>
|
||||
<LeftSideSpacer />
|
||||
<EuiFlexItem grow={false}>
|
||||
<ShowHideButton
|
||||
aria-expanded={isOpen}
|
||||
|
@ -56,17 +57,6 @@ export const BottomDrawer: React.FC<{
|
|||
{isOpen ? hideHistory : showHistory}
|
||||
</ShowHideButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
style={{
|
||||
position: 'relative',
|
||||
minWidth: 400,
|
||||
height: '16px',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</EuiFlexItem>
|
||||
<RightSideSpacer />
|
||||
</BottomActionTopBar>
|
||||
<EuiFlexGroup style={{ marginTop: 0 }}>
|
||||
<Timeline isVisible={isOpen} interval={interval} yAxisFormatter={formatter} />
|
||||
|
@ -97,6 +87,6 @@ const ShowHideButton = euiStyled(EuiButtonEmpty).attrs({ size: 's' })`
|
|||
width: 140px;
|
||||
`;
|
||||
|
||||
const RightSideSpacer = euiStyled(EuiSpacer).attrs({ size: 'xs' })`
|
||||
const LeftSideSpacer = euiStyled(EuiSpacer).attrs({ size: 'xs' })`
|
||||
width: 140px;
|
||||
`;
|
||||
|
|
|
@ -17,9 +17,13 @@ import { calculateBoundsFromNodes } from '../lib/calculate_bounds_from_nodes';
|
|||
import { PageContent } from '../../../../components/page';
|
||||
import { useWaffleTimeContext } from '../hooks/use_waffle_time';
|
||||
import { useWaffleFiltersContext } from '../hooks/use_waffle_filters';
|
||||
import { DEFAULT_LEGEND, useWaffleOptionsContext } from '../hooks/use_waffle_options';
|
||||
import {
|
||||
DEFAULT_LEGEND,
|
||||
useWaffleOptionsContext,
|
||||
WaffleLegendOptions,
|
||||
} from '../hooks/use_waffle_options';
|
||||
import { useSourceContext } from '../../../../containers/metrics_source';
|
||||
import { InfraFormatterType } from '../../../../lib/lib';
|
||||
import { InfraFormatterType, InfraWaffleMapBounds } from '../../../../lib/lib';
|
||||
import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common';
|
||||
import { Toolbar } from './toolbars/toolbar';
|
||||
import { ViewSwitcher } from './waffle/view_switcher';
|
||||
|
@ -28,6 +32,7 @@ import { createLegend } from '../lib/create_legend';
|
|||
import { useWaffleViewState } from '../hooks/use_waffle_view_state';
|
||||
import { BottomDrawer } from './bottom_drawer';
|
||||
import { Legend } from './waffle/legend';
|
||||
import { LegendControls } from './waffle/legend_controls';
|
||||
|
||||
interface Props {
|
||||
shouldLoadDefault: boolean;
|
||||
|
@ -38,6 +43,12 @@ interface Props {
|
|||
loading: boolean;
|
||||
}
|
||||
|
||||
interface LegendControlOptions {
|
||||
auto: boolean;
|
||||
bounds: InfraWaffleMapBounds;
|
||||
legend: WaffleLegendOptions;
|
||||
}
|
||||
|
||||
export const Layout = React.memo(
|
||||
({ shouldLoadDefault, currentView, reload, interval, nodes, loading }: Props) => {
|
||||
const [showLoading, setShowLoading] = useState(true);
|
||||
|
@ -52,6 +63,9 @@ export const Layout = React.memo(
|
|||
autoBounds,
|
||||
boundsOverride,
|
||||
legend,
|
||||
changeBoundsOverride,
|
||||
changeAutoBounds,
|
||||
changeLegend,
|
||||
} = useWaffleOptionsContext();
|
||||
const { currentTime, jumpToTime, isAutoReloading } = useWaffleTimeContext();
|
||||
const { applyFilterQuery } = useWaffleFiltersContext();
|
||||
|
@ -118,6 +132,15 @@ export const Layout = React.memo(
|
|||
setShowLoading(!hasNodes);
|
||||
}, [nodes]);
|
||||
|
||||
const handleLegendControlChange = useCallback(
|
||||
(opts: LegendControlOptions) => {
|
||||
changeBoundsOverride(opts.bounds);
|
||||
changeAutoBounds(opts.auto);
|
||||
changeLegend(opts.legend);
|
||||
},
|
||||
[changeBoundsOverride, changeAutoBounds, changeLegend]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageContent>
|
||||
|
@ -137,6 +160,18 @@ export const Layout = React.memo(
|
|||
gutterSize="m"
|
||||
>
|
||||
<Toolbar nodeType={nodeType} currentTime={currentTime} />
|
||||
{view === 'map' && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<LegendControls
|
||||
options={legend != null ? legend : DEFAULT_LEGEND}
|
||||
dataBounds={dataBounds}
|
||||
bounds={bounds}
|
||||
autoBounds={autoBounds}
|
||||
boundsOverride={boundsOverride}
|
||||
onChange={handleLegendControlChange}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
<EuiFlexItem grow={false}>
|
||||
<ViewSwitcher view={view} onChange={changeView} />
|
||||
</EuiFlexItem>
|
||||
|
@ -167,14 +202,7 @@ export const Layout = React.memo(
|
|||
interval={interval}
|
||||
formatter={formatter}
|
||||
width={width}
|
||||
>
|
||||
<Legend
|
||||
formatter={formatter}
|
||||
bounds={bounds}
|
||||
dataBounds={dataBounds}
|
||||
legend={options.legend}
|
||||
/>
|
||||
</BottomDrawer>
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
@ -182,6 +210,14 @@ export const Layout = React.memo(
|
|||
</>
|
||||
)}
|
||||
</AutoSizer>
|
||||
{view === 'map' && (
|
||||
<Legend
|
||||
formatter={formatter}
|
||||
bounds={bounds}
|
||||
dataBounds={dataBounds}
|
||||
legend={options.legend}
|
||||
/>
|
||||
)}
|
||||
</MainContainer>
|
||||
)}
|
||||
</AutoSizer>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
|
||||
import {
|
||||
|
@ -17,13 +17,7 @@ import {
|
|||
GradientLegendRT,
|
||||
} from '../../../../../lib/lib';
|
||||
import { GradientLegend } from './gradient_legend';
|
||||
import { LegendControls } from './legend_controls';
|
||||
import { StepLegend } from './steps_legend';
|
||||
import {
|
||||
DEFAULT_LEGEND,
|
||||
useWaffleOptionsContext,
|
||||
WaffleLegendOptions,
|
||||
} from '../../hooks/use_waffle_options';
|
||||
import { SteppedGradientLegend } from './stepped_gradient_legend';
|
||||
interface Props {
|
||||
legend: InfraWaffleMapLegend;
|
||||
|
@ -32,39 +26,9 @@ interface Props {
|
|||
formatter: InfraFormatter;
|
||||
}
|
||||
|
||||
interface LegendControlOptions {
|
||||
auto: boolean;
|
||||
bounds: InfraWaffleMapBounds;
|
||||
legend: WaffleLegendOptions;
|
||||
}
|
||||
|
||||
export const Legend: React.FC<Props> = ({ dataBounds, legend, bounds, formatter }) => {
|
||||
const {
|
||||
changeBoundsOverride,
|
||||
changeAutoBounds,
|
||||
autoBounds,
|
||||
legend: legendOptions,
|
||||
changeLegend,
|
||||
boundsOverride,
|
||||
} = useWaffleOptionsContext();
|
||||
const handleChange = useCallback(
|
||||
(options: LegendControlOptions) => {
|
||||
changeBoundsOverride(options.bounds);
|
||||
changeAutoBounds(options.auto);
|
||||
changeLegend(options.legend);
|
||||
},
|
||||
[changeBoundsOverride, changeAutoBounds, changeLegend]
|
||||
);
|
||||
export const Legend: React.FC<Props> = ({ legend, bounds, formatter }) => {
|
||||
return (
|
||||
<LegendContainer>
|
||||
<LegendControls
|
||||
options={legendOptions != null ? legendOptions : DEFAULT_LEGEND}
|
||||
dataBounds={dataBounds}
|
||||
bounds={bounds}
|
||||
autoBounds={autoBounds}
|
||||
boundsOverride={boundsOverride}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
{GradientLegendRT.is(legend) && (
|
||||
<GradientLegend formatter={formatter} legend={legend} bounds={bounds} />
|
||||
)}
|
||||
|
@ -78,7 +42,7 @@ export const Legend: React.FC<Props> = ({ dataBounds, legend, bounds, formatter
|
|||
|
||||
const LegendContainer = euiStyled.div`
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
left: 10px;
|
||||
top: 0px;
|
||||
right: 10px;
|
||||
bottom: 0px;
|
||||
`;
|
||||
|
|
|
@ -26,7 +26,6 @@ import { i18n } from '@kbn/i18n';
|
|||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React, { SyntheticEvent, useState, useCallback, useEffect } from 'react';
|
||||
import { first, last } from 'lodash';
|
||||
import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
|
||||
import { InfraWaffleMapBounds, InventoryColorPalette, PALETTES } from '../../../../../lib/lib';
|
||||
import { WaffleLegendOptions } from '../../hooks/use_waffle_options';
|
||||
import { getColorPalette } from '../../lib/get_color_palette';
|
||||
|
@ -78,12 +77,15 @@ export const LegendControls = ({
|
|||
|
||||
const buttonComponent = (
|
||||
<EuiButtonIcon
|
||||
iconType="controlsHorizontal"
|
||||
color="text"
|
||||
iconType="color"
|
||||
color="primary"
|
||||
display="base"
|
||||
size="m"
|
||||
aria-label={i18n.translate('xpack.infra.legendControls.buttonLabel', {
|
||||
defaultMessage: 'configure legend',
|
||||
})}
|
||||
onClick={() => setPopoverState(true)}
|
||||
style={{ width: 50 }}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -179,182 +181,174 @@ export const LegendControls = ({
|
|||
: [];
|
||||
|
||||
return (
|
||||
<ControlContainer>
|
||||
<EuiPopover
|
||||
isOpen={isPopoverOpen}
|
||||
closePopover={handleCancelClick}
|
||||
id="legendControls"
|
||||
button={buttonComponent}
|
||||
>
|
||||
<EuiPopoverTitle>Legend Options</EuiPopoverTitle>
|
||||
<EuiForm style={{ minWidth: 400 }}>
|
||||
<EuiFormRow
|
||||
display="columnCompressed"
|
||||
label={i18n.translate('xpack.infra.legendControls.colorPaletteLabel', {
|
||||
defaultMessage: 'Color palette',
|
||||
})}
|
||||
>
|
||||
<>
|
||||
<EuiSelect
|
||||
options={PALETTE_OPTIONS}
|
||||
value={draftLegend.palette}
|
||||
id="palette"
|
||||
onChange={handlePaletteChange}
|
||||
compressed
|
||||
/>
|
||||
<EuiSpacer size="m" />
|
||||
<PalettePreview
|
||||
palette={draftLegend.palette}
|
||||
steps={draftLegend.steps}
|
||||
reverse={draftLegend.reverseColors}
|
||||
/>
|
||||
</>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
display="columnCompressed"
|
||||
label={i18n.translate('xpack.infra.legendControls.stepsLabel', {
|
||||
defaultMessage: 'Number of colors',
|
||||
})}
|
||||
>
|
||||
<EuiRange
|
||||
id="steps"
|
||||
min={2}
|
||||
max={18}
|
||||
<EuiPopover
|
||||
isOpen={isPopoverOpen}
|
||||
closePopover={handleCancelClick}
|
||||
id="legendControls"
|
||||
button={buttonComponent}
|
||||
anchorPosition="leftCenter"
|
||||
>
|
||||
<EuiPopoverTitle>Legend Options</EuiPopoverTitle>
|
||||
<EuiForm style={{ minWidth: 400 }}>
|
||||
<EuiFormRow
|
||||
display="columnCompressed"
|
||||
label={i18n.translate('xpack.infra.legendControls.colorPaletteLabel', {
|
||||
defaultMessage: 'Color palette',
|
||||
})}
|
||||
>
|
||||
<>
|
||||
<EuiSelect
|
||||
options={PALETTE_OPTIONS}
|
||||
value={draftLegend.palette}
|
||||
id="palette"
|
||||
onChange={handlePaletteChange}
|
||||
compressed
|
||||
/>
|
||||
<EuiSpacer size="m" />
|
||||
<PalettePreview
|
||||
palette={draftLegend.palette}
|
||||
steps={draftLegend.steps}
|
||||
reverse={draftLegend.reverseColors}
|
||||
/>
|
||||
</>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
display="columnCompressed"
|
||||
label={i18n.translate('xpack.infra.legendControls.stepsLabel', {
|
||||
defaultMessage: 'Number of colors',
|
||||
})}
|
||||
>
|
||||
<EuiRange
|
||||
id="steps"
|
||||
min={2}
|
||||
max={18}
|
||||
step={1}
|
||||
value={draftLegend.steps}
|
||||
onChange={handleStepsChange}
|
||||
showValue
|
||||
fullWidth
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
fullWidth
|
||||
display="columnCompressed"
|
||||
label={i18n.translate('xpack.infra.legendControls.reverseDirectionLabel', {
|
||||
defaultMessage: 'Reverse direction',
|
||||
})}
|
||||
>
|
||||
<EuiSwitch
|
||||
showLabel={false}
|
||||
name="reverseColors"
|
||||
label="reverseColors"
|
||||
checked={draftLegend.reverseColors}
|
||||
onChange={handleReverseColors}
|
||||
compressed
|
||||
style={{
|
||||
position: 'relative',
|
||||
top: '8px',
|
||||
}}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
fullWidth
|
||||
display="columnCompressed"
|
||||
label={i18n.translate('xpack.infra.legendControls.switchLabel', {
|
||||
defaultMessage: 'Auto calculate range',
|
||||
})}
|
||||
>
|
||||
<EuiSwitch
|
||||
showLabel={false}
|
||||
name="bounds"
|
||||
label="bounds"
|
||||
checked={draftAuto}
|
||||
onChange={handleAutoChange}
|
||||
compressed
|
||||
style={{
|
||||
position: 'relative',
|
||||
top: '8px',
|
||||
}}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
fullWidth
|
||||
label={
|
||||
<SwatchLabel
|
||||
color={first(paletteColors)!}
|
||||
label={i18n.translate('xpack.infra.legendControls.minLabel', {
|
||||
defaultMessage: 'Minimum',
|
||||
})}
|
||||
/>
|
||||
}
|
||||
isInvalid={!boundsValidRange}
|
||||
display="columnCompressed"
|
||||
error={errors}
|
||||
>
|
||||
<div style={{ maxWidth: 150 }}>
|
||||
<EuiFieldNumber
|
||||
disabled={draftAuto}
|
||||
step={1}
|
||||
value={draftLegend.steps}
|
||||
onChange={handleStepsChange}
|
||||
showValue
|
||||
fullWidth
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
fullWidth
|
||||
display="columnCompressed"
|
||||
label={i18n.translate('xpack.infra.legendControls.reverseDirectionLabel', {
|
||||
defaultMessage: 'Reverse direction',
|
||||
})}
|
||||
>
|
||||
<EuiSwitch
|
||||
showLabel={false}
|
||||
name="reverseColors"
|
||||
label="reverseColors"
|
||||
checked={draftLegend.reverseColors}
|
||||
onChange={handleReverseColors}
|
||||
value={isNaN(draftBounds.min) ? '' : draftBounds.min}
|
||||
isInvalid={!boundsValidRange}
|
||||
name="legendMin"
|
||||
onChange={handleMinBounds}
|
||||
append="%"
|
||||
compressed
|
||||
style={{
|
||||
position: 'relative',
|
||||
top: '8px',
|
||||
}}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
fullWidth
|
||||
display="columnCompressed"
|
||||
label={i18n.translate('xpack.infra.legendControls.switchLabel', {
|
||||
defaultMessage: 'Auto calculate range',
|
||||
})}
|
||||
>
|
||||
<EuiSwitch
|
||||
showLabel={false}
|
||||
name="bounds"
|
||||
label="bounds"
|
||||
checked={draftAuto}
|
||||
onChange={handleAutoChange}
|
||||
</div>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
fullWidth
|
||||
display="columnCompressed"
|
||||
label={
|
||||
<SwatchLabel
|
||||
color={last(paletteColors)!}
|
||||
label={i18n.translate('xpack.infra.legendControls.maxLabel', {
|
||||
defaultMessage: 'Maxium',
|
||||
})}
|
||||
/>
|
||||
}
|
||||
isInvalid={!boundsValidRange}
|
||||
error={errors}
|
||||
>
|
||||
<div style={{ maxWidth: 150 }}>
|
||||
<EuiFieldNumber
|
||||
disabled={draftAuto}
|
||||
step={1}
|
||||
isInvalid={!boundsValidRange}
|
||||
value={isNaN(draftBounds.max) ? '' : draftBounds.max}
|
||||
name="legendMax"
|
||||
onChange={handleMaxBounds}
|
||||
append="%"
|
||||
compressed
|
||||
style={{
|
||||
position: 'relative',
|
||||
top: '8px',
|
||||
}}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
fullWidth
|
||||
label={
|
||||
<SwatchLabel
|
||||
color={first(paletteColors)!}
|
||||
label={i18n.translate('xpack.infra.legendControls.minLabel', {
|
||||
defaultMessage: 'Minimum',
|
||||
})}
|
||||
</div>
|
||||
</EuiFormRow>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFlexGroup justifyContent="flexEnd" responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty type="submit" size="s" onClick={handleCancelClick}>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.legendControls.cancelButton"
|
||||
defaultMessage="Cancel"
|
||||
/>
|
||||
}
|
||||
isInvalid={!boundsValidRange}
|
||||
display="columnCompressed"
|
||||
error={errors}
|
||||
>
|
||||
<div style={{ maxWidth: 150 }}>
|
||||
<EuiFieldNumber
|
||||
disabled={draftAuto}
|
||||
step={1}
|
||||
value={isNaN(draftBounds.min) ? '' : draftBounds.min}
|
||||
isInvalid={!boundsValidRange}
|
||||
name="legendMin"
|
||||
onChange={handleMinBounds}
|
||||
append="%"
|
||||
compressed
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
type="submit"
|
||||
size="s"
|
||||
fill
|
||||
disabled={commited || !boundsValidRange}
|
||||
onClick={handleApplyClick}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.legendControls.applyButton"
|
||||
defaultMessage="Apply"
|
||||
/>
|
||||
</div>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
fullWidth
|
||||
display="columnCompressed"
|
||||
label={
|
||||
<SwatchLabel
|
||||
color={last(paletteColors)!}
|
||||
label={i18n.translate('xpack.infra.legendControls.maxLabel', {
|
||||
defaultMessage: 'Maxium',
|
||||
})}
|
||||
/>
|
||||
}
|
||||
isInvalid={!boundsValidRange}
|
||||
error={errors}
|
||||
>
|
||||
<div style={{ maxWidth: 150 }}>
|
||||
<EuiFieldNumber
|
||||
disabled={draftAuto}
|
||||
step={1}
|
||||
isInvalid={!boundsValidRange}
|
||||
value={isNaN(draftBounds.max) ? '' : draftBounds.max}
|
||||
name="legendMax"
|
||||
onChange={handleMaxBounds}
|
||||
append="%"
|
||||
compressed
|
||||
/>
|
||||
</div>
|
||||
</EuiFormRow>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFlexGroup justifyContent="flexEnd" responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty type="submit" size="s" onClick={handleCancelClick}>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.legendControls.cancelButton"
|
||||
defaultMessage="Cancel"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
type="submit"
|
||||
size="s"
|
||||
fill
|
||||
disabled={commited || !boundsValidRange}
|
||||
onClick={handleApplyClick}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.legendControls.applyButton"
|
||||
defaultMessage="Apply"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiForm>
|
||||
</EuiPopover>
|
||||
</ControlContainer>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiForm>
|
||||
</EuiPopover>
|
||||
);
|
||||
};
|
||||
|
||||
const ControlContainer = euiStyled.div`
|
||||
position: absolute;
|
||||
top: -20px;
|
||||
right: 6px;
|
||||
bottom: 0;
|
||||
`;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiText } from '@elastic/eui';
|
||||
import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common';
|
||||
import {
|
||||
InfraWaffleMapBounds,
|
||||
|
@ -22,10 +23,7 @@ type TickValue = 0 | 1;
|
|||
export const SteppedGradientLegend: React.FC<Props> = ({ legend, bounds, formatter }) => {
|
||||
return (
|
||||
<LegendContainer>
|
||||
<Ticks>
|
||||
<TickLabel value={0} bounds={bounds} formatter={formatter} />
|
||||
<TickLabel value={1} bounds={bounds} formatter={formatter} />
|
||||
</Ticks>
|
||||
<TickLabel value={0} bounds={bounds} formatter={formatter} />
|
||||
<GradientContainer>
|
||||
{legend.rules.map((rule, index) => (
|
||||
<GradientStep
|
||||
|
@ -34,6 +32,7 @@ export const SteppedGradientLegend: React.FC<Props> = ({ legend, bounds, formatt
|
|||
/>
|
||||
))}
|
||||
</GradientContainer>
|
||||
<TickLabel value={1} bounds={bounds} formatter={formatter} />
|
||||
</LegendContainer>
|
||||
);
|
||||
};
|
||||
|
@ -46,62 +45,41 @@ interface TickProps {
|
|||
|
||||
const TickLabel = ({ value, bounds, formatter }: TickProps) => {
|
||||
const normalizedValue = value === 0 ? bounds.min : bounds.max * value;
|
||||
const style = { left: `${value * 100}%` };
|
||||
const label = formatter(normalizedValue);
|
||||
return <Tick style={style}>{label}</Tick>;
|
||||
return (
|
||||
<div>
|
||||
<EuiText size="xs">{label}</EuiText>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const GradientStep = euiStyled.div`
|
||||
height: ${(props) => props.theme.eui.paddingSizes.s};
|
||||
flex: 1 1 auto;
|
||||
&:first-child {
|
||||
border-radius: ${(props) => props.theme.eui.euiBorderRadius} 0 0 ${(props) =>
|
||||
props.theme.eui.euiBorderRadius};
|
||||
}
|
||||
&:last-child {
|
||||
border-radius: 0 ${(props) => props.theme.eui.euiBorderRadius} ${(props) =>
|
||||
props.theme.eui.euiBorderRadius} 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const Ticks = euiStyled.div`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
top: -18px;
|
||||
`;
|
||||
|
||||
const Tick = euiStyled.div`
|
||||
position: absolute;
|
||||
font-size: 11px;
|
||||
text-align: center;
|
||||
top: 0;
|
||||
left: 0;
|
||||
white-space: nowrap;
|
||||
transform: translate(-50%, 0);
|
||||
&:first-child {
|
||||
padding-left: 5px;
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
&:last-child {
|
||||
padding-right: 5px;
|
||||
transform: translate(-100%, 0);
|
||||
}
|
||||
`;
|
||||
|
||||
const GradientContainer = euiStyled.div`
|
||||
display: flex;
|
||||
flex-direction; row;
|
||||
align-items: stretch;
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
const LegendContainer = euiStyled.div`
|
||||
position: absolute;
|
||||
height: 10px;
|
||||
top: 0;
|
||||
right: -16px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const GradientContainer = euiStyled.div`
|
||||
height: 200px;
|
||||
width: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
`;
|
||||
|
||||
const GradientStep = euiStyled.div`
|
||||
flex: 1 1 auto;
|
||||
&:first-child {
|
||||
border-radius: ${(props) => props.theme.eui.euiBorderRadius} ${(props) =>
|
||||
props.theme.eui.euiBorderRadius} 0 0;
|
||||
}
|
||||
&:last-child {
|
||||
border-radius: 0 0 ${(props) => props.theme.eui.euiBorderRadius} ${(props) =>
|
||||
props.theme.eui.euiBorderRadius};
|
||||
}
|
||||
`;
|
||||
|
|
Loading…
Reference in a new issue