diff --git a/src/components/charts/BasicCurveChart/index.tsx b/src/components/charts/BasicCurveChart/index.tsx index 6f9ea9cd..33c8b85f 100644 --- a/src/components/charts/BasicCurveChart/index.tsx +++ b/src/components/charts/BasicCurveChart/index.tsx @@ -1,40 +1,93 @@ -import React, { VFC } from 'react'; +import React, { useMemo, VFC } from 'react'; +import { lighten } from 'color2k'; + +import { makeBezierCurve, PathPoint } from '~/utils/dom/makeBezierCurve'; import { SVGProps } from '~/utils/types'; interface BasicCurveChartProps extends SVGProps { items: number[]; + gap?: number; + fullscreen?: boolean; } +const gap = 5; const BasicCurveChart: VFC = ({ stroke = '#007962', items = [], + fullscreen = true, ...props }) => { - const max = Math.max(...items) + 5; + const max = Math.max(...items); const height = props.height ? parseFloat(props.height.toString()) : 100; const width = props.width ? parseFloat(props.width.toString()) : 100; + const borderGap = fullscreen ? 0 : gap; - const d = items.reduce( - (acc, val, index) => [ - ...acc, - index === 0 - ? `M 5 ${height - (val / max) * height}` - : `L ${(width / (items.length - 1)) * index} ${height - (val / max) * height}`, - ], - [] + const points = useMemo( + () => + items.reduce( + (acc, val, index) => [ + ...acc, + index === 0 + ? { x: borderGap, y: height - (val / max) * (height - gap * 2) - gap } + : { + x: ((width - borderGap) / (items.length - 1)) * index, + y: height - (val / max) * (height - gap * 2) - gap, + }, + ], + [] + ), + [height, width, items, gap] ); + if (!points.length) { + return null; + } + return ( - + - + - - + + + + + ); }; diff --git a/src/layouts/BorisLayout/index.tsx b/src/layouts/BorisLayout/index.tsx index 4c1abddb..3d07ec47 100644 --- a/src/layouts/BorisLayout/index.tsx +++ b/src/layouts/BorisLayout/index.tsx @@ -6,6 +6,8 @@ import { BorisSidebar } from '~/components/boris/BorisSidebar'; import { Superpower } from '~/components/boris/Superpower'; import { BasicCurveChart } from '~/components/charts/BasicCurveChart'; import { Card } from '~/components/containers/Card'; +import { Filler } from '~/components/containers/Filler'; +import { Grid } from '~/components/containers/Grid'; import { Group } from '~/components/containers/Group'; import { Padder } from '~/components/containers/Padder'; import { Sticky } from '~/components/containers/Sticky'; @@ -66,10 +68,19 @@ const BorisLayout: FC = ({ title, setIsBetaTester, isTester, stats, isLo

Количество нод за год

- + + + + + + + + + + +

Количество комментов за год

- diff --git a/src/utils/dom/makeBezierCurve.ts b/src/utils/dom/makeBezierCurve.ts new file mode 100644 index 00000000..43581246 --- /dev/null +++ b/src/utils/dom/makeBezierCurve.ts @@ -0,0 +1,60 @@ +// size of the tangent +const t = 1 / 5; + +export interface PathPoint { + x: number; + y: number; +} + +const controlPoints = (p: PathPoint[]) => { + const pc: PathPoint[][] = []; + + for (let i = 1; i < p.length - 1; i++) { + const dx = p[i - 1].x - p[i + 1].x; // difference x + const dy = p[i - 1].y - p[i + 1].y; // difference y + + // the first control point + const x1 = p[i].x - dx * t; + const y1 = p[i].y - dy * t; + const o1 = { + x: x1, + y: y1, + }; + + // the second control point + const x2 = p[i].x + dx * t; + const y2 = p[i].y + dy * t; + const o2 = { + x: x2, + y: y2, + }; + + // building the control points array + pc[i] = []; + pc[i].push(o1); + pc[i].push(o2); + } + + return pc; +}; + +export const makeBezierCurve = (p: PathPoint[]) => { + const pc = controlPoints(p); // the control points array + + let d = `M${p[0].x},${p[0].y} Q${pc[1][1].x},${pc[1][1].y}, ${p[1].x},${p[1].y}`; + + if (p.length > 2) { + // central curves are cubic Bezier + for (let i = 1; i < p.length - 2; i++) { + d += `C${pc[i][0].x}, ${pc[i][0].y}, ${pc[i + 1][1].x}, ${pc[i + 1][1].y}, ${p[i + 1].x},${ + p[i + 1].y + }`; + } + + // the first & the last curve are quadratic Bezier + const n = p.length - 1; + d += `Q${pc[n - 1][0].x}, ${pc[n - 1][0].y}, ${p[n].x}, ${p[n].y}`; + } + + return d; +};