Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend clip path configs - new PR #3602

Merged
merged 15 commits into from
May 31, 2023
4 changes: 3 additions & 1 deletion demo/component/AreaChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@ export default class AreaChartDemo extends React.Component<any, any> {
render() {
return (
<div className="area-charts">
<button type="button" onClick={this.handleChangeData}>change data</button>
<button type="button" onClick={this.handleChangeData}>
change data
</button>
<br />

<p>Stacked AreaChart</p>
Expand Down
11 changes: 10 additions & 1 deletion demo/component/LineChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,14 @@ const data03 = [
{ date: 'Dec 30 2016', price: 115.82 },
];

const data04 = [
{ name: 'Page A', uv: 1000, pv: 400, amt: 2400 },
{ name: 'Page B', uv: 3000, pv: 1398, amt: 2210 },
{ name: 'Page C', uv: 2000, pv: 0, amt: 2290 },
{ name: 'Page D', uv: 2780, pv: 0, amt: 2000 },
{ name: 'Page E', uv: 1890, pv: 3000, amt: 2181 },
];

const series = [
{
name: 'Series 1',
Expand Down Expand Up @@ -343,6 +351,7 @@ const initialState = {
data,
data01,
data02,
data04,
opacity: 1,
anotherState: false,
};
Expand Down Expand Up @@ -416,7 +425,7 @@ export default class Demo extends Component<any, any> {
};

render() {
const { data, data01, data02, opacity } = this.state;
const { data, data01, data02, data04, opacity } = this.state;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering, do we want to continue maintaining the demo? Should we consider moving things into storybook instead? 🤔
Unrelated to this PR, curious to hear your opinion.

Copy link
Member Author

@ckifer ckifer May 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah we shouldn't keep maintaining the demo. This was just completely mimicking the linked PR which was pre-storybook.

I think it's fine to merge changes to the demo since it doesn't hurt anything, but overall yeah we have no need for it anymore.

Maybe we should look over it and see if there are any useful things to add to examples


return (
<div className="line-charts">
Expand Down
30 changes: 24 additions & 6 deletions src/cartesian/Area.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ interface State {
totalLength?: number;
}

const isDotProps = (dot: AreaDot): dot is DotProps =>
typeof dot === 'object' && 'cx' in dot && 'cy' in dot && 'r' in dot;

export class Area extends PureComponent<Props, State> {
static displayName = 'Area';

Expand Down Expand Up @@ -309,7 +312,7 @@ export class Area extends PureComponent<Props, State> {
}
};

renderDots(needClip: boolean, clipPathId: string) {
renderDots(needClip: boolean, clipDot: boolean, clipPathId: string) {
const { isAnimationActive } = this.props;
const { isAnimationFinished } = this.state;

Expand Down Expand Up @@ -338,7 +341,7 @@ export class Area extends PureComponent<Props, State> {
return Area.renderDotItem(dot, dotProps);
});
const dotsProps = {
clipPath: needClip ? `url(#clipPath-${clipPathId})` : null,
clipPath: needClip ? `url(#clipPath-${clipDot ? '' : 'dots-'}${clipPathId})` : null,
};
return (
<Layer className="recharts-area-dots" {...dotsProps}>
Expand Down Expand Up @@ -553,20 +556,35 @@ export class Area extends PureComponent<Props, State> {
const { isAnimationFinished } = this.state;
const hasSinglePoint = points.length === 1;
const layerClass = classNames('recharts-area', className);
const needClip = (xAxis && xAxis.allowDataOverflow) || (yAxis && yAxis.allowDataOverflow);
const needClipX = xAxis && xAxis.allowDataOverflow;
const needClipY = yAxis && yAxis.allowDataOverflow;
const needClip = needClipX || needClipY;
const clipPathId = _.isNil(id) ? this.id : id;
const { r, strokeWidth } = filterProps(dot) || { r: 3, strokeWidth: 2 };
const { clipDot = true } = isDotProps(dot) ? dot : {};
const dotSize = r * 2 + strokeWidth;

return (
<Layer className={layerClass}>
{needClip ? (
{needClipX || needClipY ? (
<defs>
<clipPath id={`clipPath-${clipPathId}`}>
<rect x={left} y={top} width={width} height={Math.floor(height)} />
<rect
x={needClipX ? left : left - width / 2}
y={needClipY ? top : top - height / 2}
width={needClipX ? width : width * 2}
height={needClipY ? height : height * 2}
/>
</clipPath>
{!clipDot && (
<clipPath id={`clipPath-dots-${clipPathId}`}>
<rect x={left - dotSize / 2} y={top - dotSize / 2} width={width + dotSize} height={height + dotSize} />
</clipPath>
)}
</defs>
) : null}
{!hasSinglePoint ? this.renderArea(needClip, clipPathId) : null}
{(dot || hasSinglePoint) && this.renderDots(needClip, clipPathId)}
{(dot || hasSinglePoint) && this.renderDots(needClip, clipDot, clipPathId)}
{(!isAnimationActive || isAnimationFinished) && LabelList.renderCallByParent(this.props, points)}
</Layer>
);
Expand Down
15 changes: 12 additions & 3 deletions src/cartesian/Bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ interface InternalBarProps {
data?: BarRectangleItem[];
top?: number;
left?: number;
width?: number;
height?: number;
}

type RectangleShapeType =
Expand Down Expand Up @@ -467,15 +469,22 @@ export class Bar extends PureComponent<Props, State> {

const { isAnimationFinished } = this.state;
const layerClass = classNames('recharts-bar', className);
const needClip = (xAxis && xAxis.allowDataOverflow) || (yAxis && yAxis.allowDataOverflow);
const needClipX = xAxis && xAxis.allowDataOverflow;
const needClipY = yAxis && yAxis.allowDataOverflow;
const needClip = needClipX || needClipY;
const clipPathId = _.isNil(id) ? this.id : id;

return (
<Layer className={layerClass}>
{needClip ? (
{needClipX || needClipY ? (
<defs>
<clipPath id={`clipPath-${clipPathId}`}>
<rect x={left} y={top} width={width} height={height} />
<rect
x={needClipX ? left : left - width / 2}
y={needClipY ? top : top - height / 2}
width={needClipX ? width : width * 2}
height={needClipY ? height : height * 2}
/>
</clipPath>
</defs>
) : null}
Expand Down
27 changes: 21 additions & 6 deletions src/cartesian/Line.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ export class Line extends PureComponent<Props, State> {
return dotItem;
}

renderDots(needClip: boolean, clipPathId: string) {
renderDots(needClip: boolean, clipDot: boolean, clipPathId: string) {
const { isAnimationActive } = this.props;

if (isAnimationActive && !this.state.isAnimationFinished) {
Expand All @@ -334,7 +334,7 @@ export class Line extends PureComponent<Props, State> {
return Line.renderDotItem(dot, dotProps);
});
const dotsProps = {
clipPath: needClip ? `url(#clipPath-${clipPathId})` : null,
clipPath: needClip ? `url(#clipPath-${clipDot ? '' : 'dots-'}${clipPathId})` : null,
};

return (
Expand Down Expand Up @@ -461,21 +461,36 @@ export class Line extends PureComponent<Props, State> {
const { isAnimationFinished } = this.state;
const hasSinglePoint = points.length === 1;
const layerClass = classNames('recharts-line', className);
const needClip = (xAxis && xAxis.allowDataOverflow) || (yAxis && yAxis.allowDataOverflow);
const needClipX = xAxis && xAxis.allowDataOverflow;
const needClipY = yAxis && yAxis.allowDataOverflow;
const needClip = needClipX || needClipY;
const clipPathId = _.isNil(id) ? this.id : id;
const { r, strokeWidth } = filterProps(dot) || { r: 3, strokeWidth: 2 };
const { clipDot = true } = dot as DotProps;
const dotSize = r * 2 + strokeWidth;

return (
<Layer className={layerClass}>
{needClip ? (
{needClipX || needClipY ? (
<defs>
<clipPath id={`clipPath-${clipPathId}`}>
<rect x={left} y={top} width={width} height={height} />
<rect
x={needClipX ? left : left - width / 2}
y={needClipY ? top : top - height / 2}
width={needClipX ? width : width * 2}
height={needClipY ? height : height * 2}
/>
</clipPath>
{!clipDot && (
<clipPath id={`clipPath-dots-${clipPathId}`}>
<rect x={left - dotSize / 2} y={top - dotSize / 2} width={width + dotSize} height={height + dotSize} />
</clipPath>
)}
</defs>
) : null}
{!hasSinglePoint && this.renderCurve(needClip, clipPathId)}
{this.renderErrorBar(needClip, clipPathId)}
{(hasSinglePoint || dot) && this.renderDots(needClip, clipPathId)}
{(hasSinglePoint || dot) && this.renderDots(needClip, clipDot, clipPathId)}
{(!isAnimationActive || isAnimationFinished) && LabelList.renderCallByParent(this.props, points)}
</Layer>
);
Expand Down
15 changes: 12 additions & 3 deletions src/cartesian/Scatter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ interface ScatterProps {

left?: number;
top?: number;
width?: number;
height?: number;

xAxis?: Omit<XAxisProps, 'scale'> & { scale: D3Scale<string | number> };
yAxis?: Omit<YAxisProps, 'scale'> & { scale: D3Scale<string | number> };
Expand Down Expand Up @@ -435,15 +437,22 @@ export class Scatter extends PureComponent<Props, State> {
}
const { isAnimationFinished } = this.state;
const layerClass = classNames('recharts-scatter', className);
const needClip = (xAxis && xAxis.allowDataOverflow) || (yAxis && yAxis.allowDataOverflow);
const needClipX = xAxis && xAxis.allowDataOverflow;
const needClipY = yAxis && yAxis.allowDataOverflow;
const needClip = needClipX || needClipY;
const clipPathId = _.isNil(id) ? this.id : id;

return (
<Layer className={layerClass} clipPath={needClip ? `url(#clipPath-${clipPathId})` : null}>
{needClip ? (
{needClipX || needClipY ? (
<defs>
<clipPath id={`clipPath-${clipPathId}`}>
<rect x={left} y={top} width={width} height={height} />
<rect
x={needClipX ? left : left - width / 2}
y={needClipY ? top : top - height / 2}
width={needClipX ? width : width * 2}
height={needClipY ? height : height * 2}
/>
</clipPath>
</defs>
) : null}
Expand Down
1 change: 1 addition & 0 deletions src/shape/Dot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface DotProps {
cx?: number;
cy?: number;
r?: number;
clipDot?: boolean;
}

export type Props = PresentationAttributesWithProps<DotProps, SVGCircleElement> & DotProps;
Expand Down
41 changes: 41 additions & 0 deletions storybook/stories/Examples/LineChart.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// eslint-disable-next-line max-classes-per-file
import React, { PureComponent, useState } from 'react';
import { StoryObj } from '@storybook/react';
import { Impressions, impressionsData, pageData } from '../data';
import {
Line,
Expand Down Expand Up @@ -325,6 +326,46 @@ export const WithCustomizedDot = {
},
};

export const ClipDot: StoryObj = {
render: (args: Record<string, any>) => {
return (
<ResponsiveContainer width="100%" height={300}>
<LineChart
margin={{
top: 20,
right: 20,
bottom: 20,
left: 20,
}}
data={pageData}
>
<Line
isAnimationActive={false}
dataKey="uv"
{...args}
dot={{ clipDot: args.clipDot, r: 4, strokeWidth: 2, fill: '#ffffff', fillOpacity: 1 }}
/>
<Tooltip />
<XAxis dataKey="name" allowDataOverflow />
<YAxis />
</LineChart>
</ResponsiveContainer>
);
},
args: {
clipDot: false,
},
parameters: {
controls: { include: ['clipDot'] },
docs: {
description: {
clipDot: `When \`allowDataOverflow\` is true on \`XAxis\` or \`YAxis\`, set
\`clipDot\` within the dot object to determine if recharts should clip the dots at the end of the page.`,
},
},
},
};

export const WithCustomizedLabel = {
render: () => {
class CustomizedLabel extends PureComponent {
Expand Down