Ghost

struxadotcloud/struxa

public
Fork0
CodeIssues0Pull requests0
1branch
struxapackagesuisrccomponentsdither-kitarea.tsx
feat: replace server info sparklines with dither-kit charts5845cd43 months ago102 lines · 3.2 KB
1"use client"
2
3import { type ReactNode, useEffect } from "react"
4import {
5 type AreaVariant,
6 type SeriesKind,
7 type StrokeVariant,
8 useChartPart,
9} from "./chart-context"
10import { SeriesContext }
11
12export type SeriesProps
13 dataKey: string
14 variant?: AreaVariant
15 strokeVariant?: StrokeVariant
16 isClickable?: boolean
17 children?: ReactNode
18}
19
20/**
21 * Shared implementation for the continuous series (`<Area>`, `<Line>`). The
22 * dithered fill/line is painted on the canvas; this registers the series so the
23 * canvas knows how to draw it, wires click-to-select via a transparent band
24 * polygon, and exposes the series to child `<Dot>`/`<ActiveDot>` markers.
25 */
26function CartesianSeries({
27 part,
28 kind,
29 dataKey,
30 variant = "gradient"
31 strokeVariant = "solid"
32 isClickable = false,
33 children,
34}: SeriesProps
35 const ctx =
36 const { registerSeries,
37
38 if (process.env
39 console.warn(
40 `<${part} dataKey="${
41 )
42 }
43
44 useEffect(() =>
45 registerSeries({ dataKey,
46 return () =>
47 }, [dataKey,
48
49 const band =
50 if (!ctx.ready
51
52 const seed =
53 const emphasis =
54 const dimmed =
55 const onClick =
56 ? () => ctx
57 : undefined
58
59 // Transparent hit polygon tracing the series' own band, so clicking a series
60 // selects *that* series. The Legend offers the same toggle accessibly.
61 // One pass out along the top edge, one pass back along the floor.
62 let hitPath:
63 if (isClickable) {
64 const parts:
65 band.forEach((b
66 parts.push(`${
67 })
68 for (let i
69 parts.push(`L
70 }
71 hitPath = `${parts
72 }
73
74 return (
75 <>
76 {hitPath && (
77 // biome-ignore lint/a11y/noStaticElementInteractions: progressive enhancement; the Legend offers the same toggle accessibly
78 <path
79 d={hitPath}
80 fill="transparent"
81 style={{ cursor:
82 onClick={onClick}
83 />
84 )}
85 <SeriesContext value={
86 {children}
87 </SeriesContext>
88 </>
89 )
90}
91
92export type AreaProps
93
94/** One area series — dithered fill from the value line down to its floor. */
95export function Area(props
96 return <CartesianSeries part
97}
98
99/** One line series — bright line with a thin dither glow hugging it. */
100export function Line(props
101 return <CartesianSeries part
102}
from
"
./series-context
"
=
{
,
,
&
{
part
:
string
;
kind
:
SeriesKind
})
{
useChartPart
(
part
,
kind
===
"
line
"
?
"
line
"
:
"
area
"
)
unregisterSeries
}
=
ctx
.
NODE_ENV
!==
"
production
"
&&
!
ctx
.
config
[
dataKey
])
{
dataKey
}
" />: "
${
dataKey
}
" is not in the chart
\`
config
\`
. Add it so the series has a colour and label.
`
{
kind
,
variant
,
strokeVariant
}
)
unregisterSeries
(
dataKey
)
kind
,
variant
,
strokeVariant
,
registerSeries
,
unregisterSeries
])
ctx
.
bands
[
dataKey
]
||
!
band
)
return
null
ctx
.
seedOf
(
dataKey
)
ctx
.
selectedDataKey
??
ctx
.
focusDataKey
emphasis
!==
null
&&
emphasis
!==
dataKey
isClickable
.
selectDataKey
(
ctx
.
selectedDataKey
===
dataKey
?
null
:
dataKey
)
string
|
null
=
null
string
[]
=
[]
,
i
)
=>
{
i
===
0
?
"
M
"
:
"
L
"
}${
ctx
.
xCenter
(
i
)
}
,
${
ctx
.
y
(
b
[
1
])
}
`
)
=
band
.
length
-
1
;
i
>=
0
;
i
-=
1
)
{
${
ctx
.
xCenter
(
i
)
}
,
${
ctx
.
y
(
band
[
i
]
!
[
0
])
}
`
)
.
join
(
"
"
)
}
Z
`
"
pointer
"
}
}
{
dataKey
,
seed
,
dimmed
}
}
>
=
SeriesProps
:
AreaProps
)
{
=
"
Area
"
kind
=
"
area
"
{
...
props
}
/>
:
AreaProps
)
{
=
"
Line
"
kind
=
"
line
"
{
...
props
}
/>