Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
mbondyra committed May 13, 2024
1 parent d6f73ea commit ea44e88
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 10 deletions.
34 changes: 34 additions & 0 deletions packages/charts/src/components/legend/components/legend_action.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import React from 'react';

import { SeriesIdentifier } from '../../../common/series_id';
import { LegendAction } from '../../../specs/settings';

/** @internal */
export const LegendActionComponent = ({
Action,
series,
color,
label,
}: {
Action?: LegendAction;
series: SeriesIdentifier[];
color: string;
label: string;
}) => {
if (!Action) {
return null;
}
return (
<div className="echLegendItem__action">
<Action series={series} color={color} label={label} />
</div>
);
};
189 changes: 189 additions & 0 deletions packages/charts/src/components/legend/components/legend_item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import classNames from 'classnames';
import React, { Component, CSSProperties } from 'react';

import { LegendActionComponent } from './legend_action';
import { LegendTableCell } from './legend_table_cell';
import { LegendTableRow } from './legend_table_row';
import { LegendValueComponent } from './legend_value';
import { nonNullable } from '../../../chart_types/xy_chart/state/utils/get_legend_values';
import { LegendItem, LegendItemExtraValues, LegendValue } from '../../../common/legend';
import { SeriesIdentifier } from '../../../common/series_id';
import {
LegendItemListener,
BasicListener,
LegendAction,
LegendPositionConfig,
LegendColorPicker,
} from '../../../specs/settings';
import {
clearTemporaryColors as clearTemporaryColorsAction,
setTemporaryColor as setTemporaryColorAction,
setPersistedColor as setPersistedColorAction,
} from '../../../state/actions/colors';
import {
onLegendItemOutAction,
onLegendItemOverAction,
onToggleDeselectSeriesAction,
} from '../../../state/actions/legend';
import { LayoutDirection } from '../../../utils/common';
import { deepEqual } from '../../../utils/fast_deep_equal';
import { LegendLabelOptions } from '../../../utils/themes/theme';
import { Label as ItemLabel } from '../label';
import { LegendColorPicker as LegendColorPickerComponent } from '../legend_color_picker';
import { getExtra } from '../utils';

/** @internal */
export const LEGEND_HIERARCHY_MARGIN = 10;

/** @internal */
export interface SharedLegendItemProps {
flatLegend: boolean;
totalItems: number;
positionConfig: LegendPositionConfig;
extraValues: Map<string, LegendItemExtraValues>;
legendValues: Array<LegendValue>;
isMostlyRTL: boolean;
labelOptions: LegendLabelOptions;
colorPicker?: LegendColorPicker;
action?: LegendAction;
onClick?: LegendItemListener;
onMouseOut?: BasicListener;
onMouseOver?: LegendItemListener;
mouseOutAction: typeof onLegendItemOutAction;
mouseOverAction: typeof onLegendItemOverAction;
clearTemporaryColorsAction: typeof clearTemporaryColorsAction;
setTemporaryColorAction: typeof setTemporaryColorAction;
setPersistedColorAction: typeof setPersistedColorAction;
toggleDeselectSeriesAction: typeof onToggleDeselectSeriesAction;
}

/** @internal */
export interface LegendItemProps extends SharedLegendItemProps {
item: LegendItem;
}

/** @internal */
export class LegendListItem extends Component<LegendItemProps> {
static displayName = 'LegendItem';

shouldComponentUpdate(nextProps: LegendItemProps) {
return !deepEqual(this.props, nextProps);
}

onLegendItemMouseOver = () => {
const { onMouseOver, mouseOverAction, item } = this.props;
// call the settings listener directly if available
if (onMouseOver) {
onMouseOver(item.seriesIdentifiers);
}
mouseOverAction(item.path);
};

onLegendItemMouseOut = () => {
const { onMouseOut, mouseOutAction } = this.props;
// call the settings listener directly if available
if (onMouseOut) {
onMouseOut();
}
mouseOutAction();
};

/**
* Returns click function only if toggleable or click listener is provided
*/
onLabelToggle = (legendItemId: SeriesIdentifier[]): ((negate: boolean) => void) | undefined => {
const { item, onClick, toggleDeselectSeriesAction, totalItems } = this.props;
if (totalItems <= 1 || (!item.isToggleable && !onClick)) {
return;
}

return (negate) => {
if (onClick) {
onClick(legendItemId);
}

if (item.isToggleable) {
toggleDeselectSeriesAction(legendItemId, negate);
}
};
};

render() {
const {
extraValues,
item,
totalItems,
action: Action,
positionConfig,
labelOptions,
isMostlyRTL,
flatLegend,
} = this.props;
const { color, isSeriesHidden, isItemHidden, seriesIdentifiers, label } = item;

if (isItemHidden) return null;

const itemClassNames = classNames('echLegendItem', 'echLegendItem--highlightable', {
'echLegendItem--hidden': isSeriesHidden,
'echLegendItem--vertical': positionConfig.direction === LayoutDirection.Vertical,
});

const legendValueItems = item.values
.map((v) => {
if (v.type === LegendValue.CurrentAndLastValue || (v && !v.type)) {
return getExtra(extraValues, item, totalItems);
}
return v;
})
.filter(nonNullable);

const style: CSSProperties = flatLegend
? {}
: {
[isMostlyRTL ? 'marginRight' : 'marginLeft']: LEGEND_HIERARCHY_MARGIN * (item.depth ?? 0),
};

return (
<LegendTableRow
className={itemClassNames}
onMouseEnter={this.onLegendItemMouseOver}
onMouseLeave={this.onLegendItemMouseOut}
style={style}
dir={isMostlyRTL ? 'rtl' : 'ltr'}
data-ech-series-name={label}
>
<LegendTableCell className="colorWrapper">
<LegendColorPickerComponent {...this.props} />
</LegendTableCell>
<LegendTableCell>
<ItemLabel
label={label}
options={labelOptions}
isToggleable={totalItems > 1 && item.isToggleable}
onToggle={this.onLabelToggle(seriesIdentifiers)}
isSeriesHidden={isSeriesHidden}
/>
</LegendTableCell>

{legendValueItems?.map((l, i) => {
return (
<LegendTableCell key={l?.type || i}>
<LegendValueComponent {...l} />
</LegendTableCell>
);
})}
<LegendTableCell>
<LegendActionComponent Action={Action} series={seriesIdentifiers} color={color} label={label} />
</LegendTableCell>
</LegendTableRow>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

import React from 'react';

import { SharedLegendItemProps, LegendListItem } from './legend_item';
import { LegendItem } from '../../../common/legend';
import { SharedLegendItemProps, LegendListItem } from '../legend_item';

/** @internal */
export const LegendTableBody: React.FC<SharedLegendItemProps & { items: LegendItem[] }> = ({ items, ...itemProps }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,20 @@
import classNames from 'classnames';
import React, { PropsWithChildren } from 'react';

import { LegendCellStyle } from './types';

/** @public */
export type LegendTableCellProps = PropsWithChildren<{
truncate?: boolean;
className?: string;
title?: string;
style?: LegendCellStyle;
}>;

/** @public */
export const LegendTableCell = ({ style, truncate = false, className, children }: LegendTableCellProps) => {
export const LegendTableCell = ({ truncate = false, className, children }: LegendTableCellProps) => {
const classes = classNames('echLegendTable__cell', className, {
'echLegendTable__cell--truncate': truncate,
});

return (
<div role="gridcell" className={classes} style={style}>
<div role="gridcell" className={classes}>
{children}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ export const LegendTableRow = ({ id, children, className, ...rest }: LegendTable
const classes = classNames('echLegendTable__row', className);

return (
// cannot focus row using display: contents to structure grid
// eslint-disable-next-line jsx-a11y/interactive-supports-focus
<div role="row" id={id} className={classes} {...rest}>
{children}
</div>
Expand Down
20 changes: 20 additions & 0 deletions packages/charts/src/components/legend/components/legend_value.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import React from 'react';

import { LegendItemValue } from '../../../common/legend';

/** @internal */
export const LegendValueComponent = ({ label }: LegendItemValue) => {
return (
<div className="echLegendItem__legendValue" title={`${label}`}>
{label}
</div>
);
};
1 change: 0 additions & 1 deletion packages/charts/src/components/legend/legend_item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,6 @@ export class LegendListItem extends Component<LegendItemProps> {
</LegendTableCell>

{legendValueItems?.map((l, i) => {
console.log(l, 'legendValueItems');
return (
<LegendTableCell key={l?.type || i}>
<LegendValueComponent {...l} />
Expand Down

0 comments on commit ea44e88

Please sign in to comment.