@exegia/corpora-ui 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist-lib/components/composed/chat/chart/chart-atom.d.ts +30 -0
  2. package/dist-lib/components/composed/chat/chart/chart-context.d.ts +2 -0
  3. package/dist-lib/components/composed/chat/chart/chart-legend.d.ts +4 -0
  4. package/dist-lib/components/composed/chat/chart/chart-plot.d.ts +3 -0
  5. package/dist-lib/components/composed/chat/chart/chart-root.d.ts +3 -0
  6. package/dist-lib/components/composed/chat/chart/chart-tooltip.d.ts +4 -0
  7. package/dist-lib/components/composed/chat/chart/constants.d.ts +3 -0
  8. package/dist-lib/components/composed/chat/chart/evilcharts/echarts-area-chart.d.ts +93 -0
  9. package/dist-lib/components/composed/chat/chart/evilcharts/echarts-bar-chart.d.ts +86 -0
  10. package/dist-lib/components/composed/chat/chart/evilcharts/echarts-brush.d.ts +65 -0
  11. package/dist-lib/components/composed/chat/chart/evilcharts/echarts-chart.d.ts +44 -0
  12. package/dist-lib/components/composed/chat/chart/evilcharts/echarts-dot.d.ts +16 -0
  13. package/dist-lib/components/composed/chat/chart/evilcharts/echarts-legend.d.ts +24 -0
  14. package/dist-lib/components/composed/chat/chart/evilcharts/echarts-line-chart.d.ts +90 -0
  15. package/dist-lib/components/composed/chat/chart/evilcharts/echarts-pie-chart.d.ts +73 -0
  16. package/dist-lib/components/composed/chat/chart/evilcharts/echarts-tooltip.d.ts +29 -0
  17. package/dist-lib/components/composed/chat/chart/index.d.ts +6 -0
  18. package/dist-lib/components/composed/chat/chart/type.d.ts +80 -0
  19. package/dist-lib/components/composed/chat/chart/use-chart-state.d.ts +3 -0
  20. package/dist-lib/components/composed/chat/chart/use-chart.d.ts +6 -0
  21. package/dist-lib/components/composed/chat/chart/utils.d.ts +5 -0
  22. package/dist-lib/components/composed/chat/chart.d.ts +1 -44
  23. package/dist-lib/components/composed/chat/index.d.ts +1 -1
  24. package/dist-lib/components/stories/chart.story.d.ts +39 -2
  25. package/dist-lib/components/stories/insight-cards.story.d.ts +30 -1
  26. package/dist-lib/index.js +10020 -5549
  27. package/dist-lib/index.js.map +1 -1
  28. package/package.json +2 -1
  29. package/src/components/composed/chat/__tests__/chart.test.tsx +255 -1
  30. package/src/components/composed/chat/chart/chart-atom.ts +55 -0
  31. package/src/components/composed/chat/chart/chart-context.ts +9 -0
  32. package/src/components/composed/chat/chart/chart-legend.tsx +75 -0
  33. package/src/components/composed/chat/chart/chart-plot.tsx +233 -0
  34. package/src/components/composed/chat/chart/chart-root.tsx +229 -0
  35. package/src/components/composed/chat/chart/chart-tooltip.tsx +74 -0
  36. package/src/components/composed/chat/chart/constants.ts +8 -0
  37. package/src/components/composed/chat/chart/evilcharts/LICENSE +21 -0
  38. package/src/components/composed/chat/chart/evilcharts/README.md +8 -0
  39. package/src/components/composed/chat/chart/evilcharts/echarts-area-chart.tsx +2358 -0
  40. package/src/components/composed/chat/chart/evilcharts/echarts-bar-chart.tsx +2261 -0
  41. package/src/components/composed/chat/chart/evilcharts/echarts-brush.tsx +233 -0
  42. package/src/components/composed/chat/chart/evilcharts/echarts-chart.tsx +214 -0
  43. package/src/components/composed/chat/chart/evilcharts/echarts-dot.tsx +111 -0
  44. package/src/components/composed/chat/chart/evilcharts/echarts-legend.tsx +131 -0
  45. package/src/components/composed/chat/chart/evilcharts/echarts-line-chart.tsx +2112 -0
  46. package/src/components/composed/chat/chart/evilcharts/echarts-pie-chart.tsx +1235 -0
  47. package/src/components/composed/chat/chart/evilcharts/echarts-tooltip.tsx +125 -0
  48. package/src/components/composed/chat/chart/index.ts +13 -0
  49. package/src/components/composed/chat/chart/type.ts +88 -0
  50. package/src/components/composed/chat/chart/use-chart-state.ts +22 -0
  51. package/src/components/composed/chat/chart/use-chart.ts +70 -0
  52. package/src/components/composed/chat/chart/utils.ts +47 -0
  53. package/src/components/composed/chat/chart.tsx +2 -400
  54. package/src/components/composed/chat/index.ts +1 -7
  55. package/src/components/stories/chart.story.tsx +16 -1
@@ -1,401 +1,3 @@
1
1
  "use client"
2
-
3
- import { useReducedMotion } from "motion/react"
4
- import type * as React from "react"
5
- import {
6
- Area,
7
- AreaChart,
8
- Bar,
9
- BarChart,
10
- CartesianGrid,
11
- Cell,
12
- Line,
13
- LineChart,
14
- Pie,
15
- PieChart,
16
- ResponsiveContainer,
17
- Tooltip,
18
- XAxis,
19
- } from "recharts"
20
- import { cn } from "@/lib/utils"
21
- import {
22
- Card,
23
- CardFrame,
24
- CardFrameHeader,
25
- CardPanel,
26
- } from "@/components/ui/card"
27
- import { Dot, LegendItem, Pill, type TDotTone } from "@/components/ui/chat"
28
- import { Reference } from "@/components/atoms/reference"
29
-
30
- export type TChartType = "pie" | "area" | "line" | "bar"
31
-
32
- export interface IChartSeries {
33
- /** Key into each data row. */
34
- key: string
35
- label: React.ReactNode
36
- /** Defaults to `var(--chart-series-N)` by position. */
37
- color?: string
38
- /** Renders a value in the legend and tooltip (`v => \`${v}%\``). */
39
- format?: (value: number) => React.ReactNode
40
- }
41
-
42
- export type TChartDatum = { label: string; [key: string]: string | number }
43
-
44
- export interface IChartProps extends Omit<
45
- React.ComponentPropsWithoutRef<"div">,
46
- "title"
47
- > {
48
- type: TChartType
49
- title?: React.ReactNode
50
- subtitle?: React.ReactNode
51
- /** Pill text; defaults to the capitalised type. */
52
- badge?: React.ReactNode
53
- /** The passage or Strong's entry behind the data. Replaces the type badge. */
54
- reference?: React.ComponentProps<typeof Reference>
55
- data: TChartDatum[]
56
- /** Series to plot; pie uses the first one. */
57
- series: IChartSeries[]
58
- /** Pie only: big number and caption in the donut's centre. */
59
- center?: { value: React.ReactNode; label?: React.ReactNode }
60
- /** Drop the title row (InsightCards embeds a bare plot). */
61
- headerless?: boolean
62
- /** Plot height in px; the card is 244 tall with the header. */
63
- plotHeight?: number
64
- }
65
-
66
- const TYPE_LABEL: Record<TChartType, string> = {
67
- pie: "Pie",
68
- area: "Area",
69
- line: "Line",
70
- bar: "Bar",
71
- }
72
-
73
- const seriesColor = (s: IChartSeries, i: number) =>
74
- s.color ?? `var(--chart-series-${(i % 5) + 1})`
75
- const seriesTone = (i: number) => `series-${(i % 5) + 1}` as TDotTone
76
-
77
- const fmt = (s: IChartSeries | undefined, v: unknown): React.ReactNode =>
78
- typeof v === "number" && s?.format ? s.format(v) : String(v)
79
-
80
- interface ITooltipRow {
81
- name?: unknown
82
- value?: unknown
83
- color?: string
84
- payload?: TChartDatum
85
- }
86
-
87
- /** Hover card: category label, then one dot · series · value row per series. */
88
- function ChartTooltip({
89
- active,
90
- payload,
91
- label,
92
- series,
93
- }: {
94
- active?: boolean
95
- payload?: ITooltipRow[]
96
- label?: unknown
97
- series: IChartSeries[]
98
- }): React.ReactElement | null {
99
- if (!active || !payload?.length) return null
100
- // Pie rows already carry the category as their name, so no heading there.
101
- const heading =
102
- series.length > 1 || label !== undefined
103
- ? (label ?? payload[0].payload?.label)
104
- : undefined
105
- return (
106
- <div
107
- data-slot="chart-tooltip"
108
- className="min-w-28 px-2.5 py-2 shadow-md rounded-md border border-border-default bg-surface-card"
109
- >
110
- {heading !== undefined ? (
111
- <div className="mb-1.5 font-medium leading-3 text-[11px] text-text-secondary">
112
- {String(heading)}
113
- </div>
114
- ) : null}
115
- <ul className="gap-1 flex flex-col">
116
- {payload.map((row, i) => {
117
- const si = Math.max(
118
- 0,
119
- series.findIndex((s) => s.key === row.name)
120
- )
121
- const s = series[si]
122
- return (
123
- <li
124
- key={i}
125
- className="gap-2 leading-3 flex items-center text-[11px]"
126
- >
127
- <Dot tone={seriesTone(series.length > 1 ? si : i)} />
128
- <span className="truncate text-text-secondary">
129
- {series.length > 1
130
- ? s?.label
131
- : (row.payload?.label ?? s?.label)}
132
- </span>
133
- <span className="pl-3 font-medium ml-auto text-text-primary">
134
- {fmt(s, row.value)}
135
- </span>
136
- </li>
137
- )
138
- })}
139
- </ul>
140
- </div>
141
- )
142
- }
143
-
144
- const AXIS = {
145
- tick: { fontSize: 11, fill: "var(--text-secondary)" },
146
- axisLine: false,
147
- tickLine: false,
148
- dataKey: "label",
149
- padding: { left: 12, right: 12 },
150
- interval: 0 as const,
151
- }
152
-
153
- /**
154
- * Compact chart card: title, subtitle, reference or type pill, plot and legend.
155
- * Series colours are the `--chart-series-*` tokens, grid is `--chart-grid`.
156
- *
157
- * @sketch "Component / Chart / {Pie, Area, Line, Bar}"
158
- */
159
- export function Chart({
160
- type,
161
- title,
162
- subtitle,
163
- badge,
164
- reference,
165
- data,
166
- series,
167
- center,
168
- headerless = false,
169
- plotHeight,
170
- className,
171
- ...props
172
- }: IChartProps): React.ReactElement {
173
- const height = plotHeight ?? (type === "pie" ? 140 : 150)
174
- const empty = data.length === 0 || series.length === 0
175
- const reduceMotion = useReducedMotion()
176
- const anim = {
177
- isAnimationActive: !reduceMotion,
178
- animationDuration: 700,
179
- animationEasing: "ease-out" as const,
180
- }
181
- const tooltip = (
182
- <Tooltip
183
- content={<ChartTooltip series={series} />}
184
- cursor={{
185
- stroke: "var(--chart-grid)",
186
- fill: "var(--chart-grid)",
187
- fillOpacity: 0.4,
188
- }}
189
- isAnimationActive={!reduceMotion}
190
- animationDuration={150}
191
- />
192
- )
193
-
194
- const plot = (
195
- <>
196
- {type === "pie" ? (
197
- <div className="gap-6 flex flex-wrap items-center justify-center">
198
- <div className="relative shrink-0" style={{ width: height, height }}>
199
- {empty ? null : (
200
- <ResponsiveContainer width="100%" height="100%">
201
- <PieChart>
202
- {tooltip}
203
- <Pie
204
- data={data}
205
- dataKey={series[0].key}
206
- nameKey="label"
207
- innerRadius="66%"
208
- outerRadius="100%"
209
- paddingAngle={2}
210
- stroke="none"
211
- {...anim}
212
- >
213
- {data.map((_, i) => (
214
- <Cell
215
- key={i}
216
- fill={`var(--chart-series-${(i % 5) + 1})`}
217
- className="transition-opacity duration-200 hover:opacity-80"
218
- />
219
- ))}
220
- </Pie>
221
- </PieChart>
222
- </ResponsiveContainer>
223
- )}
224
- {center ? (
225
- <div className="inset-0 pointer-events-none absolute flex flex-col items-center justify-center">
226
- <span className="font-semibold leading-5 text-[17px] text-text-primary">
227
- {center.value}
228
- </span>
229
- {center.label ? (
230
- <span className="leading-3 text-[11px] text-text-secondary">
231
- {center.label}
232
- </span>
233
- ) : null}
234
- </div>
235
- ) : null}
236
- </div>
237
- {empty ? null : (
238
- <ul className="min-w-24 gap-3.5 flex flex-1 flex-col">
239
- {data.map((d, i) => (
240
- <li key={d.label} className="flex">
241
- <LegendItem
242
- tone={seriesTone(i)}
243
- value={fmt(series[0], d[series[0].key])}
244
- className="w-full"
245
- >
246
- {d.label}
247
- </LegendItem>
248
- </li>
249
- ))}
250
- </ul>
251
- )}
252
- </div>
253
- ) : (
254
- <>
255
- <div style={{ height }} className="w-full">
256
- {empty ? null : (
257
- <ResponsiveContainer width="100%" height="100%">
258
- {type === "bar" ? (
259
- <BarChart
260
- data={data}
261
- margin={{ top: 4, right: 4, bottom: 0, left: 4 }}
262
- barCategoryGap="30%"
263
- >
264
- <CartesianGrid
265
- vertical={false}
266
- stroke="var(--chart-grid)"
267
- />
268
- <XAxis {...AXIS} />
269
- {tooltip}
270
- {series.map((s, i) => (
271
- <Bar
272
- key={s.key}
273
- dataKey={s.key}
274
- fill={seriesColor(s, i)}
275
- radius={[4, 4, 0, 0]}
276
- {...anim}
277
- />
278
- ))}
279
- </BarChart>
280
- ) : type === "line" ? (
281
- <LineChart
282
- data={data}
283
- margin={{ top: 4, right: 8, bottom: 0, left: 8 }}
284
- >
285
- <CartesianGrid
286
- vertical={false}
287
- stroke="var(--chart-grid)"
288
- />
289
- <XAxis {...AXIS} />
290
- {tooltip}
291
- {series.map((s, i) => (
292
- <Line
293
- key={s.key}
294
- type="monotone"
295
- dataKey={s.key}
296
- stroke={seriesColor(s, i)}
297
- strokeWidth={2}
298
- dot={{
299
- r: 3,
300
- strokeWidth: 2,
301
- fill: "var(--surface-card)",
302
- }}
303
- activeDot={{ r: 5, strokeWidth: 0 }}
304
- {...anim}
305
- />
306
- ))}
307
- </LineChart>
308
- ) : (
309
- <AreaChart
310
- data={data}
311
- margin={{ top: 4, right: 8, bottom: 0, left: 8 }}
312
- >
313
- <CartesianGrid
314
- vertical={false}
315
- stroke="var(--chart-grid)"
316
- />
317
- <XAxis {...AXIS} />
318
- {tooltip}
319
- {series.map((s, i) => (
320
- <Area
321
- key={s.key}
322
- type="monotone"
323
- dataKey={s.key}
324
- stroke={seriesColor(s, i)}
325
- strokeWidth={2}
326
- fill={
327
- i === 0 ? "var(--chart-area-fill)" : seriesColor(s, i)
328
- }
329
- fillOpacity={1}
330
- activeDot={{ r: 5, strokeWidth: 0 }}
331
- {...anim}
332
- />
333
- ))}
334
- </AreaChart>
335
- )}
336
- </ResponsiveContainer>
337
- )}
338
- </div>
339
- {empty ? null : (
340
- <div className="gap-4 flex flex-wrap">
341
- {series.map((s, i) => (
342
- <LegendItem key={s.key} tone={seriesTone(i)}>
343
- {s.label}
344
- </LegendItem>
345
- ))}
346
- </div>
347
- )}
348
- </>
349
- )}
350
- </>
351
- )
352
-
353
- // Embedded (InsightCards) plots stay bare; a titled chart is a framed card.
354
- if (headerless) {
355
- return (
356
- <div
357
- data-slot="chart"
358
- data-type={type}
359
- className={cn("w-80 gap-2 flex max-w-full flex-col", className)}
360
- {...props}
361
- >
362
- {plot}
363
- </div>
364
- )
365
- }
366
- return (
367
- <CardFrame
368
- data-slot="chart"
369
- data-type={type}
370
- className={cn(
371
- "w-80 max-w-full [--frame-radius:var(--radius-md)]",
372
- className
373
- )}
374
- {...props}
375
- >
376
- <CardFrameHeader className="gap-x-3 gap-y-2 px-3.5 py-3 flex flex-row flex-wrap items-start justify-between">
377
- <div className="min-w-0 basis-36 gap-0.5 flex flex-1 flex-col">
378
- <span className="font-semibold leading-4 text-[13px] text-text-primary">
379
- {title}
380
- </span>
381
- {subtitle ? (
382
- <span className="leading-3 text-[11px] text-text-secondary">
383
- {subtitle}
384
- </span>
385
- ) : null}
386
- </div>
387
- {reference ? (
388
- <Reference
389
- {...reference}
390
- className={cn("max-w-full shrink-0", reference.className)}
391
- />
392
- ) : (
393
- <Pill>{badge ?? TYPE_LABEL[type]}</Pill>
394
- )}
395
- </CardFrameHeader>
396
- <Card>
397
- <CardPanel className="gap-3 p-3.5 flex flex-col">{plot}</CardPanel>
398
- </Card>
399
- </CardFrame>
400
- )
401
- }
2
+ // Compatibility entry point; implementation follows the scaffold module layout.
3
+ export * from "./chart/index"
@@ -4,13 +4,7 @@ export {
4
4
  type TAttachmentProps,
5
5
  type TAttachmentVariant,
6
6
  } from "./attachment"
7
- export {
8
- Chart,
9
- type TChartDatum,
10
- type IChartProps,
11
- type IChartSeries,
12
- type TChartType,
13
- } from "./chart"
7
+ export * from "./chart"
14
8
  export {
15
9
  Markdown,
16
10
  markdownViewAtom,
@@ -19,6 +19,14 @@ type TPreviewProps = TStoryData<
19
19
  | "center"
20
20
  | "headerless"
21
21
  | "plotHeight"
22
+ | "width"
23
+ | "height"
24
+ | "variant"
25
+ | "interactive"
26
+ | "loading"
27
+ | "showLegend"
28
+ | "animation"
29
+ | "renderer"
22
30
  >
23
31
  >
24
32
 
@@ -31,6 +39,12 @@ export const story = defineStory({
31
39
  args: {
32
40
  initial: {
33
41
  type: "bar",
42
+ variant: "default",
43
+ interactive: true,
44
+ loading: false,
45
+ showLegend: true,
46
+ animation: true,
47
+ renderer: "canvas",
34
48
  title: "Word occurrences by book",
35
49
  subtitle: "Illustrative counts · selected Bible books",
36
50
  reference: {
@@ -39,7 +53,8 @@ export const story = defineStory({
39
53
  "Matched tokens grouped by Strong’s entry and Bible book. These counts are illustrative.",
40
54
  },
41
55
  headerless: false,
42
- plotHeight: 244,
56
+ width: "100%",
57
+ height: 360,
43
58
  data: wordOccurrences,
44
59
  series: [
45
60
  {