@antadesign/anta 0.3.31 → 0.3.33

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 (39) hide show
  1. package/dist/avatar-core.d.ts +7 -2
  2. package/dist/box-types.d.ts +12 -1
  3. package/dist/bundle.css +1 -1
  4. package/dist/bundle.js +148 -73
  5. package/dist/components/Box.d.ts +18 -6
  6. package/dist/components/Box.js +4 -0
  7. package/dist/components/Expander.d.ts +17 -9
  8. package/dist/components/Expander.js +14 -1
  9. package/dist/components/Input.d.ts +9 -3
  10. package/dist/components/Input.js +2 -0
  11. package/dist/components/Slider.d.ts +22 -10
  12. package/dist/components/Slider.js +9 -5
  13. package/dist/elements/a-box.css +1 -1
  14. package/dist/elements/a-box.d.ts +2 -0
  15. package/dist/elements/a-box.js +74 -32
  16. package/dist/elements/a-button.js +2 -4
  17. package/dist/elements/a-dialog.js +2 -0
  18. package/dist/elements/a-expander.css +1 -1
  19. package/dist/elements/a-expander.js +118 -39
  20. package/dist/elements/a-input-time.js +1 -2
  21. package/dist/elements/a-input.css +1 -1
  22. package/dist/elements/a-input.js +11 -8
  23. package/dist/elements/a-menu.d.ts +1 -1
  24. package/dist/elements/a-menu.js +57 -7
  25. package/dist/elements/a-slider.d.ts +7 -5
  26. package/dist/elements/a-slider.js +256 -59
  27. package/dist/general_types.d.ts +27 -10
  28. package/dist/index.d.ts +2 -2
  29. package/dist/theme-antune.css +1 -1
  30. package/docs/changelog.md +59 -0
  31. package/docs/components/box.md +81 -24
  32. package/docs/components/button.md +9 -6
  33. package/docs/components/expander.md +88 -47
  34. package/docs/components/input.md +29 -9
  35. package/docs/components/panel.md +3 -2
  36. package/docs/components/select-faceted.md +40 -26
  37. package/docs/components/slider.md +139 -4
  38. package/docs/packages/plot.md +392 -38
  39. package/package.json +4 -2
@@ -333,14 +333,15 @@ Install the package to use plots in React, Preact, or plain JavaScript.
333
333
  npm install @antadesign/plot # or pnpm / bun
334
334
  ```
335
335
 
336
- Import `Plot` from `/components` and series factories from `@antadesign/plot`.
337
- React 19 is a peer dependency. For Preact, use `preact/compat` aliases.
336
+ Import `Plot` and series factories from `@antadesign/plot`. These root component
337
+ imports require version `0.1.3` or later. Anta `^0.3.30` and React `^19.0.0` are
338
+ peer dependencies. For Preact, use `preact/compat` aliases.
338
339
 
339
340
  ```tsx
340
- import { Plot } from '@antadesign/plot/components'
341
- import { line } from '@antadesign/plot'
341
+ import { Plot, line } from '@antadesign/plot'
342
342
  import '@antadesign/plot/elements/a-plot-surface'
343
343
  import '@antadesign/anta/elements/a-tooltip'
344
+ import '@antadesign/anta/theme-antune.css'
344
345
 
345
346
  const plotArgs = {
346
347
  height: 300,
@@ -353,8 +354,9 @@ function Example() {
353
354
  ```
354
355
 
355
356
  `Plot` is a function component that uses Anta's configured JSX factory and hooks.
356
- It needs no DOM registration. Import the surface and tooltip registration modules
357
- once in your browser entry; their elements install their styles automatically.
357
+ Importing it does not register browser elements. Import the surface and tooltip
358
+ registration modules once in your browser entry; their elements install their
359
+ base styles automatically. Load your application's Anta theme separately.
358
360
  `Plot` handles canvas setup, drawing, interactions, and cleanup.
359
361
 
360
362
  By default, `Plot` fills its parent. Give the parent a height or set `plotArgs.height`,
@@ -364,9 +366,10 @@ With a worker renderer and a DOM bridge that transfers the surface's canvases,
364
366
 
365
367
  For plain JavaScript or TypeScript, import `@antadesign/plot/elements/a-plot`, then use `<a-plot>` and assign its `plotArgs` property.
366
368
 
367
- For custom host integrations, `/components` also exports `PlotSurface`.
368
- Register it with `@antadesign/plot/elements/a-plot-surface`; your host manages
369
- controllers, drawing, and tooltip content.
369
+ `Plot` owns canvas rendering, interactions, and cleanup. The package also exports
370
+ `PlotSurface` as a low-level JSX wrapper, but controllers and drawing lifecycle
371
+ helpers remain internal. Customize marks through `renderer` and
372
+ `highlight_renderer`, and tooltips through the series or root tooltip callbacks.
370
373
 
371
374
  ## Plot arguments
372
375
 
@@ -393,7 +396,7 @@ function presentation(): PlotArgs<Node> {
393
396
  return {
394
397
  series: [scatter<Node>({ data: examplePoints, size: 3,
395
398
  color: { light: '#9ca3af', dark: '#6b7280' }, hoverable: false })],
396
- title: { text: 'Plot title', size: 16, color: { light: '#374151', dark: '#e5e7eb' } },
399
+ title: { text: 'Plot title', font: { size: 16, color: { light: '#374151', dark: '#e5e7eb' } } },
397
400
  height: 260,
398
401
  margin: { top: 38, right: 16, bottom: 42, left: 52 },
399
402
  background: { light: '#f3f4f6', dark: '#202124' },
@@ -448,7 +451,8 @@ document.body.append(plot)
448
451
  |---|---|---|---|
449
452
  | [`series`](#series) | `Series[]` | Required | What to draw, in paint order. Build each one with a factory. |
450
453
  | [`axis?`](#axes) | `{ x?: AxisArgs, y?: AxisArgs }` | Inferred from data | Per-axis scale, label, ticks, and domain. |
451
- | [`title?`](#title) | `string \| { text, size?, color? }` | None | Plot title. |
454
+ | [`title?`](#title) | `string \| { text, font? }` | None | Plot title. |
455
+ | [`font?`](#font) | `FontArg` | Role defaults | Shared font configuration. |
452
456
  | `width?` | `number` | Container width | Canvas width in pixels. Overrides the wrapper's CSS width. |
453
457
  | `height?` | `number` | Parent height for `Plot`; `300` for `<a-plot>` | Canvas height in pixels. Overrides the host's CSS height. |
454
458
  | [`margin?`](#margins) | `number \| { top?, right?, bottom?, left? }` | `60` per side; `2` without axes or title | Space reserved around the plot area for axes and title. |
@@ -683,6 +687,7 @@ Scatter arguments
683
687
  | [`on_select?`](#selection-callbacks) | `SelectFn` | None | Callback receiving the selected point and its source row. |
684
688
  | `hoverable?` | `boolean` | `true` | Allow the series to participate in hit testing. |
685
689
  | `highlight?` | `boolean` | `true` | Draw hover feedback for a hit point. |
690
+ | `highlight_color?` | `ColorArg` | Automatic | Exact hover color, including opacity. For custom series, affects only the fallback renderer. Stacked bars also accept a `ThemeColor[]` palette. |
686
691
 
687
692
  ```ts
688
693
  import { scatter } from '@antadesign/plot'
@@ -810,6 +815,7 @@ Line arguments
810
815
  | [`on_select?`](#selection-callbacks) | `SelectFn` | None | Callback receiving the selected point and its source row. |
811
816
  | `hoverable?` | `boolean` | `true` | Allow the series to participate in hit testing. |
812
817
  | `highlight?` | `boolean` | `true` | Draw hover feedback for a hit point. |
818
+ | `highlight_color?` | `ColorArg` | Automatic | Exact hover color, including opacity. For custom series, affects only the fallback renderer. Stacked bars also accept a `ThemeColor[]` palette. |
813
819
 
814
820
  ```ts
815
821
  import { line } from '@antadesign/plot'
@@ -1025,6 +1031,7 @@ Bar arguments
1025
1031
  | [`on_select?`](#selection-callbacks) | `SelectFn` | None | Callback receiving the selected point and its source row. |
1026
1032
  | `hoverable?` | `boolean` | `true` | Allow the series to participate in hit testing. |
1027
1033
  | `highlight?` | `boolean` | `true` | Draw hover feedback for a hit point. |
1034
+ | `highlight_color?` | `ColorArg` | Automatic | Exact hover color, including opacity. For custom series, affects only the fallback renderer. Stacked bars also accept a `ThemeColor[]` palette. |
1028
1035
 
1029
1036
  ```ts
1030
1037
  import { bar } from '@antadesign/plot'
@@ -1460,6 +1467,7 @@ Area arguments
1460
1467
  | [`on_select?`](#selection-callbacks) | `SelectFn` | None | Callback receiving the selected point and its source row. |
1461
1468
  | `hoverable?` | `boolean` | `true` | Allow the series to participate in hit testing. |
1462
1469
  | `highlight?` | `boolean` | `true` | Draw hover feedback for a hit point. |
1470
+ | `highlight_color?` | `ColorArg` | Automatic | Exact hover color, including opacity. For custom series, affects only the fallback renderer. Stacked bars also accept a `ThemeColor[]` palette. |
1463
1471
 
1464
1472
  ```ts
1465
1473
  import { area } from '@antadesign/plot'
@@ -1720,6 +1728,7 @@ Rect arguments
1720
1728
  | [`on_select?`](#selection-callbacks) | `SelectFn` | None | Callback receiving the selected point and its source row. |
1721
1729
  | `hoverable?` | `boolean` | `true` | Allow the series to participate in hit testing. |
1722
1730
  | `highlight?` | `boolean` | `true` | Draw hover feedback for a hit point. |
1731
+ | `highlight_color?` | `ColorArg` | Automatic | Exact hover color, including opacity. For custom series, affects only the fallback renderer. Stacked bars also accept a `ThemeColor[]` palette. |
1723
1732
 
1724
1733
  ```ts
1725
1734
  import { rect } from '@antadesign/plot'
@@ -1908,6 +1917,7 @@ Rule arguments
1908
1917
  | [`on_select?`](#selection-callbacks) | `SelectFn` | None | Callback receiving the selected point and its source row. |
1909
1918
  | `hoverable?` | `boolean` | `true` | Allow the series to participate in hit testing. |
1910
1919
  | `highlight?` | `boolean` | `true` | Draw hover feedback for a hit point. |
1920
+ | `highlight_color?` | `ColorArg` | Automatic | Exact hover color, including opacity. For custom series, affects only the fallback renderer. Stacked bars also accept a `ThemeColor[]` palette. |
1911
1921
 
1912
1922
  ```ts
1913
1923
  import { rule } from '@antadesign/plot'
@@ -2079,6 +2089,37 @@ plot.plotArgs = {
2079
2089
  return index < 0 ? null : index
2080
2090
  },
2081
2091
  tooltip: ({ row }) => document.createTextNode('Group ' + row?.name + ': ' + row?.total + ' connections'),
2092
+ highlight_renderer: (series, i, { ctx, inner, color_at, highlight_color_at }) => {
2093
+ const rows = series.rows as typeof groups
2094
+ const group = rows[i]
2095
+ const cx = (inner.left + inner.right) / 2
2096
+ const cy = (inner.top + inner.bottom) / 2
2097
+ const radius = Math.min(inner.right - inner.left, inner.bottom - inner.top) / 2 - 32
2098
+ const point = (a: number) => [cx + radius * Math.cos(a), cy + radius * Math.sin(a)]
2099
+ // Match the main renderer: ribbons take the lower-index group's color.
2100
+ ctx.fillStyle = color_at(i)
2101
+ ctx.globalAlpha = 0.45
2102
+ for (let j = i + 1; j < rows.length; j++) {
2103
+ const source = group.segments[j]
2104
+ const target = rows[j].segments[i]
2105
+ const [sx, sy] = point(source.start)
2106
+ const [tx, ty] = point(target.start)
2107
+ ctx.beginPath()
2108
+ ctx.moveTo(sx, sy)
2109
+ ctx.arc(cx, cy, radius, source.start, source.end)
2110
+ ctx.quadraticCurveTo(cx, cy, tx, ty)
2111
+ ctx.arc(cx, cy, radius, target.start, target.end)
2112
+ ctx.quadraticCurveTo(cx, cy, sx, sy)
2113
+ ctx.closePath()
2114
+ ctx.fill()
2115
+ }
2116
+ ctx.globalAlpha = 1
2117
+ ctx.strokeStyle = highlight_color_at(i)
2118
+ ctx.lineWidth = 13
2119
+ ctx.beginPath()
2120
+ ctx.arc(cx, cy, radius + 7, group.start, group.end)
2121
+ ctx.stroke()
2122
+ },
2082
2123
  renderer: (_, { ctx, inner, color_at }) => {
2083
2124
  const cx = (inner.left + inner.right) / 2
2084
2125
  const cy = (inner.top + inner.bottom) / 2
@@ -2195,11 +2236,12 @@ plot.plotArgs = {
2195
2236
  return null
2196
2237
  },
2197
2238
  tooltip: ({ row }) => document.createTextNode(row?.group + ' · 80 samples · mean ' + Number(row?.mean).toFixed(1)),
2198
- renderer: (_, { ctx, x_scale, resolve_y, color_at, inner }) => {
2239
+ renderer: (series, { ctx, x_scale, resolve_y, color_at, inner }) => {
2199
2240
  const step = (inner.bottom - inner.top) / data.length
2200
2241
  ctx.save()
2201
2242
  // Paint from the top row down, so foreground ridges cover the preceding tails.
2202
- data.forEach((row, i) => {
2243
+ const rows = series.rows as typeof data
2244
+ rows.forEach((row, i) => {
2203
2245
  const center = resolve_y(i)
2204
2246
  if (center === undefined) return
2205
2247
  const baseline = center + step * 0.45
@@ -2213,7 +2255,7 @@ plot.plotArgs = {
2213
2255
  })
2214
2256
  ctx.lineTo(Number(x_scale(100)), baseline)
2215
2257
  ctx.closePath()
2216
- ctx.globalAlpha = [1, 0.7, 0.85, 0.65, 0.8, 0.7][i]
2258
+ ctx.globalAlpha = [1, 0.7, 0.85, 0.65, 0.8, 0.7][data.indexOf(row)]
2217
2259
  ctx.fill()
2218
2260
  ctx.globalAlpha = 1
2219
2261
  ctx.stroke()
@@ -2282,9 +2324,9 @@ plot.plotArgs = {
2282
2324
  data,
2283
2325
  x: 'group',
2284
2326
  color: (_, i) => [
2285
- { light: '#0d9488', dark: '#5eead4' },
2286
- { light: '#7c3aed', dark: '#c4b5fd' },
2287
- { light: '#c2410c', dark: '#fdba74' },
2327
+ { light: 'color-mix(in oklch, #0d9488 60%, transparent)', dark: 'color-mix(in oklch, #5eead4 60%, transparent)' },
2328
+ { light: 'color-mix(in oklch, #7c3aed 60%, transparent)', dark: 'color-mix(in oklch, #c4b5fd 60%, transparent)' },
2329
+ { light: 'color-mix(in oklch, #c2410c 60%, transparent)', dark: 'color-mix(in oklch, #fdba74 60%, transparent)' },
2288
2330
  ][i],
2289
2331
  hit_test: (_, { cursor, y_scale, resolve_x, inner }) => {
2290
2332
  const width = Math.min(34, (inner.right - inner.left) / 9)
@@ -2302,10 +2344,11 @@ plot.plotArgs = {
2302
2344
  tooltip: ({ row }) => document.createTextNode('Group ' + row?.group
2303
2345
  + ' · median ' + Number(row?.median).toFixed(1)
2304
2346
  + ' · middle 50%: ' + Number(row?.q1).toFixed(1) + '–' + Number(row?.q3).toFixed(1)),
2305
- renderer: (_, { ctx, y_scale, resolve_x, color_at, inner }) => {
2347
+ renderer: (series, { ctx, y_scale, resolve_x, color_at, inner }) => {
2306
2348
  const width = Math.min(34, (inner.right - inner.left) / 9)
2307
2349
  ctx.save()
2308
- data.forEach((row, i) => {
2350
+ const rows = series.rows as typeof data
2351
+ rows.forEach((row, i) => {
2309
2352
  const x = resolve_x(i)
2310
2353
  if (x === undefined) return
2311
2354
  ctx.fillStyle = color_at(i)
@@ -2348,6 +2391,73 @@ plot.plotArgs = {
2348
2391
  document.body.append(plot)
2349
2392
  ```
2350
2393
 
2394
+ ##### Custom hover highlights
2395
+
2396
+ Hover the points at x = 20 for the default single-point repaint, x = 50 for an explicit halo, and x = 80 for tooltips with highlighting disabled. Only the hovered point is repainted. Translucent fills strengthen when drawn again; an identical opaque fill may look unchanged.
2397
+
2398
+ Without `highlight_renderer`, Anta calls `renderer` on the overlay with a single-point series view. The aligned `x`, `y`, `rows`, `colors`, `highlight_colors`, and `labels` arrays contain only that point at index 0; scales and bounds are unchanged. Render from the supplied series, not captured full-data arrays, to use this fallback. Set `highlight_color` to override the fallback's color without changing its geometry.
2399
+
2400
+ For connected geometry or a different highlight, supply `highlight_renderer(series, point_index, context)`. It receives the full composed series, the original hovered index, and the normal render helpers plus `highlight_color_at(index)`. Anta handles DPR, clearing, clipping, and canvas-state isolation. Set `highlight: false` to disable either path. Both paths require data and hit testing.
2401
+
2402
+ ```ts
2403
+ import { custom, type CustomRendererFn, type CustomHitTestFn, type CustomHighlightRendererFn } from '@antadesign/plot'
2404
+ import type { APlotElement } from '@antadesign/plot/browser'
2405
+ import '@antadesign/plot/elements/a-plot'
2406
+
2407
+ const plot = document.createElement('a-plot') as APlotElement
2408
+ function highlights() {
2409
+ const renderer: CustomRendererFn<Node> = (series, { ctx, resolve_x, resolve_y, color_at }) => {
2410
+ for (let i = 0; i < series.x.length; i++) {
2411
+ const x = resolve_x(i), y = resolve_y(i)
2412
+ if (x === undefined || y === undefined) continue
2413
+ ctx.fillStyle = color_at(i)
2414
+ ctx.beginPath()
2415
+ ctx.arc(x, y, 16, 0, Math.PI * 2)
2416
+ ctx.fill()
2417
+ }
2418
+ }
2419
+ const hit_test: CustomHitTestFn<Node> = (series, { cursor, resolve_x, resolve_y }) => {
2420
+ for (let i = series.x.length - 1; i >= 0; i--) {
2421
+ const x = resolve_x(i), y = resolve_y(i)
2422
+ if (x !== undefined && y !== undefined && Math.hypot(cursor.x - x, cursor.y - y) <= 16) return i
2423
+ }
2424
+ return null
2425
+ }
2426
+ const highlight_renderer: CustomHighlightRendererFn<Node> = (series, i, { ctx, resolve_x, resolve_y, highlight_color_at }) => {
2427
+ const x = resolve_x(i), y = resolve_y(i)
2428
+ if (x === undefined || y === undefined) return
2429
+ ctx.strokeStyle = highlight_color_at(i)
2430
+ ctx.lineWidth = 3
2431
+ ctx.beginPath()
2432
+ ctx.arc(x, y, 22, 0, Math.PI * 2)
2433
+ ctx.stroke()
2434
+ }
2435
+ return {
2436
+ height: 260,
2437
+ axis: { x: { min: 0, max: 100 }, y: { min: 0, max: 100 } },
2438
+ margin: { top: 12, right: 12, bottom: 28, left: 32 },
2439
+ grid: false, zoom_pan: false,
2440
+ series: [
2441
+ custom<Node>({
2442
+ data: [{ x: 20, y: 35 }, { x: 20, y: 65 }], x: 'x', y: 'y', renderer, hit_test,
2443
+ color: 'color-mix(in oklch, teal 40%, transparent)', tooltip: true,
2444
+ }),
2445
+ custom<Node>({
2446
+ data: [{ x: 50, y: 35 }, { x: 50, y: 65 }], x: 'x', y: 'y', renderer, hit_test, highlight_renderer,
2447
+ color: { light: '#7c3aed', dark: '#c4b5fd' }, tooltip: true,
2448
+ }),
2449
+ custom<Node>({
2450
+ data: [{ x: 80, y: 35 }, { x: 80, y: 65 }], x: 'x', y: 'y', renderer, hit_test,
2451
+ color: '#94a3b8', highlight: false, tooltip: true,
2452
+ }),
2453
+ ],
2454
+ }
2455
+ }
2456
+
2457
+ plot.plotArgs = highlights()
2458
+ document.body.append(plot)
2459
+ ```
2460
+
2351
2461
  Custom arguments
2352
2462
 
2353
2463
  | Field | Type | Default | Description |
@@ -2356,6 +2466,9 @@ Custom arguments
2356
2466
  | `x?` | [`FieldArg`](#data-fields) | Unclaimed | Horizontal field or accessor; omitted axes do not contribute data bounds. |
2357
2467
  | `y?` | [`FieldArg`](#data-fields) | Unclaimed | Vertical field or accessor; omitted axes do not contribute data bounds. |
2358
2468
  | [`renderer`](#custom-rendering) | `CustomRendererFn` | Required | Draw the series using its data and the supplied canvas context and pixel resolvers. |
2469
+ | `highlight_renderer?` | `CustomHighlightRendererFn` | Single-point repaint | Draw on the hover overlay using the full series, hovered index, and highlight context. Requires data and hit testing. |
2470
+ | `highlight?` | `boolean` | `true` | Enable the custom callback or default single-point repaint. |
2471
+ | `highlight_color?` | `ColorArg` | Automatic | Exact hover color, including opacity. For custom series, affects only the fallback renderer. Stacked bars also accept a `ThemeColor[]` palette. |
2359
2472
  | [`hit_test?`](#custom-hit-testing) | `CustomHitTestFn` | None | Return a row index for a hit, or `null`. Required for custom tooltips and selection. |
2360
2473
  | `color?` | [`ColorArg`](#series-colors) | Black | Color made available to the renderer, including optional per-row colors. |
2361
2474
  | `axis_range?` | `{ x?: number[]; y?: number[] }` | Data extent | Explicit `[low, high]` bounds for either axis. |
@@ -2421,7 +2534,11 @@ to declare bounds when they cannot be inferred from the data.
2421
2534
 
2422
2535
  ### Hover highlights
2423
2536
 
2424
- Hover bars A through D to see their color intensify. Bar E sets `highlight: false`, so its appearance stays unchanged. Bar F uses `hoverable: false`, so it does not participate in hit testing.
2537
+ Bars A and B use the default highlight. Bar C uses an opaque orange `highlight_color`, and D uses a translucent magenta override. Bar E sets `highlight: false`, so its appearance stays unchanged. Bar F uses `hoverable: false`, so it does not participate in hit testing.
2538
+
2539
+ Set `highlight_color` to a CSS color, a `{ light, dark }` pair, or a per-row accessor. Stacked bars also accept a color array with one entry per segment. Anta uses the resolved color without shading or changing its opacity. Translucent highlights blend over the original mark. Omit the property to keep the default highlight.
2540
+
2541
+ For custom series, this property changes the fallback renderer's `color`, `colors`, and `color_at`. An explicit `highlight_renderer` keeps control of its canvas colors and helpers. A fallback renderer must use the supplied colors to reflect the override; a hard-coded canvas fill will stay unchanged.
2425
2542
 
2426
2543
  Hover feedback is temporary. For custom geometry, supply a [hit-testing callback](#custom-hit-testing).
2427
2544
 
@@ -2440,9 +2557,16 @@ const data = [
2440
2557
  plot.plotArgs = {
2441
2558
  series: [
2442
2559
  bar<Node>({
2443
- data: data.slice(0, 4), inset: 7, border_radius: 4,
2560
+ data: data.slice(0, 2), inset: 7, border_radius: 4,
2444
2561
  color: { light: 'rgba(13, 148, 136, 0.35)', dark: 'rgba(94, 234, 212, 0.35)' },
2445
2562
  }),
2563
+ bar<Node>({
2564
+ data: data.slice(2, 4), inset: 7, border_radius: 4,
2565
+ color: { light: '#0d9488', dark: '#5eead4' },
2566
+ highlight_color: row => row.x === 'C'
2567
+ ? { light: '#f97316', dark: '#fdba74' }
2568
+ : 'color-mix(in oklch, magenta 50%, transparent)',
2569
+ }),
2446
2570
  bar<Node>({
2447
2571
  data: data.slice(4, 5), inset: 7, border_radius: 4,
2448
2572
  color: { light: '#7c3aed', dark: '#c4b5fd' },
@@ -2557,6 +2681,98 @@ With the React component, `TooltipContent` is a `ReactNode`, including strings
2557
2681
  and JSX. With the standalone browser host, return a DOM `Node`. The core
2558
2682
  leaves the content type to the host.
2559
2683
 
2684
+ #### Composing tooltips across series
2685
+
2686
+ Set `tooltip` on the root plot config to compose one tooltip from all hovered series. The callback receives one slot per declared series, in declaration order. Each populated slot contains `{ series, data }`: `series` is the original factory arguments, and `data` contains the hovered point’s `x`, `y`, and optional `row` and `label`. A series with no hit leaves `undefined`. Destructure the array to match your series order. If you need identifiers, include them in your data rows.
2687
+
2688
+ Use `hit.series.color` to read the original color option, including a color defined outside the data. Factory arguments are retained by reference, not copied or resolved; treat them as read-only. Color accessors remain functions, and theme colors remain light/dark pairs. Manually constructed series without factory arguments have `hit.series === undefined`.
2689
+
2690
+ The root callback receives hits even when a series has no tooltip configured. Series with `hoverable: false` retain an empty slot. Per-series tooltip callbacks are not called while a root callback is configured. Returning `null` or `undefined` hides the tooltip without falling back; removing the root callback restores the existing stacked behavior. The callback is not called when there are no hits.
2691
+
2692
+ Hover the overlapping rectangles to compare baseline and current bounds under one window title. The tooltip matches hits by position, places the values side by side, and calculates the change for each bound. Outside the overlap, missing values appear as “—”.
2693
+
2694
+ ```ts
2695
+ import { rect, type APlotElement } from '@antadesign/plot/browser'
2696
+ import '@antadesign/plot/elements/a-plot'
2697
+
2698
+ const plot = document.createElement('a-plot') as APlotElement
2699
+ const data = [
2700
+ { x: 8, x2: 38, y: 15, y2: 48, name: 'Window A' },
2701
+ { x: 55, x2: 86, y: 48, y2: 82, name: 'Window B' },
2702
+ ]
2703
+
2704
+ plot.plotArgs = {
2705
+ series: [
2706
+ rect({
2707
+ data, tooltip: true,
2708
+ color: { light: 'rgba(13, 148, 136, 0.45)', dark: 'rgba(94, 234, 212, 0.45)' },
2709
+ stroke: { color: { light: '#0d9488', dark: '#5eead4' }, width: 2 },
2710
+ }),
2711
+ rect({
2712
+ data: data.map(row => ({
2713
+ ...row, x: row.x + 12, x2: row.x2 + 8, y: row.y + 12, y2: row.y2 + 10,
2714
+ })),
2715
+ color: { light: 'rgba(124, 58, 237, 0.45)', dark: 'rgba(196, 181, 253, 0.45)' },
2716
+ stroke: { color: { light: '#7c3aed', dark: '#c4b5fd' }, width: 2 },
2717
+ tooltip: ({ row }) => {
2718
+ const content = document.createElement('div')
2719
+ const title = document.createElement('strong')
2720
+ title.textContent = String(row.name)
2721
+ const value = document.createElement('div')
2722
+ value.textContent = `Bounds: x ${row.x}–${row.x2}, y ${row.y}–${row.y2}`
2723
+ content.append(title, value)
2724
+ return content
2725
+ },
2726
+ }),
2727
+ ],
2728
+ tooltip: ([baseline_hit, current_hit]) => {
2729
+ const baseline = baseline_hit?.data.row
2730
+ const current = current_hit?.data.row
2731
+ const content = document.createElement('div')
2732
+ const title = document.createElement('strong')
2733
+ title.textContent = String((current ?? baseline)?.name ?? 'Window comparison')
2734
+ const table = document.createElement('table')
2735
+ const header = table.createTHead().insertRow()
2736
+ for (const label of ['Bound', 'Baseline', 'Current', 'Change']) {
2737
+ const cell = document.createElement('th')
2738
+ cell.scope = 'col'
2739
+ cell.textContent = label
2740
+ header.append(cell)
2741
+ }
2742
+ const body = table.createTBody()
2743
+ for (const [field, label] of [['x', 'X start'], ['x2', 'X end'], ['y', 'Y start'], ['y2', 'Y end']]) {
2744
+ const before = baseline?.[field]
2745
+ const after = current?.[field]
2746
+ const delta = typeof before === 'number' && typeof after === 'number' ? after - before : undefined
2747
+ const row = body.insertRow()
2748
+ const heading = document.createElement('th')
2749
+ heading.scope = 'row'
2750
+ heading.textContent = label
2751
+ row.append(heading)
2752
+ for (const value of [before ?? '—', after ?? '—', delta === undefined ? '—' : `${delta > 0 ? '+' : ''}${delta}`]) {
2753
+ row.insertCell().textContent = String(value)
2754
+ }
2755
+ }
2756
+ content.append(title, table)
2757
+ return content
2758
+ },
2759
+ height: 260,
2760
+ margin: { top: 8, right: 12, bottom: 24, left: 32 },
2761
+ axis: { x: { min: 0, max: 100, label: '' }, y: { min: 0, max: 100, label: '' } },
2762
+ background: { light: '#ffffff', dark: '#151b28' },
2763
+ chrome_color: { light: '#e2e8f0', dark: '#334155' },
2764
+ grid: false, border: false, zoom_pan: false,
2765
+ }
2766
+
2767
+ // Remove the root callback for stacked per-series tooltips:
2768
+ // plot.plotArgs = { ...plot.plotArgs, tooltip: undefined }
2769
+ // Suppress tooltips without falling back:
2770
+ // plot.plotArgs = { ...plot.plotArgs, tooltip: () => undefined }
2771
+ document.body.append(plot)
2772
+ ```
2773
+
2774
+ The callback type is `PlotTooltipFn<TooltipContent>`, receiving `PlotTooltipHit[]`. As with series callbacks, return a DOM `Node` for the standalone browser host or a `ReactNode` for React.
2775
+
2560
2776
  ### Selection
2561
2777
 
2562
2778
  Select a point to display its source data in the preview’s text readout. `on_select` receives the selected point and its original row, so you can update a detail panel or application state.
@@ -2880,6 +3096,147 @@ Each `AxisViewport` contains:
2880
3096
 
2881
3097
  ## Configuration details
2882
3098
 
3099
+ ### Font
3100
+
3101
+ `font` on the plot supplies shared text settings. `title.font`,
3102
+ `axis.x.label.font`, `axis.y.label.font`, and each axis's `tick_label.font`
3103
+ accept local overrides. A string is shorthand for `{ family: string }`.
3104
+ Fields resolve individually from local settings, plot settings, and role defaults.
3105
+ Font assets are supplied by the application.
3106
+
3107
+ | Field | Type | Constraint |
3108
+ | --- | --- | --- |
3109
+ | `family?` | `string` | CSS font family or family list. |
3110
+ | `size?` | `number` | Positive finite CSS pixels. |
3111
+ | `weight?` | `number` | Finite CSS weight from 1 to 1000. |
3112
+ | `color?` | [`ThemeColor`](#theme-colors) | Color string or light/dark pair. |
3113
+ | `italic?` | `boolean` | Select italic text. |
3114
+ | `condensed?` | `boolean` | Select condensed text. |
3115
+ | `letter_spacing?` | `number` | Finite CSS pixels; negative values are allowed. |
3116
+ | `word_spacing?` | `number` | Finite CSS pixels; negative values are allowed. |
3117
+ | `caps?` | `boolean \| FontCaps` | `true` means `all-small-caps`; `false` means `normal`. |
3118
+
3119
+ `FontCaps` accepts `normal`, `small-caps`, `all-small-caps`, `petite-caps`,
3120
+ `all-petite-caps`, `unicase`, and `titling-caps`.
3121
+
3122
+ Canvas drawing and text measurement use the same resolved font settings.
3123
+ Titles default to 14px and axis names to 12px, using the initially inherited
3124
+ CSS font family with a sans-serif fallback. Tick labels default to 10px monospace. The default weight is 400. Root settings override
3125
+ these role defaults; local settings override the root field by field.
3126
+
3127
+ Plots capture the inherited CSS font family once when they mount. Explicit
3128
+ plot-level and local families take precedence. Later CSS font changes and
3129
+ font-loading completion do not trigger a redraw. Make fonts available before
3130
+ drawing; replacing `plotArgs` applies explicit font configuration changes.
3131
+
3132
+ Different fonts by role
3133
+
3134
+ The title combines bold weight, condensed width, and tighter letters. The axes switch to italic serif and spaced monospace, with colors that adapt to the theme.
3135
+
3136
+ ```ts
3137
+ import { scatter, type PlotArgs } from '@antadesign/plot'
3138
+ import '@antadesign/plot/elements/a-plot'
3139
+ import type { APlotElement } from '@antadesign/plot/browser'
3140
+
3141
+ function base(): PlotArgs<Node> {
3142
+ return {
3143
+ series: [scatter<Node>({ data: [
3144
+ { x: 12, y: 18 }, { x: 30, y: 42 }, { x: 48, y: 35 },
3145
+ { x: 58, y: 64 }, { x: 76, y: 58 }, { x: 88, y: 82 },
3146
+ ], size: 3, color: { light: '#9ca3af', dark: '#6b7280' }, hoverable: false })],
3147
+ height: 260,
3148
+ margin: { top: 20, right: 24, bottom: 48, left: 76 },
3149
+ axis: { x: { min: 0, max: 100, label: 'Time' }, y: { min: 0, max: 100, label: 'Value' } },
3150
+ background: { light: '#ffffff', dark: '#202124' },
3151
+ chrome_color: { light: '#9ca3af', dark: '#d1d5db' },
3152
+ border: true,
3153
+ grid: false,
3154
+ zoom_pan: false,
3155
+ }
3156
+ }
3157
+
3158
+ function fontRoles(): PlotArgs<Node> {
3159
+ return {
3160
+ ...base(),
3161
+ margin: { top: 48, right: 20, bottom: 58, left: 72 },
3162
+ font: { family: '"TT Interphases Pro Variable", sans-serif', size: 13, weight: 450,
3163
+ color: { light: '#713fff', dark: '#c4b5fd' } },
3164
+ title: { text: '22px · bold · condensed',
3165
+ font: { size: 22, weight: 750, condensed: true, letter_spacing: -0.35 } },
3166
+ axis: {
3167
+ x: { min: 0, max: 100,
3168
+ label: { text: 'Serif · italic · 16px', font: { family: 'Georgia, serif', size: 16, italic: true,
3169
+ color: { light: '#c2410c', dark: '#fdba74' } } } },
3170
+ y: { min: 0, max: 100,
3171
+ label: { text: 'Mono · +1px letters', font: { family: 'monospace', size: 12, letter_spacing: 1 } } },
3172
+ },
3173
+ }
3174
+ }
3175
+
3176
+ await Promise.all([
3177
+ document.fonts.load('750 22px "TT Interphases Pro Variable"'),
3178
+ document.fonts.load('italic 600 20px "TT Interphases Pro Variable"'),
3179
+ ])
3180
+ const plot = document.querySelector<APlotElement>('a-plot')!
3181
+ plot.plotArgs = fontRoles()
3182
+ ```
3183
+
3184
+ Shared settings and local overrides
3185
+
3186
+ The title inherits italic small caps and expanded spacing. The horizontal label resets caps, italic, and both spacing values; the vertical label selects `small-caps` explicitly. Tick labels use compact monospace overrides.
3187
+
3188
+ ```ts
3189
+ import { scatter, type PlotArgs } from '@antadesign/plot'
3190
+ import '@antadesign/plot/elements/a-plot'
3191
+ import type { APlotElement } from '@antadesign/plot/browser'
3192
+
3193
+ function base(): PlotArgs<Node> {
3194
+ return {
3195
+ series: [scatter<Node>({ data: [
3196
+ { x: 12, y: 18 }, { x: 30, y: 42 }, { x: 48, y: 35 },
3197
+ { x: 58, y: 64 }, { x: 76, y: 58 }, { x: 88, y: 82 },
3198
+ ], size: 3, color: { light: '#9ca3af', dark: '#6b7280' }, hoverable: false })],
3199
+ height: 260,
3200
+ margin: { top: 20, right: 24, bottom: 48, left: 76 },
3201
+ axis: { x: { min: 0, max: 100, label: 'Time' }, y: { min: 0, max: 100, label: 'Value' } },
3202
+ background: { light: '#ffffff', dark: '#202124' },
3203
+ chrome_color: { light: '#9ca3af', dark: '#d1d5db' },
3204
+ border: true,
3205
+ grid: false,
3206
+ zoom_pan: false,
3207
+ }
3208
+ }
3209
+
3210
+ function fontOverrides(): PlotArgs<Node> {
3211
+ return {
3212
+ ...base(),
3213
+ margin: { top: 48, right: 20, bottom: 58, left: 72 },
3214
+ font: { family: '"TT Interphases Pro Variable", sans-serif', size: 14, weight: 600,
3215
+ italic: true, caps: true, letter_spacing: 1, word_spacing: 4,
3216
+ color: { light: '#0f766e', dark: '#5eead4' } },
3217
+ title: { text: 'Small caps · +4px words', font: { size: 20 } },
3218
+ axis: {
3219
+ x: { min: 0, max: 100,
3220
+ label: { text: 'Normal caps · zero spacing', font: { caps: false, italic: false,
3221
+ letter_spacing: 0, word_spacing: 0, color: { light: '#c2410c', dark: '#fdba74' } } },
3222
+ tick_label: { font: { family: 'monospace', size: 10, weight: 400, italic: false,
3223
+ caps: false, letter_spacing: 0, word_spacing: 0 } } },
3224
+ y: { min: 0, max: 100,
3225
+ label: { text: 'Italic · small caps', font: { caps: 'small-caps', letter_spacing: 0.5, word_spacing: 0 } },
3226
+ tick_label: { font: { family: 'monospace', size: 10, weight: 400, italic: false,
3227
+ caps: false, letter_spacing: 0, word_spacing: 0 } } },
3228
+ },
3229
+ }
3230
+ }
3231
+
3232
+ await Promise.all([
3233
+ document.fonts.load('750 22px "TT Interphases Pro Variable"'),
3234
+ document.fonts.load('italic 600 20px "TT Interphases Pro Variable"'),
3235
+ ])
3236
+ const plot = document.querySelector<APlotElement>('a-plot')!
3237
+ plot.plotArgs = fontOverrides()
3238
+ ```
3239
+
2883
3240
  ### Title
2884
3241
 
2885
3242
  Set the title text, size, and purple theme colors.
@@ -2908,7 +3265,7 @@ function base(): PlotArgs<Node> {
2908
3265
 
2909
3266
  function title(): PlotArgs<Node> {
2910
3267
  return { ...base(), margin: { top: 44, right: 24, bottom: 48, left: 76 },
2911
- title: { text: 'Plot title', size: 20, color: { light: '#713fff', dark: '#c4b5fd' } } }
3268
+ title: { text: 'Plot title', font: { size: 20, color: { light: '#713fff', dark: '#c4b5fd' } } } }
2912
3269
  }
2913
3270
 
2914
3271
  const plot = document.createElement('a-plot') as APlotElement
@@ -2921,8 +3278,7 @@ document.body.append(plot)
2921
3278
  | Field | Type | Default | Description |
2922
3279
  |---|---|---|---|
2923
3280
  | `text` | `string` | Required | Plot title text. |
2924
- | `size?` | `number` | `14` | Font size in pixels. |
2925
- | `color?` | [`ThemeColor`](#theme-colors) | Theme default | Title color. |
3281
+ | `font?` | `FontArg` | Role default | Text styling; replaces the former `size` and `color` fields. |
2926
3282
 
2927
3283
  ### Margins
2928
3284
 
@@ -3335,8 +3691,8 @@ function base(): PlotArgs<Node> {
3335
3691
 
3336
3692
  function axisLabels(): PlotArgs<Node> {
3337
3693
  return { ...base(), margin: { top: 44, right: 28, bottom: 62, left: 94 }, axis: {
3338
- x: { min: 0, max: 100, label: { text: 'Time', position: 'right', size: 18, color: { light: '#713fff', dark: '#c4b5fd' } } },
3339
- y: { min: 0, max: 100, label: { text: 'Value', position: 'top', size: 12, color: { light: '#c2410c', dark: '#fdba74' } } },
3694
+ x: { min: 0, max: 100, label: { text: 'Time', position: 'right', font: { size: 18, color: { light: '#713fff', dark: '#c4b5fd' } } } },
3695
+ y: { min: 0, max: 100, label: { text: 'Value', position: 'top', font: { size: 12, color: { light: '#c2410c', dark: '#fdba74' } } } },
3340
3696
  } }
3341
3697
  }
3342
3698
 
@@ -3371,10 +3727,10 @@ function base(): PlotArgs<Node> {
3371
3727
 
3372
3728
  function axisLabelsOpposite(): PlotArgs<Node> {
3373
3729
  return { ...base(), margin: { top: 44, right: 28, bottom: 62, left: 94 }, axis: {
3374
- x: { min: 0, max: 100, label: { text: 'Time', position: 'left', size: 12,
3375
- color: { light: '#c2410c', dark: '#fdba74' } } },
3376
- y: { min: 0, max: 100, label: { text: 'Value', position: 'bottom', size: 20,
3377
- color: { light: '#713fff', dark: '#c4b5fd' } } },
3730
+ x: { min: 0, max: 100, label: { text: 'Time', position: 'left', font: { size: 12,
3731
+ color: { light: '#c2410c', dark: '#fdba74' } } } },
3732
+ y: { min: 0, max: 100, label: { text: 'Value', position: 'bottom', font: { size: 20,
3733
+ color: { light: '#713fff', dark: '#c4b5fd' } } } },
3378
3734
  } }
3379
3735
  }
3380
3736
 
@@ -3388,8 +3744,7 @@ document.body.append(plot)
3388
3744
  | Field | Type | Default | Description |
3389
3745
  |---|---|---|---|
3390
3746
  | `text` | `string` | Required | Axis label text. |
3391
- | `size?` | `number` | `12` | Font size in pixels. |
3392
- | `color?` | [`ThemeColor`](#theme-colors) | Theme default | Label color. |
3747
+ | `font?` | `FontArg` | Role default | Text styling; replaces the former `size` and `color` fields. |
3393
3748
  | `position?` | `'center' \| 'left' \| 'right' \| 'top' \| 'bottom'` | `'center'` | Use `left` or `right` on x; `top` or `bottom` on y. |
3394
3749
 
3395
3750
  ### Tick labels
@@ -3420,8 +3775,8 @@ function base(): PlotArgs<Node> {
3420
3775
 
3421
3776
  function tickLabels(): PlotArgs<Node> {
3422
3777
  return { ...base(), margin: { top: 44, right: 28, bottom: 62, left: 94 }, axis: {
3423
- x: { min: 0, max: 100, label: 'Elapsed time', tick_label: { format: value => value + ' s', size: 10, color: { light: '#713fff', dark: '#c4b5fd' } } },
3424
- y: { min: 0, max: 100, label: 'Utilization', tick_label: { format: value => value + '%', size: 15, color: { light: '#c2410c', dark: '#fdba74' } } },
3778
+ x: { min: 0, max: 100, label: 'Elapsed time', tick_label: { format: value => value + ' s', font: { size: 10, color: { light: '#713fff', dark: '#c4b5fd' } } } },
3779
+ y: { min: 0, max: 100, label: 'Utilization', tick_label: { format: value => value + '%', font: { size: 15, color: { light: '#c2410c', dark: '#fdba74' } } } },
3425
3780
  } }
3426
3781
  }
3427
3782
 
@@ -3435,8 +3790,7 @@ Use `TickLabelArg` at `axis.x.tick_label` or `axis.y.tick_label`:
3435
3790
  | Field | Type | Default | Description |
3436
3791
  |---|---|---|---|
3437
3792
  | `format?` | `(value: number \| string, index: number) => string` | Scale-dependent | Format each tick value. Return `''` for an empty label. |
3438
- | `size?` | `number` | `10` | Font size in pixels. |
3439
- | `color?` | [`ThemeColor`](#theme-colors) | Theme default | Tick label color. |
3793
+ | `font?` | `FontArg` | Role default | Text styling; replaces the former `size` and `color` fields. |
3440
3794
 
3441
3795
  ### Series colors
3442
3796
 
@@ -3762,8 +4116,8 @@ grid lines, and the plot border. Use `background` for the plot fill and each
3762
4116
  series’ `color` for its marks. These colors accept `{ light, dark }` pairs.
3763
4117
  Plain color strings remain subject to the plot’s `theme_invert` setting.
3764
4118
 
3765
- Set text colors separately with `title.color`, `axis.x.label.color`,
3766
- `axis.y.label.color`, `axis.x.tick_label.color`, and `axis.y.tick_label.color`.
4119
+ Set text colors separately with `title.font.color`, `axis.x.label.font.color`,
4120
+ `axis.y.label.font.color`, `axis.x.tick_label.font.color`, and `axis.y.tick_label.font.color`.
3767
4121
 
3768
4122
  With the JSX `Plot` component, return content supported by your renderer from a
3769
4123
  series’ [`tooltip`](#tooltips) callback and style it like other markup. Anta’s
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antadesign/anta",
3
- "version": "0.3.31",
3
+ "version": "0.3.33",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -61,7 +61,9 @@
61
61
  "build:docs": "pnpm --filter anta-site run docs:api && pnpm --filter anta-site run docs:pages && node scripts/generate-package-docs.mjs",
62
62
  "audit:contrast": "node scripts/audit-contrast.mjs",
63
63
  "dev": "node scripts/dev.mjs",
64
- "dev:run": "echo $$ > \"$ANTA_DEV_PID_FILE\"; trap 'rm -f \"$ANTA_DEV_PID_FILE\"' EXIT; pnpm run build:dev && pnpm --filter @antadesign/stickers run build:dev && pnpm --filter @antadesign/plot run build:dev && (node scripts/dev-watch.mjs & pnpm --filter anta-site run dev:server & pnpm --filter anta-site run dev:search & wait)",
64
+ "docs-fix": "node scripts/docs-fix.mjs",
65
+ "docs-review": "node scripts/docs-review.mjs",
66
+ "dev:run": "echo $$ > \"$ANTA_DEV_PID_FILE\"; trap 'rm -f \"$ANTA_DEV_PID_FILE\"' EXIT; pnpm run build:dev && pnpm --filter @antadesign/stickers run build:dev && pnpm --filter @antadesign/plot run build:dev && pnpm --filter @antadesign/typedoc-theme run build:dev && (node scripts/dev-watch.mjs & pnpm --filter anta-site run dev:server & (if [ \"$ANTA_DEV_WRANGLER\" = \"1\" ]; then pnpm --filter anta-site run dev:search; fi) & wait)",
65
67
  "stop": "node scripts/dev-stop.mjs",
66
68
  "typecheck": "tsc --noEmit -p src/tsconfig.json",
67
69
  "test": "node --test tests/*.test.mjs",