insomni-plot 0.1.0-alpha.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.
- package/LICENSE.md +674 -0
- package/README.md +81 -0
- package/dist/core.d.mts +340 -0
- package/dist/core.mjs +1047 -0
- package/dist/index.d.mts +3426 -0
- package/dist/index.mjs +12762 -0
- package/dist/interactions-DEFL_F4E.mjs +5395 -0
- package/dist/range-presets-CzECsu3V.d.mts +1523 -0
- package/package.json +34 -0
- package/src/annotations.d.ts +121 -0
- package/src/annotations.ts +438 -0
- package/src/axis.d.ts +184 -0
- package/src/axis.test.ts +131 -0
- package/src/axis.ts +765 -0
- package/src/colorbar.d.ts +69 -0
- package/src/colorbar.ts +294 -0
- package/src/colors.d.ts +57 -0
- package/src/colors.test.ts +28 -0
- package/src/colors.ts +486 -0
- package/src/core.ts +299 -0
- package/src/format.d.ts +54 -0
- package/src/format.ts +138 -0
- package/src/grammar/accessibility.d.ts +147 -0
- package/src/grammar/accessibility.test.ts +199 -0
- package/src/grammar/accessibility.ts +443 -0
- package/src/grammar/aes.d.ts +35 -0
- package/src/grammar/aes.test.ts +75 -0
- package/src/grammar/aes.ts +120 -0
- package/src/grammar/annotations.d.ts +86 -0
- package/src/grammar/annotations.test.ts +68 -0
- package/src/grammar/annotations.ts +336 -0
- package/src/grammar/attach-brush.d.ts +44 -0
- package/src/grammar/attach-brush.test.ts +214 -0
- package/src/grammar/attach-brush.ts +111 -0
- package/src/grammar/attach-presets.d.ts +33 -0
- package/src/grammar/attach-presets.test.ts +106 -0
- package/src/grammar/attach-presets.ts +215 -0
- package/src/grammar/chart.d.ts +952 -0
- package/src/grammar/chart.test.ts +118 -0
- package/src/grammar/chart.ts +1172 -0
- package/src/grammar/color-utils.d.ts +29 -0
- package/src/grammar/color-utils.test.ts +53 -0
- package/src/grammar/color-utils.ts +66 -0
- package/src/grammar/constants.d.ts +45 -0
- package/src/grammar/constants.ts +61 -0
- package/src/grammar/coord.d.ts +183 -0
- package/src/grammar/coord.test.ts +355 -0
- package/src/grammar/coord.ts +619 -0
- package/src/grammar/data/pivot.d.ts +57 -0
- package/src/grammar/data/pivot.ts +107 -0
- package/src/grammar/emphasis-driver.d.ts +69 -0
- package/src/grammar/emphasis-driver.test.ts +199 -0
- package/src/grammar/emphasis-driver.ts +205 -0
- package/src/grammar/equality.d.ts +3 -0
- package/src/grammar/equality.ts +40 -0
- package/src/grammar/facet.d.ts +63 -0
- package/src/grammar/facet.test.ts +60 -0
- package/src/grammar/facet.ts +175 -0
- package/src/grammar/geoms/_categorical.d.ts +94 -0
- package/src/grammar/geoms/_categorical.ts +0 -0
- package/src/grammar/geoms/_distribution.d.ts +52 -0
- package/src/grammar/geoms/_distribution.ts +125 -0
- package/src/grammar/geoms/_mark.d.ts +69 -0
- package/src/grammar/geoms/_mark.ts +136 -0
- package/src/grammar/geoms/_shape.d.ts +41 -0
- package/src/grammar/geoms/_shape.ts +74 -0
- package/src/grammar/geoms/aggregate.d.ts +95 -0
- package/src/grammar/geoms/aggregate.test.ts +554 -0
- package/src/grammar/geoms/aggregate.ts +840 -0
- package/src/grammar/geoms/area.d.ts +32 -0
- package/src/grammar/geoms/area.test.ts +165 -0
- package/src/grammar/geoms/area.ts +578 -0
- package/src/grammar/geoms/band.d.ts +27 -0
- package/src/grammar/geoms/band.test.ts +57 -0
- package/src/grammar/geoms/band.ts +126 -0
- package/src/grammar/geoms/bar.d.ts +56 -0
- package/src/grammar/geoms/bar.test.ts +367 -0
- package/src/grammar/geoms/bar.ts +1054 -0
- package/src/grammar/geoms/boxplot.d.ts +129 -0
- package/src/grammar/geoms/boxplot.test.ts +299 -0
- package/src/grammar/geoms/boxplot.ts +834 -0
- package/src/grammar/geoms/connected-scatter.d.ts +27 -0
- package/src/grammar/geoms/connected-scatter.test.ts +157 -0
- package/src/grammar/geoms/connected-scatter.ts +63 -0
- package/src/grammar/geoms/emphasis.d.ts +76 -0
- package/src/grammar/geoms/emphasis.test.ts +135 -0
- package/src/grammar/geoms/emphasis.ts +162 -0
- package/src/grammar/geoms/histogram.d.ts +75 -0
- package/src/grammar/geoms/histogram.test.ts +262 -0
- package/src/grammar/geoms/histogram.ts +740 -0
- package/src/grammar/geoms/index.d.ts +20 -0
- package/src/grammar/geoms/index.ts +77 -0
- package/src/grammar/geoms/interval.d.ts +31 -0
- package/src/grammar/geoms/interval.test.ts +154 -0
- package/src/grammar/geoms/interval.ts +342 -0
- package/src/grammar/geoms/line.d.ts +38 -0
- package/src/grammar/geoms/line.test.ts +247 -0
- package/src/grammar/geoms/line.ts +659 -0
- package/src/grammar/geoms/point.d.ts +57 -0
- package/src/grammar/geoms/point.test.ts +163 -0
- package/src/grammar/geoms/point.ts +545 -0
- package/src/grammar/geoms/polar.test.ts +216 -0
- package/src/grammar/geoms/ribbon.d.ts +21 -0
- package/src/grammar/geoms/ribbon.test.ts +170 -0
- package/src/grammar/geoms/ribbon.ts +87 -0
- package/src/grammar/geoms/ridgeline.d.ts +89 -0
- package/src/grammar/geoms/ridgeline.test.ts +247 -0
- package/src/grammar/geoms/ridgeline.ts +1164 -0
- package/src/grammar/geoms/rolling.d.ts +43 -0
- package/src/grammar/geoms/rolling.test.ts +217 -0
- package/src/grammar/geoms/rolling.ts +387 -0
- package/src/grammar/geoms/rug.d.ts +28 -0
- package/src/grammar/geoms/rug.test.ts +126 -0
- package/src/grammar/geoms/rug.ts +214 -0
- package/src/grammar/geoms/rule.d.ts +23 -0
- package/src/grammar/geoms/rule.test.ts +69 -0
- package/src/grammar/geoms/rule.ts +212 -0
- package/src/grammar/geoms/smooth.d.ts +54 -0
- package/src/grammar/geoms/smooth.test.ts +78 -0
- package/src/grammar/geoms/smooth.ts +337 -0
- package/src/grammar/geoms/text.d.ts +29 -0
- package/src/grammar/geoms/text.test.ts +64 -0
- package/src/grammar/geoms/text.ts +234 -0
- package/src/grammar/geoms/tile.d.ts +61 -0
- package/src/grammar/geoms/tile.test.ts +157 -0
- package/src/grammar/geoms/tile.ts +621 -0
- package/src/grammar/geoms/types.d.ts +319 -0
- package/src/grammar/geoms/types.ts +362 -0
- package/src/grammar/geoms/violin.d.ts +85 -0
- package/src/grammar/geoms/violin.test.ts +187 -0
- package/src/grammar/geoms/violin.ts +672 -0
- package/src/grammar/index.d.ts +22 -0
- package/src/grammar/index.ts +269 -0
- package/src/grammar/interactions/_disposable.d.ts +5 -0
- package/src/grammar/interactions/_disposable.ts +23 -0
- package/src/grammar/interactions/_z.d.ts +4 -0
- package/src/grammar/interactions/_z.ts +16 -0
- package/src/grammar/interactions/brush-selection.test.ts +262 -0
- package/src/grammar/interactions/brush.d.ts +63 -0
- package/src/grammar/interactions/brush.test.ts +483 -0
- package/src/grammar/interactions/brush.ts +452 -0
- package/src/grammar/interactions/crosshair.d.ts +19 -0
- package/src/grammar/interactions/crosshair.test.ts +127 -0
- package/src/grammar/interactions/crosshair.ts +76 -0
- package/src/grammar/interactions/hit-layer.d.ts +64 -0
- package/src/grammar/interactions/hit-layer.ts +246 -0
- package/src/grammar/interactions/legend.d.ts +19 -0
- package/src/grammar/interactions/legend.ts +101 -0
- package/src/grammar/interactions/menu.d.ts +93 -0
- package/src/grammar/interactions/menu.test.ts +373 -0
- package/src/grammar/interactions/menu.ts +342 -0
- package/src/grammar/interactions/selection.d.ts +25 -0
- package/src/grammar/interactions/selection.test.ts +289 -0
- package/src/grammar/interactions/selection.ts +142 -0
- package/src/grammar/interactions/series-readout.d.ts +91 -0
- package/src/grammar/interactions/series-readout.test.ts +668 -0
- package/src/grammar/interactions/series-readout.ts +422 -0
- package/src/grammar/interactions/series-snap.d.ts +70 -0
- package/src/grammar/interactions/series-snap.test.ts +214 -0
- package/src/grammar/interactions/series-snap.ts +218 -0
- package/src/grammar/interactions/tooltip-axis.test.ts +176 -0
- package/src/grammar/interactions/tooltip-touch.browser.test.ts +49 -0
- package/src/grammar/interactions/tooltip-touch.test.ts +161 -0
- package/src/grammar/interactions/tooltip.d.ts +140 -0
- package/src/grammar/interactions/tooltip.test.ts +406 -0
- package/src/grammar/interactions/tooltip.ts +622 -0
- package/src/grammar/interactions/transitions.d.ts +34 -0
- package/src/grammar/interactions/transitions.test.ts +172 -0
- package/src/grammar/interactions/transitions.ts +160 -0
- package/src/grammar/layout.d.ts +68 -0
- package/src/grammar/layout.ts +186 -0
- package/src/grammar/legend-merge.test.ts +332 -0
- package/src/grammar/mount.d.ts +78 -0
- package/src/grammar/mount.test.ts +479 -0
- package/src/grammar/mount.ts +2112 -0
- package/src/grammar/palettes.d.ts +54 -0
- package/src/grammar/palettes.test.ts +80 -0
- package/src/grammar/palettes.ts +167 -0
- package/src/grammar/pan-zoom.test.ts +398 -0
- package/src/grammar/phylo.d.ts +65 -0
- package/src/grammar/phylo.test.ts +59 -0
- package/src/grammar/phylo.ts +112 -0
- package/src/grammar/pipeline.auto-ticks.test.ts +40 -0
- package/src/grammar/pipeline.d.ts +158 -0
- package/src/grammar/pipeline.test.ts +463 -0
- package/src/grammar/pipeline.ts +1233 -0
- package/src/grammar/profiling.d.ts +8 -0
- package/src/grammar/profiling.ts +24 -0
- package/src/grammar/scales.d.ts +188 -0
- package/src/grammar/scales.test.ts +181 -0
- package/src/grammar/scales.ts +800 -0
- package/src/grammar/svg.d.ts +3 -0
- package/src/grammar/svg.ts +39 -0
- package/src/grammar/theme.d.ts +261 -0
- package/src/grammar/theme.test.ts +105 -0
- package/src/grammar/theme.ts +490 -0
- package/src/heatmap/cpu.ts +109 -0
- package/src/heatmap/gpu.ts +565 -0
- package/src/heatmap/types.ts +177 -0
- package/src/heatmap.browser.test.ts +308 -0
- package/src/heatmap.test.ts +320 -0
- package/src/heatmap.ts +123 -0
- package/src/index.d.ts +1 -0
- package/src/index.ts +8 -0
- package/src/interactions.d.ts +48 -0
- package/src/interactions.test.ts +226 -0
- package/src/interactions.ts +394 -0
- package/src/layout/box.d.ts +48 -0
- package/src/layout/box.test.ts +107 -0
- package/src/layout/box.ts +143 -0
- package/src/legend.d.ts +115 -0
- package/src/legend.ts +422 -0
- package/src/marks/curve.d.ts +43 -0
- package/src/marks/curve.ts +244 -0
- package/src/marks/stack.d.ts +53 -0
- package/src/marks/stack.ts +184 -0
- package/src/marks.d.ts +273 -0
- package/src/marks.test.ts +541 -0
- package/src/marks.ts +1292 -0
- package/src/navigator.test.ts +174 -0
- package/src/navigator.ts +393 -0
- package/src/range-presets.d.ts +113 -0
- package/src/range-presets.test.ts +345 -0
- package/src/range-presets.ts +349 -0
- package/src/scales.d.ts +98 -0
- package/src/scales.test.ts +103 -0
- package/src/scales.ts +695 -0
- package/src/stats/index.d.ts +200 -0
- package/src/stats/index.test.ts +349 -0
- package/src/stats/index.ts +740 -0
- package/src/stats/regression.d.ts +38 -0
- package/src/stats/regression.test.ts +56 -0
- package/src/stats/regression.ts +396 -0
- package/src/stats/rolling-window.d.ts +55 -0
- package/src/stats/rolling-window.test.ts +237 -0
- package/src/stats/rolling-window.ts +256 -0
- package/src/test-setup.ts +19 -0
- package/src/viewport/axis-state.d.ts +72 -0
- package/src/viewport/axis-state.ts +476 -0
- package/src/viewport.d.ts +170 -0
- package/src/viewport.test.ts +363 -0
- package/src/viewport.ts +510 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type BlendSpace, type Color, type GlyphAtlas, type Group } from "insomni";
|
|
2
|
+
import type { ContinuousPalette } from "./colors.ts";
|
|
3
|
+
import type { LegendBuilder } from "./legend.ts";
|
|
4
|
+
export type ColorBarOrientation = "vertical" | "horizontal";
|
|
5
|
+
export type ColorBarLabelSide = "auto" | "before" | "after";
|
|
6
|
+
export interface ColorBarOptions {
|
|
7
|
+
/** Continuous palette to sample. */
|
|
8
|
+
palette: ContinuousPalette;
|
|
9
|
+
/** Numeric domain mapped onto the bar. */
|
|
10
|
+
domain: readonly [number, number];
|
|
11
|
+
/** Default `"vertical"` (chart-right placement). */
|
|
12
|
+
orientation?: ColorBarOrientation;
|
|
13
|
+
/** Long-axis length in pixels. Default `120`. */
|
|
14
|
+
length?: number;
|
|
15
|
+
/** Short-axis thickness in pixels. Default `12`. */
|
|
16
|
+
thickness?: number;
|
|
17
|
+
/** Number of palette stops sampled into rects. Default `64`. */
|
|
18
|
+
steps?: number;
|
|
19
|
+
/** Tick count target. Default `5`. */
|
|
20
|
+
ticks?: number;
|
|
21
|
+
/** Optional explicit tick values (override `ticks`). */
|
|
22
|
+
tickValues?: readonly number[];
|
|
23
|
+
/**
|
|
24
|
+
* Minor ticks between majors — number `N` subdivides each major interval
|
|
25
|
+
* into `N` parts (so `N=2` puts one minor halfway between adjacent majors).
|
|
26
|
+
* Pass an explicit array to place minor ticks at specific values.
|
|
27
|
+
*/
|
|
28
|
+
minorTicks?: number | readonly number[];
|
|
29
|
+
/** Length of minor tick marks. Default `tickSize / 2` from the axis. */
|
|
30
|
+
minorTickSize?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Render a tick label only every `N`th major tick. Default `1`. Use `2`
|
|
33
|
+
* to label every other major, etc. The unlabeled ticks still draw.
|
|
34
|
+
*/
|
|
35
|
+
labelStep?: number;
|
|
36
|
+
/** Tick label formatter. Default uses the underlying linear-scale formatter. */
|
|
37
|
+
format?: (v: number) => string;
|
|
38
|
+
/**
|
|
39
|
+
* Where to draw the tick labels relative to the bar.
|
|
40
|
+
* - `"after"` (default): right of vertical / below horizontal — the
|
|
41
|
+
* conventional placement next to a chart's outer edge.
|
|
42
|
+
* - `"before"`: left of vertical / above horizontal.
|
|
43
|
+
*/
|
|
44
|
+
labelSide?: ColorBarLabelSide;
|
|
45
|
+
/** Optional axis title (typically the color channel's name). */
|
|
46
|
+
title?: string;
|
|
47
|
+
/** Atlas required to render labels/title. Without it the bar is unlabeled. */
|
|
48
|
+
atlas?: GlyphAtlas;
|
|
49
|
+
fontSize?: number;
|
|
50
|
+
labelColor?: Color;
|
|
51
|
+
titleFontSize?: number;
|
|
52
|
+
titleColor?: Color;
|
|
53
|
+
/** Optional outline around the bar rectangle. */
|
|
54
|
+
stroke?: Color;
|
|
55
|
+
strokeWidth?: number;
|
|
56
|
+
group?: Group;
|
|
57
|
+
/**
|
|
58
|
+
* Override the color space used to interpolate the palette stops into
|
|
59
|
+
* the bar's gradient. Falls back to the palette's own `blendSpace`
|
|
60
|
+
* (which the chart pipeline normally sets from `theme.paletteBlendSpace`,
|
|
61
|
+
* default `"oklch"`).
|
|
62
|
+
*/
|
|
63
|
+
blendSpace?: BlendSpace;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Continuous color bar. Returned shape matches `LegendBuilder` so it slots
|
|
67
|
+
* into the same chart-pipeline legend anchor as the categorical legend().
|
|
68
|
+
*/
|
|
69
|
+
export declare function colorBar(options: ColorBarOptions): LegendBuilder;
|
package/src/colorbar.ts
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Continuous color-bar legend
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Mirrors the categorical `legend()` shape (`LegendBuilder`) so the chart
|
|
5
|
+
// pipeline can place either kind into the same legend slot. The bar samples
|
|
6
|
+
// a continuous palette across `domain`, paints it as N stacked rects (so
|
|
7
|
+
// SVG export stays vector-friendly without an embedded raster), and draws
|
|
8
|
+
// ticks/labels via `bottomAxis` / `leftAxis` against a `linearScale`.
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
BLACK,
|
|
12
|
+
type BlendSpace,
|
|
13
|
+
type Color,
|
|
14
|
+
type GlyphAtlas,
|
|
15
|
+
type Group,
|
|
16
|
+
type Layer,
|
|
17
|
+
} from "insomni";
|
|
18
|
+
import { bottomAxis, leftAxis, rightAxis, topAxis } from "./axis.ts";
|
|
19
|
+
import type { ContinuousPalette } from "./colors.ts";
|
|
20
|
+
import { pad, placeable, stack, type Placeable } from "./layout/box.ts";
|
|
21
|
+
import type { LegendBuilder } from "./legend.ts";
|
|
22
|
+
import { linearScale } from "./scales.ts";
|
|
23
|
+
|
|
24
|
+
export type ColorBarOrientation = "vertical" | "horizontal";
|
|
25
|
+
export type ColorBarLabelSide = "auto" | "before" | "after";
|
|
26
|
+
|
|
27
|
+
export interface ColorBarOptions {
|
|
28
|
+
/** Continuous palette to sample. */
|
|
29
|
+
palette: ContinuousPalette;
|
|
30
|
+
/** Numeric domain mapped onto the bar. */
|
|
31
|
+
domain: readonly [number, number];
|
|
32
|
+
/** Default `"vertical"` (chart-right placement). */
|
|
33
|
+
orientation?: ColorBarOrientation;
|
|
34
|
+
/** Long-axis length in pixels. Default `120`. */
|
|
35
|
+
length?: number;
|
|
36
|
+
/** Short-axis thickness in pixels. Default `12`. */
|
|
37
|
+
thickness?: number;
|
|
38
|
+
/** Number of palette stops sampled into rects. Default `64`. */
|
|
39
|
+
steps?: number;
|
|
40
|
+
/** Tick count target. Default `5`. */
|
|
41
|
+
ticks?: number;
|
|
42
|
+
/** Optional explicit tick values (override `ticks`). */
|
|
43
|
+
tickValues?: readonly number[];
|
|
44
|
+
/**
|
|
45
|
+
* Minor ticks between majors — number `N` subdivides each major interval
|
|
46
|
+
* into `N` parts (so `N=2` puts one minor halfway between adjacent majors).
|
|
47
|
+
* Pass an explicit array to place minor ticks at specific values.
|
|
48
|
+
*/
|
|
49
|
+
minorTicks?: number | readonly number[];
|
|
50
|
+
/** Length of minor tick marks. Default `tickSize / 2` from the axis. */
|
|
51
|
+
minorTickSize?: number;
|
|
52
|
+
/**
|
|
53
|
+
* Render a tick label only every `N`th major tick. Default `1`. Use `2`
|
|
54
|
+
* to label every other major, etc. The unlabeled ticks still draw.
|
|
55
|
+
*/
|
|
56
|
+
labelStep?: number;
|
|
57
|
+
/** Tick label formatter. Default uses the underlying linear-scale formatter. */
|
|
58
|
+
format?: (v: number) => string;
|
|
59
|
+
/**
|
|
60
|
+
* Where to draw the tick labels relative to the bar.
|
|
61
|
+
* - `"after"` (default): right of vertical / below horizontal — the
|
|
62
|
+
* conventional placement next to a chart's outer edge.
|
|
63
|
+
* - `"before"`: left of vertical / above horizontal.
|
|
64
|
+
*/
|
|
65
|
+
labelSide?: ColorBarLabelSide;
|
|
66
|
+
/** Optional axis title (typically the color channel's name). */
|
|
67
|
+
title?: string;
|
|
68
|
+
/** Atlas required to render labels/title. Without it the bar is unlabeled. */
|
|
69
|
+
atlas?: GlyphAtlas;
|
|
70
|
+
fontSize?: number;
|
|
71
|
+
labelColor?: Color;
|
|
72
|
+
titleFontSize?: number;
|
|
73
|
+
titleColor?: Color;
|
|
74
|
+
/** Optional outline around the bar rectangle. */
|
|
75
|
+
stroke?: Color;
|
|
76
|
+
strokeWidth?: number;
|
|
77
|
+
group?: Group;
|
|
78
|
+
/**
|
|
79
|
+
* Override the color space used to interpolate the palette stops into
|
|
80
|
+
* the bar's gradient. Falls back to the palette's own `blendSpace`
|
|
81
|
+
* (which the chart pipeline normally sets from `theme.paletteBlendSpace`,
|
|
82
|
+
* default `"oklch"`).
|
|
83
|
+
*/
|
|
84
|
+
blendSpace?: BlendSpace;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const DEFAULT_LENGTH = 120;
|
|
88
|
+
const DEFAULT_THICKNESS = 12;
|
|
89
|
+
const DEFAULT_STEPS = 64;
|
|
90
|
+
const DEFAULT_TICKS = 5;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Continuous color bar. Returned shape matches `LegendBuilder` so it slots
|
|
94
|
+
* into the same chart-pipeline legend anchor as the categorical legend().
|
|
95
|
+
*/
|
|
96
|
+
export function colorBar(options: ColorBarOptions): LegendBuilder {
|
|
97
|
+
const palette =
|
|
98
|
+
options.blendSpace && options.palette.blendSpace !== options.blendSpace
|
|
99
|
+
? options.palette.withBlendSpace(options.blendSpace)
|
|
100
|
+
: options.palette;
|
|
101
|
+
const orientation: ColorBarOrientation = options.orientation ?? "vertical";
|
|
102
|
+
const length = options.length ?? DEFAULT_LENGTH;
|
|
103
|
+
const thickness = options.thickness ?? DEFAULT_THICKNESS;
|
|
104
|
+
const steps = Math.max(2, options.steps ?? DEFAULT_STEPS);
|
|
105
|
+
const fontSize = options.fontSize ?? 11;
|
|
106
|
+
const labelColor = options.labelColor ?? BLACK;
|
|
107
|
+
const titleFontSize = options.titleFontSize ?? fontSize;
|
|
108
|
+
const titleColor = options.titleColor ?? labelColor;
|
|
109
|
+
const labelSide: ColorBarLabelSide = options.labelSide ?? "auto";
|
|
110
|
+
const atlas = options.atlas;
|
|
111
|
+
const title = options.title && options.title.length > 0 ? options.title : undefined;
|
|
112
|
+
|
|
113
|
+
// Resolve which side of the bar the tick labels go on.
|
|
114
|
+
// "auto" defaults to "after" on both axes.
|
|
115
|
+
const before = labelSide === "before";
|
|
116
|
+
|
|
117
|
+
// Underlying tick scale: linear over the bar's pixel length.
|
|
118
|
+
// For vertical bars we want the domain max at the *top*, so we map
|
|
119
|
+
// domain → [length, 0] (low end at the bottom of the bar).
|
|
120
|
+
const tickScale =
|
|
121
|
+
orientation === "vertical"
|
|
122
|
+
? linearScale(options.domain, [length, 0])
|
|
123
|
+
: linearScale(options.domain, [0, length]);
|
|
124
|
+
|
|
125
|
+
// Axis builder (without title — title is rendered as a sibling Placeable
|
|
126
|
+
// below so its width is honestly accounted for in the bbox).
|
|
127
|
+
const axisOptions = {
|
|
128
|
+
atlas,
|
|
129
|
+
ticks: options.ticks ?? DEFAULT_TICKS,
|
|
130
|
+
tickValues: options.tickValues,
|
|
131
|
+
minorTicks: options.minorTicks,
|
|
132
|
+
minorTickSize: options.minorTickSize,
|
|
133
|
+
labelStep: options.labelStep,
|
|
134
|
+
format: options.format,
|
|
135
|
+
labelFontSize: fontSize,
|
|
136
|
+
labelColor,
|
|
137
|
+
tickColor: labelColor,
|
|
138
|
+
axisLineColor: labelColor,
|
|
139
|
+
gridLines: false,
|
|
140
|
+
} as const;
|
|
141
|
+
|
|
142
|
+
const axisBuilder =
|
|
143
|
+
orientation === "vertical"
|
|
144
|
+
? before
|
|
145
|
+
? leftAxis(tickScale, axisOptions)
|
|
146
|
+
: rightAxis(tickScale, axisOptions)
|
|
147
|
+
: before
|
|
148
|
+
? topAxis(tickScale, axisOptions)
|
|
149
|
+
: bottomAxis(tickScale, axisOptions);
|
|
150
|
+
|
|
151
|
+
const measurement = axisBuilder.measure(atlas);
|
|
152
|
+
const labelOverhead = measurement.thickness;
|
|
153
|
+
|
|
154
|
+
// Bar+axis Placeable: the bar rectangle plus its tick axis. No title.
|
|
155
|
+
const barWidth = orientation === "vertical" ? thickness + labelOverhead : length;
|
|
156
|
+
const barHeight = orientation === "vertical" ? length : thickness + labelOverhead;
|
|
157
|
+
|
|
158
|
+
const isVertical = orientation === "vertical";
|
|
159
|
+
|
|
160
|
+
const barWithAxis: Placeable = placeable({ width: barWidth, height: barHeight }, (layer, o) => {
|
|
161
|
+
// Inner top-left of the bar rectangle within this Placeable. The axis
|
|
162
|
+
// labels live on whichever side `before` selects, so the bar is offset
|
|
163
|
+
// by labelOverhead along the *thickness* axis only.
|
|
164
|
+
const barX = isVertical && before ? o.x + labelOverhead : o.x;
|
|
165
|
+
const barY = !isVertical && before ? o.y + labelOverhead : o.y;
|
|
166
|
+
|
|
167
|
+
paintBar({
|
|
168
|
+
layer,
|
|
169
|
+
x: barX,
|
|
170
|
+
y: barY,
|
|
171
|
+
orientation,
|
|
172
|
+
length,
|
|
173
|
+
thickness,
|
|
174
|
+
steps,
|
|
175
|
+
palette,
|
|
176
|
+
group: options.group,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
if (options.stroke) {
|
|
180
|
+
layer.pushRect({
|
|
181
|
+
x: barX,
|
|
182
|
+
y: barY,
|
|
183
|
+
width: isVertical ? thickness : length,
|
|
184
|
+
height: isVertical ? length : thickness,
|
|
185
|
+
stroke: options.stroke,
|
|
186
|
+
strokeWidth: options.strokeWidth ?? 1,
|
|
187
|
+
group: options.group,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Axis hugs the bar's far edge along the thickness axis when `!before`.
|
|
192
|
+
const axisOrigin = {
|
|
193
|
+
x: isVertical && !before ? barX + thickness : barX,
|
|
194
|
+
y: !isVertical && !before ? barY + thickness : barY,
|
|
195
|
+
};
|
|
196
|
+
axisBuilder.addTo(layer, axisOrigin);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// The endpoint tick labels are vertically (or horizontally for horizontal
|
|
200
|
+
// bars) centered on the bar's first/last tick — so they extend `fontSize/2`
|
|
201
|
+
// axially past the bar's length on both ends. Pad the bar Placeable so the
|
|
202
|
+
// bbox includes that overflow; otherwise the top label crashes into a
|
|
203
|
+
// title stacked above (or into the plot below).
|
|
204
|
+
const labelHalf = fontSize / 2;
|
|
205
|
+
const paddedBarWithAxis: Placeable =
|
|
206
|
+
orientation === "vertical"
|
|
207
|
+
? pad(barWithAxis, { top: labelHalf, bottom: labelHalf })
|
|
208
|
+
: pad(barWithAxis, { left: labelHalf, right: labelHalf });
|
|
209
|
+
|
|
210
|
+
// Optional title Placeable, stacked above the bar. Width measured against
|
|
211
|
+
// the atlas so the colorbar's overall bbox includes it — fixing the case
|
|
212
|
+
// where a wide title (e.g. "value") used to overflow into the plot area.
|
|
213
|
+
let composed: Placeable = paddedBarWithAxis;
|
|
214
|
+
if (atlas && title) {
|
|
215
|
+
const titleWidth = atlas.measureText(title, {
|
|
216
|
+
fontSize: titleFontSize,
|
|
217
|
+
simple: true,
|
|
218
|
+
}).width;
|
|
219
|
+
const titleBox: Placeable = placeable(
|
|
220
|
+
{ width: titleWidth, height: titleFontSize },
|
|
221
|
+
(layer, o) => {
|
|
222
|
+
layer.pushText({
|
|
223
|
+
simple: true,
|
|
224
|
+
text: title,
|
|
225
|
+
x: o.x,
|
|
226
|
+
// `pushText.y` is the TOP of the text (matches axis.ts label
|
|
227
|
+
// rendering where `y - fontSize/2` centers on a tick).
|
|
228
|
+
y: o.y,
|
|
229
|
+
fontSize: titleFontSize,
|
|
230
|
+
color: titleColor,
|
|
231
|
+
align: "left",
|
|
232
|
+
group: options.group,
|
|
233
|
+
});
|
|
234
|
+
},
|
|
235
|
+
);
|
|
236
|
+
// Vertical bars: stack title above bar, left-aligned (so the bar stays
|
|
237
|
+
// flush with the legend slot's left edge — i.e. close to the plot).
|
|
238
|
+
// Horizontal bars: same vertical stacking; title sits above.
|
|
239
|
+
const titlePadding = 4;
|
|
240
|
+
composed = stack([titleBox, paddedBarWithAxis], {
|
|
241
|
+
direction: "vertical",
|
|
242
|
+
align: "start",
|
|
243
|
+
gap: titlePadding,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const totalSize = composed.measure();
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
length: steps,
|
|
251
|
+
measure: () => totalSize,
|
|
252
|
+
getEntryBboxes: () => [],
|
|
253
|
+
addTo(layer: Layer, origin) {
|
|
254
|
+
composed.addTo(layer, { x: origin.x, y: origin.y });
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
interface PaintBarArgs {
|
|
260
|
+
layer: Layer;
|
|
261
|
+
x: number;
|
|
262
|
+
y: number;
|
|
263
|
+
orientation: ColorBarOrientation;
|
|
264
|
+
length: number;
|
|
265
|
+
thickness: number;
|
|
266
|
+
steps: number;
|
|
267
|
+
palette: ContinuousPalette;
|
|
268
|
+
group: Group | undefined;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Paint the bar gradient as N stacked rects. SVG export stays vector-only.
|
|
273
|
+
* Cells are sized with a 0.5px overlap on each side so anti-aliased seams
|
|
274
|
+
* don't show up against light backgrounds.
|
|
275
|
+
*/
|
|
276
|
+
function paintBar(a: PaintBarArgs): void {
|
|
277
|
+
const stepLen = a.length / a.steps;
|
|
278
|
+
const vertical = a.orientation === "vertical";
|
|
279
|
+
for (let i = 0; i < a.steps; i++) {
|
|
280
|
+
// For vertical bars the palette runs bottom→top on screen but `t` runs
|
|
281
|
+
// low→high; flip i so top = max.
|
|
282
|
+
const tBase = (i + 0.5) / a.steps;
|
|
283
|
+
const fill = a.palette(vertical ? 1 - tBase : tBase);
|
|
284
|
+
const offset = i * stepLen;
|
|
285
|
+
a.layer.pushRect({
|
|
286
|
+
x: vertical ? a.x : a.x + offset - 0.5,
|
|
287
|
+
y: vertical ? a.y + offset - 0.5 : a.y,
|
|
288
|
+
width: vertical ? a.thickness : stepLen + 1,
|
|
289
|
+
height: vertical ? stepLen + 1 : a.thickness,
|
|
290
|
+
fill,
|
|
291
|
+
group: a.group,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|
package/src/colors.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type BlendSpace, type Color } from "insomni";
|
|
2
|
+
export interface ContinuousPalette {
|
|
3
|
+
(t: number): Color;
|
|
4
|
+
readonly kind: "continuous";
|
|
5
|
+
readonly stops: readonly Color[];
|
|
6
|
+
/** Color space used to interpolate between adjacent stops. */
|
|
7
|
+
readonly blendSpace: BlendSpace;
|
|
8
|
+
/**
|
|
9
|
+
* Return a new palette with the same stops but interpolating in the given
|
|
10
|
+
* space. The original is unchanged. Cheap — palettes are stateless.
|
|
11
|
+
*/
|
|
12
|
+
withBlendSpace(space: BlendSpace): ContinuousPalette;
|
|
13
|
+
}
|
|
14
|
+
export interface CategoricalPalette {
|
|
15
|
+
(index: number): Color;
|
|
16
|
+
readonly kind: "categorical";
|
|
17
|
+
readonly colors: readonly Color[];
|
|
18
|
+
}
|
|
19
|
+
export declare function colorScale(palette: ContinuousPalette, domain: readonly [number, number]): (value: number) => Color;
|
|
20
|
+
export declare function colorScale<Domain>(palette: CategoricalPalette, domain: readonly Domain[]): (value: Domain) => Color;
|
|
21
|
+
export declare const viridis: ContinuousPalette;
|
|
22
|
+
export declare const plasma: ContinuousPalette;
|
|
23
|
+
export declare const inferno: ContinuousPalette;
|
|
24
|
+
export declare const magma: ContinuousPalette;
|
|
25
|
+
export declare const cividis: ContinuousPalette;
|
|
26
|
+
export declare const coolwarm: ContinuousPalette;
|
|
27
|
+
export declare const rdbu: ContinuousPalette;
|
|
28
|
+
export declare const spectral: ContinuousPalette;
|
|
29
|
+
export declare const tableau10: CategoricalPalette;
|
|
30
|
+
export declare const category10: CategoricalPalette;
|
|
31
|
+
export declare const pastel: CategoricalPalette;
|
|
32
|
+
export declare const set1: CategoricalPalette;
|
|
33
|
+
export declare const set2: CategoricalPalette;
|
|
34
|
+
export declare const set3: CategoricalPalette;
|
|
35
|
+
export declare const dark2: CategoricalPalette;
|
|
36
|
+
export declare const accent: CategoricalPalette;
|
|
37
|
+
export declare const paired: CategoricalPalette;
|
|
38
|
+
export declare const blues: ContinuousPalette;
|
|
39
|
+
export declare const greens: ContinuousPalette;
|
|
40
|
+
export declare const oranges: ContinuousPalette;
|
|
41
|
+
export declare const reds: ContinuousPalette;
|
|
42
|
+
export declare const purples: ContinuousPalette;
|
|
43
|
+
export declare const greys: ContinuousPalette;
|
|
44
|
+
export declare const brbg: ContinuousPalette;
|
|
45
|
+
export declare const prgn: ContinuousPalette;
|
|
46
|
+
export declare const piyg: ContinuousPalette;
|
|
47
|
+
export declare const puor: ContinuousPalette;
|
|
48
|
+
/**
|
|
49
|
+
* Build a continuous palette from an arbitrary list of CSS hex stops.
|
|
50
|
+
* Stops are evenly spaced; sample with `palette(t)` where `t ∈ [0, 1]`.
|
|
51
|
+
*/
|
|
52
|
+
export declare function continuousPalette(hexStops: readonly string[]): ContinuousPalette;
|
|
53
|
+
/**
|
|
54
|
+
* Build a categorical palette from an arbitrary list of CSS hex colors.
|
|
55
|
+
* Indices wrap modulo length, so `palette(N)` always returns a valid color.
|
|
56
|
+
*/
|
|
57
|
+
export declare function categoricalPalette(hexColors: readonly string[]): CategoricalPalette;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { describe, expect, test } from "vite-plus/test";
|
|
2
|
+
|
|
3
|
+
import { category10, colorScale, viridis } from "./colors.ts";
|
|
4
|
+
|
|
5
|
+
describe("dataviz colors", () => {
|
|
6
|
+
test("continuous palettes clamp at the ends", () => {
|
|
7
|
+
expect(viridis(-1)).toEqual(viridis.stops[0]);
|
|
8
|
+
expect(viridis(2)).toEqual(viridis.stops[viridis.stops.length - 1]);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test("categorical palettes wrap indices", () => {
|
|
12
|
+
expect(category10(10)).toEqual(category10(0));
|
|
13
|
+
expect(category10(-1)).toEqual(category10(category10.colors.length - 1));
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("colorScale maps numeric domains through continuous palettes", () => {
|
|
17
|
+
const scale = colorScale(viridis, [0, 10]);
|
|
18
|
+
|
|
19
|
+
expect(scale(5)).toEqual(viridis(0.5));
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("colorScale maps categorical domains through categorical palettes", () => {
|
|
23
|
+
const scale = colorScale(category10, ["cpu", "memory", "network"]);
|
|
24
|
+
|
|
25
|
+
expect(scale("memory")).toEqual(category10(1));
|
|
26
|
+
expect(scale("network")).toEqual(category10(2));
|
|
27
|
+
});
|
|
28
|
+
});
|