Skip to content

Commit

Permalink
refactor(legend): add margins and use new kibana legend toggle
Browse files Browse the repository at this point in the history
new the legend respect the chart theme margins. The toggle is now moved to the bottom left of the
chart as in kibana new design.

fix elastic#34
  • Loading branch information
markov00 committed Feb 15, 2019
1 parent 257e69c commit 830e564
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 94 deletions.
92 changes: 56 additions & 36 deletions src/components/_legend.scss
Original file line number Diff line number Diff line change
@@ -1,101 +1,121 @@
$euiChartLegendMaxWidth: $euiSize * 10 + $euiSize;
$euiChartLegendMaxHeight: $euiSize * 4 + $euiSize;
$elasticChartsLegendMaxWidth: $euiSize * 10 + $euiSize;
$elasticChartsLegendMaxHeight: $euiSize * 4 + $euiSize;

.euiChartLegend {
.elasticChartsLegend {
position: absolute;
display: flex;
align-items: flex-start;
flex-direction: row;
}
.euiChartLegend--collapsed {
.elasticChartsLegend--collapsed {
width: $euiSize * 2 !important;
height: $euiSize * 2 !important;
.euiChartLegendList {
.elasticChartsLegendList {
display: none;
}
}
.euiChartLegend--debug {
.elasticChartsLegend--debug {
background: red;
}
.euiChartLegend--top {
.elasticChartsLegend--top {
top: 0;
left: 0;
right: 0;
width: auto;
height: $euiChartLegendMaxHeight;
height: $elasticChartsLegendMaxHeight;
flex-direction: column;
order: 1;
.euiChartLegend__listItem {
.elasticChartsLegend__listItem {
min-height: 50%;
width: $euiChartLegendMaxWidth;
min-width: $euiChartLegendMaxWidth;
width: $elasticChartsLegendMaxWidth;
min-width: $elasticChartsLegendMaxWidth;
}
}
.euiChartLegend--bottom {
.elasticChartsLegend--bottom {
bottom: 0;
left: 0;
right: 0;
width: auto;
height: $euiChartLegendMaxHeight;
height: $elasticChartsLegendMaxHeight;
flex-direction: column;
.euiChartLegend__listItem {
.elasticChartsLegend__listItem {
min-height: 50%;
width: $euiChartLegendMaxWidth;
min-width: $euiChartLegendMaxWidth;
width: $elasticChartsLegendMaxWidth;
min-width: $elasticChartsLegendMaxWidth;
}
}
.euiChartLegend--left {
.elasticChartsLegend--left {
top: 0;
bottom: 0;
left: 0;
width: $euiChartLegendMaxWidth;
width: $elasticChartsLegendMaxWidth;
order: 1;
.euiChartLegend__listItem {
.elasticChartsLegend__listItem {
min-width: 100%;
}
}
.euiChartLegend--right {
.elasticChartsLegend--right {
top: 0;
bottom: 0;
right: 0;
width: $euiChartLegendMaxWidth;
.euiChartLegend__listItem {
width: $elasticChartsLegendMaxWidth;
.elasticChartsLegend__listItem {
min-width: 100%;
}
}


.euiChartLegendCollapser {
.elasticChartsLegendCollapser {
width: 2 * $euiSize;
height: 2 * $euiSize;
flex-shrink: 0;
flex-grow: 0;
}
.euiChartLegendCollapser--top {
.elasticChartsLegendCollapser--top {
order: 2;
}
.euiChartLegendCollapser--left {
.elasticChartsLegendCollapser--left {
order: 2;
}



.euiChartLegendListContainer {
.elasticChartsLegendListContainer {
overflow: hidden;
flex-shrink: 1;
flex-grow: 0;
}
.euiChartLegendList {
.elasticChartsLegendList {
overflow-y: auto;
overflow-x: hidden;
height: 100%;
}
.euiChartLegendListItem__title {
width: $euiChartLegendMaxWidth - 4 * $euiSize;
max-width: $euiChartLegendMaxWidth - 4 * $euiSize;
}
.euiChartLegendList__item {

.elasticChartsLegendList__item {
&:hover {
text-decoration: underline;
}
}
}

.elasticChartsLegendListItem__title {
width: $elasticChartsLegendMaxWidth - 4 * $euiSize;
max-width: $elasticChartsLegendMaxWidth - 4 * $euiSize;
}

.elasticChartsLegend__toggle {
border-radius: $euiBorderRadius;
border-bottom-right-radius: 0;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
position: absolute;
bottom: 0;
left: 0;
opacity: 0;
background-color: $euiColorEmptyShade;
transition: opacity $euiAnimSpeedFast $euiAnimSlightResistance,
background-color $euiAnimSpeedFast $euiAnimSlightResistance $euiAnimSpeedExtraSlow;
&:focus {
box-shadow: none;
background-color: $euiFocusBackgroundColor !important;
}
}
.elasticChartsLegend__toggle--isOpen {
background-color: transparentize($euiColorDarkestShade, 0.9);
}
6 changes: 3 additions & 3 deletions src/components/_tooltip.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.euiChartTooltip {
.elasticChartsTooltip {
@include euiBottomShadow($color: $euiColorFullShade);
@include euiFontSizeXS;
pointer-events: none;
Expand Down Expand Up @@ -28,12 +28,12 @@
}
}
}
.euiChartTooltip__label {
.elasticChartsTooltip__label {
// max-width: $euiSizeXL * 3;
font-weight: $euiFontWeightMedium;
color: shade($euiColorGhost, 20%);
}

.euiChartTooltip--hidden {
.elasticChartsTooltip--hidden {
opacity: 0;
}
4 changes: 3 additions & 1 deletion src/components/chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { SpecsParser } from '../specs/specs_parser';
import { ChartStore } from '../state/chart_state';
import { ChartResizer } from './chart_resizer';
import { Legend } from './legend';
import { LegendButton } from './legend_button';
import { ReactiveChart as ReactChart } from './react_canvas/reactive_chart';
import { ReactiveChart as SVGChart } from './svg/reactive_chart';
import { Tooltips } from './tooltips';
Expand Down Expand Up @@ -36,7 +37,7 @@ export class Chart extends React.Component<ChartProps> {
} else {
containerStyle = {};
}
const chartClass = classNames('elasticcharts', className);
const chartClass = classNames('elasticCharts', className);
return (
<Provider chartStore={this.chartSpecStore}>
<Fragment>
Expand All @@ -47,6 +48,7 @@ export class Chart extends React.Component<ChartProps> {
{renderer === 'canvas' && <ReactChart />}
<Tooltips />
<Legend />
<LegendButton />
</div>
</Fragment>
</Provider>
Expand Down
80 changes: 30 additions & 50 deletions src/components/legend.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,15 @@
import {
EuiButtonIcon,
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
EuiText,
} from '@elastic/eui';
import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText } from '@elastic/eui';
import classNames from 'classnames';
import { inject, observer } from 'mobx-react';
import React from 'react';
import { isVertical } from '../lib/axes/axis_utils';
import { LegendItem } from '../lib/series/legend';
import { Position } from '../lib/series/specs';
import { ChartStore } from '../state/chart_state';

interface ReactiveChartProps {
chartStore?: ChartStore; // FIX until we find a better way on ts mobx
}

function getCollapseArrowType(
position: Position | undefined,
legendShown: boolean,
): 'arrowRight' | 'arrowLeft' | 'arrowDown' | 'arrowUp' {
switch (position) {
case Position.Left:
return legendShown ? 'arrowRight' : 'arrowLeft';
case Position.Bottom:
return legendShown ? 'arrowUp' : 'arrowDown';
case Position.Right:
return legendShown ? 'arrowLeft' : 'arrowRight';
case Position.Top:
return legendShown ? 'arrowDown' : 'arrowUp';
default:
return 'arrowRight';
}
}

class LegendComponent extends React.Component<ReactiveChartProps> {
static displayName = 'Legend';

Expand All @@ -49,45 +25,49 @@ class LegendComponent extends React.Component<ReactiveChartProps> {
showLegend,
legendCollapsed,
debug,
chartTheme,
} = this.props.chartStore!;

if (!showLegend.get() || !initialized.get() || legendItems.length === 0) {
if (
!showLegend.get() ||
!initialized.get() ||
legendItems.length === 0 ||
legendPosition === undefined
) {
return null;
}

const legendClasses = classNames(
'euiChartLegend',
`euiChartLegend--${legendPosition}`,
legendCollapsed.get() && 'euiChartLegend--collapsed',
debug && 'euiChartLegend--debug',
);

const legendCollapser = classNames(
'euiChartLegendCollapser',
`euiChartLegendCollapser--${legendPosition}`,
'elasticChartsLegend',
`elasticChartsLegend--${legendPosition}`,
legendCollapsed.get() && 'elasticChartsLegend--collapsed',
debug && 'elasticChartsLegend--debug',
);
const collapseArrowType = getCollapseArrowType(legendPosition, legendCollapsed.get());

let paddingStyle;
if (isVertical(legendPosition)) {
paddingStyle = {
paddingTop: chartTheme.chart.margins.top,
paddingBottom: chartTheme.chart.margins.bottom,
};
} else {
paddingStyle = {
paddingLeft: chartTheme.chart.margins.left,
paddingRight: chartTheme.chart.margins.right,
};
}
return (
<div className={legendClasses}>
<div className={legendCollapser}>
<EuiButtonIcon
onClick={this.onCollapseLegend}
iconType={collapseArrowType}
aria-label={legendCollapsed.get() ? 'Expand legend' : 'Collapse legend'}
/>
</div>
<div className="euiChartLegendList">
<div className={legendClasses} style={paddingStyle}>
<div className="elasticChartsLegendList">
<EuiFlexGroup
gutterSize="s"
wrap
className="euiChartLegendListContainer"
className="elasticChartsLegendListContainer"
responsive={false}
>
{legendItems.map((item, index) => {
const legendItemProps = {
key: index,
className: 'euiChartLegendList__item',
className: 'elasticChartsLegendList__item',
onMouseEnter: this.onLegendItemMouseover(index),
onMouseLeave: this.onLegendItemMouseout,
};
Expand Down Expand Up @@ -119,7 +99,7 @@ function LegendElement({ color, label }: Partial<LegendItem>) {
<EuiIcon type="dot" color={color} />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexItem grow={true} className="euiChartLegendListItem__title" title={label}>
<EuiFlexItem grow={true} className="elasticChartsLegendListItem__title" title={label}>
<EuiText size="xs" className="eui-textTruncate">
{label}
</EuiText>
Expand Down
39 changes: 39 additions & 0 deletions src/components/legend_button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { EuiButtonIcon } from '@elastic/eui';
import classNames from 'classnames';
import { inject, observer } from 'mobx-react';
import React from 'react';
import { ChartStore } from '../state/chart_state';

interface ReactiveChartProps {
chartStore?: ChartStore;
}

class LegendButtonComponent extends React.Component<ReactiveChartProps> {
static displayName = 'Legend';
onCollapseLegend = () => {
this.props.chartStore!.toggleLegendCollapsed();
}

render() {
const { initialized, legendItems, legendCollapsed, showLegend } = this.props.chartStore!;

if (!showLegend.get() || !initialized.get() || legendItems.length === 0) {
return null;
}
const isOpen = !legendCollapsed.get();
const classes = classNames(
'elasticChartsLegend__toggle',
isOpen && 'elasticChartsLegend__toggle--isOpen',
);
return (
<EuiButtonIcon
className={classes}
onClick={this.onCollapseLegend}
iconType="list"
aria-label={legendCollapsed.get() ? 'Expand legend' : 'Collapse legend'}
/>
);
}
}

export const LegendButton = inject('chartStore')(observer(LegendButtonComponent));
9 changes: 6 additions & 3 deletions src/components/tooltips.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class TooltipsComponent extends React.Component<ReactiveChartProps> {
const tooltipPos = tooltipPosition.get();
let hPosition;
if (!initialized.get() || !tooltip || !tooltipPos) {
return <div className="euiChartTooltip euiChartTooltip--hidden" />;
return <div className="elasticChartsTooltip elasticChartsTooltip--hidden" />;
}
if (tooltipPos.x <= parentDimensions.width / 2) {
hPosition = {
Expand Down Expand Up @@ -58,7 +58,10 @@ class TooltipsComponent extends React.Component<ReactiveChartProps> {
) => {
return (
<div
className={classNames('euiChartTooltip', showTooltip ? null : 'euiChartTooltip--hidden')}
className={classNames(
'elasticChartsTooltip',
showTooltip ? null : 'elasticChartsTooltip--hidden',
)}
style={{
position: 'absolute',
[vPosition.position]: vPosition.value,
Expand All @@ -71,7 +74,7 @@ class TooltipsComponent extends React.Component<ReactiveChartProps> {
{tooltip.map(([field, value], index) => {
return (
<tr key={`row-${index}`}>
<td className="euiChartTooltip__label">{field}</td>
<td className="elasticChartsTooltip__label">{field}</td>
<td>{value}</td>
</tr>
);
Expand Down
Loading

0 comments on commit 830e564

Please sign in to comment.