@voila.dev/chart 4.0.109 → 5.0.111
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/README.md +1 -1
- package/dist/core/marks/tick.d.ts +15 -0
- package/dist/core/marks/tick.d.ts.map +1 -0
- package/dist/core/marks/tick.js +136 -0
- package/dist/core/marks/tick.js.map +7 -0
- package/dist/hierarchy/hierarchy.d.ts.map +1 -1
- package/dist/hierarchy/hierarchy.js +3 -2
- package/dist/hierarchy/hierarchy.js.map +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +2 -2
- package/dist/react/svg-scene.js +1 -1
- package/dist/react/svg-scene.js.map +1 -1
- package/package.json +1 -1
- package/src/core/marks/tick.ts +189 -0
- package/src/hierarchy/hierarchy.ts +5 -2
- package/src/index.ts +1 -0
- package/src/react/svg-scene.tsx +1 -1
- package/src/testing/browser-commands.d.ts +11 -0
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ hit-testing, keyboard focus, the tooltip and the data table read it.
|
|
|
41
41
|
|
|
42
42
|
## Marks
|
|
43
43
|
|
|
44
|
-
Cartesian: `lineY` `lineX` `areaY` `areaX` `barY` `barX` `dot` `cell` `ruleY` `ruleX` `text`
|
|
44
|
+
Cartesian: `lineY` `lineX` `areaY` `areaX` (a band with `from`/`to`) `barY` `barX` `dot` `cell` `ruleY` `ruleX` `tickY` `tickX` `text`
|
|
45
45
|
|
|
46
46
|
Round and frame: `arc` `donut` `radialBar` `radar` `funnel`
|
|
47
47
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ChartMarkOptions } from "./shared.js";
|
|
2
|
+
import type { ChartAccessor, ChartMark, ChartValue } from "../types.js";
|
|
3
|
+
export interface TickOptions<TDatum> extends ChartMarkOptions<TDatum> {
|
|
4
|
+
readonly x?: ChartAccessor<TDatum, ChartValue>;
|
|
5
|
+
readonly y?: ChartAccessor<TDatum, ChartValue>;
|
|
6
|
+
readonly stroke?: string;
|
|
7
|
+
readonly strokeWidth?: number;
|
|
8
|
+
/** The tick's length in pixels where the cross axis is continuous; on a band it spans the band. */
|
|
9
|
+
readonly length?: number;
|
|
10
|
+
}
|
|
11
|
+
/** A short vertical stroke at each x: a strip plot, a rug, one value per category band. */
|
|
12
|
+
export declare function tickX<TDatum>(data: ReadonlyArray<TDatum>, options?: TickOptions<TDatum>): ChartMark;
|
|
13
|
+
/** A short horizontal stroke at each y: a target per bar, a median per category. */
|
|
14
|
+
export declare function tickY<TDatum>(data: ReadonlyArray<TDatum>, options?: TickOptions<TDatum>): ChartMark;
|
|
15
|
+
//# sourceMappingURL=tick.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tick.d.ts","sourceRoot":"","sources":["../../../src/core/marks/tick.ts"],"names":[],"mappings":"AACA,OAAO,EACN,KAAK,gBAAgB,EAOrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EACX,aAAa,EACb,SAAS,EAGT,UAAU,EAEV,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,WAAW,CAAC,MAAM,CAAE,SAAQ,gBAAgB,CAAC,MAAM,CAAC;IACpE,QAAQ,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC/C,QAAQ,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC/C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,mGAAmG;IACnG,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CACzB;AAoJD,2FAA2F;AAC3F,wBAAgB,KAAK,CAAC,MAAM,EAC3B,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,OAAO,GAAE,WAAW,CAAC,MAAM,CAAM,GAC/B,SAAS,CAEX;AAED,oFAAoF;AACpF,wBAAgB,KAAK,CAAC,MAAM,EAC3B,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,OAAO,GAAE,WAAW,CAAC,MAAM,CAAM,GAC/B,SAAS,CAEX"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { channelLabel, isPlaceable, readChannel } from "../channel.js";
|
|
2
|
+
import {
|
|
3
|
+
colorChannel,
|
|
4
|
+
markId,
|
|
5
|
+
paint,
|
|
6
|
+
seriesResolver,
|
|
7
|
+
tips,
|
|
8
|
+
valueOn
|
|
9
|
+
} from "./shared.js";
|
|
10
|
+
const DEFAULT_LENGTH = 12;
|
|
11
|
+
const DEFAULT_STROKE_WIDTH = 2;
|
|
12
|
+
function span(scale, value, length, plot) {
|
|
13
|
+
if (scale === void 0 || value === void 0) {
|
|
14
|
+
return [plot.start, plot.start + plot.size];
|
|
15
|
+
}
|
|
16
|
+
if (scale.kind === "band") {
|
|
17
|
+
const start = scale.map(value);
|
|
18
|
+
return [start, start + scale.bandwidth];
|
|
19
|
+
}
|
|
20
|
+
const center = scale.center(value);
|
|
21
|
+
return [center - length / 2, center + length / 2];
|
|
22
|
+
}
|
|
23
|
+
function tickMark(kind, data, options) {
|
|
24
|
+
const vertical = kind === "tickX";
|
|
25
|
+
const xs = readChannel(data, options.x);
|
|
26
|
+
const ys = readChannel(data, options.y);
|
|
27
|
+
const colors = colorChannel(data, options);
|
|
28
|
+
const valueKey = vertical ? options.x : options.y;
|
|
29
|
+
const crossKey = vertical ? options.y : options.x;
|
|
30
|
+
const values = vertical ? xs : ys;
|
|
31
|
+
const crosses = vertical ? ys : xs;
|
|
32
|
+
return {
|
|
33
|
+
id: options.id,
|
|
34
|
+
coordinate: "cartesian",
|
|
35
|
+
focusOrder: "point",
|
|
36
|
+
channels: {
|
|
37
|
+
[vertical ? "x" : "y"]: {
|
|
38
|
+
values: values.filter(isPlaceable),
|
|
39
|
+
label: channelLabel(valueKey) ?? options.label
|
|
40
|
+
},
|
|
41
|
+
...crossKey === void 0 ? {} : {
|
|
42
|
+
[vertical ? "y" : "x"]: {
|
|
43
|
+
values: crosses.filter(isPlaceable),
|
|
44
|
+
label: channelLabel(crossKey),
|
|
45
|
+
discrete: "band"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
color: colors.channel
|
|
49
|
+
},
|
|
50
|
+
render(context) {
|
|
51
|
+
const id = markId(options.id, kind, context);
|
|
52
|
+
const valueScale = vertical ? context.scales.x : context.scales.y;
|
|
53
|
+
const crossScale = vertical ? context.scales.y : context.scales.x;
|
|
54
|
+
if (valueScale === void 0) {
|
|
55
|
+
return { nodes: [] };
|
|
56
|
+
}
|
|
57
|
+
const { plot } = context;
|
|
58
|
+
const seriesOf = seriesResolver(
|
|
59
|
+
colors.values,
|
|
60
|
+
{ ...options, fixedColor: options.stroke },
|
|
61
|
+
id,
|
|
62
|
+
context
|
|
63
|
+
);
|
|
64
|
+
const length = options.length ?? DEFAULT_LENGTH;
|
|
65
|
+
const strokeWidth = options.strokeWidth ?? DEFAULT_STROKE_WIDTH;
|
|
66
|
+
const nodes = [];
|
|
67
|
+
const points = [];
|
|
68
|
+
for (const [index, datum] of data.entries()) {
|
|
69
|
+
const value = valueOn(valueScale, values[index]);
|
|
70
|
+
if (value === void 0) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const cross = crossKey === void 0 || crossScale === void 0 ? void 0 : valueOn(crossScale, crosses[index]);
|
|
74
|
+
if (crossKey !== void 0 && cross === void 0) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const at = valueScale.center(value);
|
|
78
|
+
const [from, to] = span(
|
|
79
|
+
crossKey === void 0 ? void 0 : crossScale,
|
|
80
|
+
cross,
|
|
81
|
+
length,
|
|
82
|
+
vertical ? { start: plot.y, size: plot.height } : { start: plot.x, size: plot.width }
|
|
83
|
+
);
|
|
84
|
+
const series = seriesOf(index);
|
|
85
|
+
nodes.push({
|
|
86
|
+
kind: "line",
|
|
87
|
+
key: `${id}:${index}`,
|
|
88
|
+
series: series.key,
|
|
89
|
+
role: "mark",
|
|
90
|
+
...vertical ? { x1: at, y1: from, x2: at, y2: to } : { x1: from, y1: at, x2: to, y2: at },
|
|
91
|
+
paint: paint({
|
|
92
|
+
stroke: series.color,
|
|
93
|
+
strokeWidth,
|
|
94
|
+
opacity: options.opacity
|
|
95
|
+
})
|
|
96
|
+
});
|
|
97
|
+
if (tips(options.tip, datum, index)) {
|
|
98
|
+
const middle = (from + to) / 2;
|
|
99
|
+
const xValue = vertical ? value : cross;
|
|
100
|
+
const yValue = vertical ? cross : value;
|
|
101
|
+
points.push({
|
|
102
|
+
key: `${id}:${index}`,
|
|
103
|
+
markId: id,
|
|
104
|
+
index,
|
|
105
|
+
datum,
|
|
106
|
+
x: vertical ? at : middle,
|
|
107
|
+
y: vertical ? middle : at,
|
|
108
|
+
xValue,
|
|
109
|
+
yValue,
|
|
110
|
+
series: series.key,
|
|
111
|
+
seriesLabel: series.label,
|
|
112
|
+
color: series.color,
|
|
113
|
+
title: cross === void 0 ? series.label ?? "" : vertical ? context.formatY(cross) : context.formatX(cross),
|
|
114
|
+
value: vertical ? context.formatX(value) : context.formatY(value),
|
|
115
|
+
hit: {
|
|
116
|
+
kind: "rect",
|
|
117
|
+
rect: vertical ? { x: at - 4, y: from, width: 8, height: to - from } : { x: from, y: at - 4, width: to - from, height: 8 }
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return { nodes, points };
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function tickX(data, options = {}) {
|
|
127
|
+
return tickMark("tickX", data, options);
|
|
128
|
+
}
|
|
129
|
+
function tickY(data, options = {}) {
|
|
130
|
+
return tickMark("tickY", data, options);
|
|
131
|
+
}
|
|
132
|
+
export {
|
|
133
|
+
tickX,
|
|
134
|
+
tickY
|
|
135
|
+
};
|
|
136
|
+
//# sourceMappingURL=tick.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/core/marks/tick.ts"],
|
|
4
|
+
"sourcesContent": ["import { channelLabel, isPlaceable, readChannel } from \"#/core/channel.ts\";\nimport {\n\ttype ChartMarkOptions,\n\tcolorChannel,\n\tmarkId,\n\tpaint,\n\tseriesResolver,\n\ttips,\n\tvalueOn,\n} from \"#/core/marks/shared.ts\";\nimport type {\n\tChartAccessor,\n\tChartMark,\n\tChartPoint,\n\tChartPositionScale,\n\tChartValue,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\nexport interface TickOptions<TDatum> extends ChartMarkOptions<TDatum> {\n\treadonly x?: ChartAccessor<TDatum, ChartValue>;\n\treadonly y?: ChartAccessor<TDatum, ChartValue>;\n\treadonly stroke?: string;\n\treadonly strokeWidth?: number;\n\t/** The tick's length in pixels where the cross axis is continuous; on a band it spans the band. */\n\treadonly length?: number;\n}\n\nconst DEFAULT_LENGTH = 12;\nconst DEFAULT_STROKE_WIDTH = 2;\n\n/** Where the tick spans on the cross axis: the band, or `length` around the value. */\nfunction span(\n\tscale: ChartPositionScale | undefined,\n\tvalue: ChartValue | undefined,\n\tlength: number,\n\tplot: { readonly start: number; readonly size: number },\n): readonly [number, number] {\n\tif (scale === undefined || value === undefined) {\n\t\treturn [plot.start, plot.start + plot.size];\n\t}\n\tif (scale.kind === \"band\") {\n\t\tconst start = scale.map(value);\n\t\treturn [start, start + scale.bandwidth];\n\t}\n\tconst center = scale.center(value);\n\treturn [center - length / 2, center + length / 2];\n}\n\nfunction tickMark<TDatum>(\n\tkind: \"tickX\" | \"tickY\",\n\tdata: ReadonlyArray<TDatum>,\n\toptions: TickOptions<TDatum>,\n): ChartMark {\n\tconst vertical = kind === \"tickX\";\n\tconst xs = readChannel(data, options.x);\n\tconst ys = readChannel(data, options.y);\n\tconst colors = colorChannel(data, options);\n\tconst valueKey = vertical ? options.x : options.y;\n\tconst crossKey = vertical ? options.y : options.x;\n\tconst values = vertical ? xs : ys;\n\tconst crosses = vertical ? ys : xs;\n\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"cartesian\",\n\t\tfocusOrder: \"point\",\n\t\tchannels: {\n\t\t\t[vertical ? \"x\" : \"y\"]: {\n\t\t\t\tvalues: values.filter(isPlaceable),\n\t\t\t\tlabel: channelLabel(valueKey) ?? options.label,\n\t\t\t},\n\t\t\t...(crossKey === undefined\n\t\t\t\t? {}\n\t\t\t\t: {\n\t\t\t\t\t\t[vertical ? \"y\" : \"x\"]: {\n\t\t\t\t\t\t\tvalues: crosses.filter(isPlaceable),\n\t\t\t\t\t\t\tlabel: channelLabel(crossKey),\n\t\t\t\t\t\t\tdiscrete: \"band\" as const,\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\tcolor: colors.channel,\n\t\t},\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, kind, context);\n\t\t\tconst valueScale = vertical ? context.scales.x : context.scales.y;\n\t\t\tconst crossScale = vertical ? context.scales.y : context.scales.x;\n\t\t\tif (valueScale === undefined) {\n\t\t\t\treturn { nodes: [] };\n\t\t\t}\n\t\t\tconst { plot } = context;\n\t\t\tconst seriesOf = seriesResolver(\n\t\t\t\tcolors.values,\n\t\t\t\t{ ...options, fixedColor: options.stroke },\n\t\t\t\tid,\n\t\t\t\tcontext,\n\t\t\t);\n\t\t\tconst length = options.length ?? DEFAULT_LENGTH;\n\t\t\tconst strokeWidth = options.strokeWidth ?? DEFAULT_STROKE_WIDTH;\n\t\t\tconst nodes: SceneNode[] = [];\n\t\t\tconst points: ChartPoint[] = [];\n\t\t\tfor (const [index, datum] of data.entries()) {\n\t\t\t\tconst value = valueOn(valueScale, values[index]);\n\t\t\t\tif (value === undefined) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst cross =\n\t\t\t\t\tcrossKey === undefined || crossScale === undefined\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: valueOn(crossScale, crosses[index]);\n\t\t\t\tif (crossKey !== undefined && cross === undefined) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst at = valueScale.center(value);\n\t\t\t\tconst [from, to] = span(\n\t\t\t\t\tcrossKey === undefined ? undefined : crossScale,\n\t\t\t\t\tcross,\n\t\t\t\t\tlength,\n\t\t\t\t\tvertical\n\t\t\t\t\t\t? { start: plot.y, size: plot.height }\n\t\t\t\t\t\t: { start: plot.x, size: plot.width },\n\t\t\t\t);\n\t\t\t\tconst series = seriesOf(index);\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"line\",\n\t\t\t\t\tkey: `${id}:${index}`,\n\t\t\t\t\tseries: series.key,\n\t\t\t\t\trole: \"mark\",\n\t\t\t\t\t...(vertical\n\t\t\t\t\t\t? { x1: at, y1: from, x2: at, y2: to }\n\t\t\t\t\t\t: { x1: from, y1: at, x2: to, y2: at }),\n\t\t\t\t\tpaint: paint({\n\t\t\t\t\t\tstroke: series.color,\n\t\t\t\t\t\tstrokeWidth,\n\t\t\t\t\t\topacity: options.opacity,\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t\tif (tips(options.tip, datum, index)) {\n\t\t\t\t\tconst middle = (from + to) / 2;\n\t\t\t\t\tconst xValue = vertical ? value : cross;\n\t\t\t\t\tconst yValue = vertical ? cross : value;\n\t\t\t\t\tpoints.push({\n\t\t\t\t\t\tkey: `${id}:${index}`,\n\t\t\t\t\t\tmarkId: id,\n\t\t\t\t\t\tindex,\n\t\t\t\t\t\tdatum,\n\t\t\t\t\t\tx: vertical ? at : middle,\n\t\t\t\t\t\ty: vertical ? middle : at,\n\t\t\t\t\t\txValue,\n\t\t\t\t\t\tyValue,\n\t\t\t\t\t\tseries: series.key,\n\t\t\t\t\t\tseriesLabel: series.label,\n\t\t\t\t\t\tcolor: series.color,\n\t\t\t\t\t\ttitle:\n\t\t\t\t\t\t\tcross === undefined\n\t\t\t\t\t\t\t\t? (series.label ?? \"\")\n\t\t\t\t\t\t\t\t: vertical\n\t\t\t\t\t\t\t\t\t? context.formatY(cross)\n\t\t\t\t\t\t\t\t\t: context.formatX(cross),\n\t\t\t\t\t\tvalue: vertical ? context.formatX(value) : context.formatY(value),\n\t\t\t\t\t\thit: {\n\t\t\t\t\t\t\tkind: \"rect\",\n\t\t\t\t\t\t\trect: vertical\n\t\t\t\t\t\t\t\t? { x: at - 4, y: from, width: 8, height: to - from }\n\t\t\t\t\t\t\t\t: { x: from, y: at - 4, width: to - from, height: 8 },\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { nodes, points };\n\t\t},\n\t};\n}\n\n/** A short vertical stroke at each x: a strip plot, a rug, one value per category band. */\nexport function tickX<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: TickOptions<TDatum> = {},\n): ChartMark {\n\treturn tickMark(\"tickX\", data, options);\n}\n\n/** A short horizontal stroke at each y: a target per bar, a median per category. */\nexport function tickY<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: TickOptions<TDatum> = {},\n): ChartMark {\n\treturn tickMark(\"tickY\", data, options);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hierarchy.d.ts","sourceRoot":"","sources":["../../src/hierarchy/hierarchy.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACX,SAAS,EAIT,MAAM,iBAAiB,CAAC;AAEzB;;;;;GAKG;AAEH,MAAM,WAAW,gBAAgB,CAAC,KAAK;IACtC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,aAAa,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC;IACtE,gDAAgD;IAChD,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,MAAM,CAAC;IACzC,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,MAAM,CAAC;IACvC,kDAAkD;IAClD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;CACvB;AAiGD,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,KAAK,EAChC,IAAI,EAAE,KAAK,EACX,OAAO,EAAE,gBAAgB,CAAC,KAAK,CAAC,GAC9B,SAAS,CA+EX;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,KAAK,EACjC,IAAI,EAAE,KAAK,EACX,OAAO,EAAE,gBAAgB,CAAC,KAAK,CAAC,GAC9B,SAAS,CA8DX;AAKD,iFAAiF;AACjF,wBAAgB,QAAQ,CAAC,KAAK,EAC7B,IAAI,EAAE,KAAK,EACX,OAAO,EAAE,gBAAgB,CAAC,KAAK,CAAC,GAC9B,SAAS,
|
|
1
|
+
{"version":3,"file":"hierarchy.d.ts","sourceRoot":"","sources":["../../src/hierarchy/hierarchy.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACX,SAAS,EAIT,MAAM,iBAAiB,CAAC;AAEzB;;;;;GAKG;AAEH,MAAM,WAAW,gBAAgB,CAAC,KAAK;IACtC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,aAAa,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC;IACtE,gDAAgD;IAChD,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,MAAM,CAAC;IACzC,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,MAAM,CAAC;IACvC,kDAAkD;IAClD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;CACvB;AAiGD,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,KAAK,EAChC,IAAI,EAAE,KAAK,EACX,OAAO,EAAE,gBAAgB,CAAC,KAAK,CAAC,GAC9B,SAAS,CA+EX;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,KAAK,EACjC,IAAI,EAAE,KAAK,EACX,OAAO,EAAE,gBAAgB,CAAC,KAAK,CAAC,GAC9B,SAAS,CA8DX;AAKD,iFAAiF;AACjF,wBAAgB,QAAQ,CAAC,KAAK,EAC7B,IAAI,EAAE,KAAK,EACX,OAAO,EAAE,gBAAgB,CAAC,KAAK,CAAC,GAC9B,SAAS,CAwFX"}
|
|
@@ -206,15 +206,16 @@ function treeMark(root, options) {
|
|
|
206
206
|
0,
|
|
207
207
|
...rootNode.leaves().map((leaf) => measureText(options.name(leaf.data), theme.fontSize))
|
|
208
208
|
);
|
|
209
|
+
const rootLabel = measureText(options.name(rootNode.data), theme.fontSize) + LABEL_GAP;
|
|
209
210
|
const width = Math.max(
|
|
210
211
|
0,
|
|
211
|
-
plot.width - leafLabel - LABEL_GAP - NODE_RADIUS * 2
|
|
212
|
+
plot.width - rootLabel - leafLabel - LABEL_GAP - NODE_RADIUS * 2
|
|
212
213
|
);
|
|
213
214
|
const laid = tree().size([plot.height, width])(
|
|
214
215
|
rootNode
|
|
215
216
|
);
|
|
216
217
|
const position = (node) => ({
|
|
217
|
-
x: plot.x + NODE_RADIUS + (node.y ?? 0),
|
|
218
|
+
x: plot.x + rootLabel + NODE_RADIUS + (node.y ?? 0),
|
|
218
219
|
y: plot.y + (node.x ?? 0)
|
|
219
220
|
});
|
|
220
221
|
const nodes = [];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/hierarchy/hierarchy.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n\ttype HierarchyNode,\n\ttype HierarchyRectangularNode,\n\thierarchy,\n\tpartition,\n\ttree,\n\ttreemap,\n} from \"d3-hierarchy\";\nimport { truncate } from \"#/core/guides/axes.ts\";\nimport { markId } from \"#/core/marks/shared.ts\";\nimport { arcPath, polarToCartesian } from \"#/core/paths.ts\";\nimport type {\n\tChartMark,\n\tChartMarkContext,\n\tChartPoint,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\n/**\n * Nested data: a treemap, a sunburst, a tree. Each takes one root and reads\n * its children, value and name through accessors, so any nested shape works.\n * Colours follow the root's direct children (the branches the legend lists);\n * deeper levels keep their branch's colour.\n */\n\nexport interface HierarchyOptions<TNode> {\n\treadonly id?: string;\n\treadonly children?: (node: TNode) => ReadonlyArray<TNode> | undefined;\n\t/** A leaf's size. Branches sum their leaves. */\n\treadonly value?: (node: TNode) => number;\n\treadonly name: (node: TNode) => string;\n\t/** What the values are, for the table heading. */\n\treadonly label?: string;\n\treadonly tip?: boolean;\n}\n\nfunction rooted<TNode>(\n\troot: TNode,\n\toptions: HierarchyOptions<TNode>,\n): HierarchyNode<TNode> {\n\tconst node = hierarchy(\n\t\troot,\n\t\t(datum) =>\n\t\t\t(options.children?.(datum) as TNode[] | undefined) ??\n\t\t\t(datum as { children?: TNode[] }).children,\n\t);\n\treturn node\n\t\t.sum((datum) => (options.value ? Math.max(0, options.value(datum)) : 0))\n\t\t.sort((left, right) => (right.value ?? 0) - (left.value ?? 0));\n}\n\nfunction branchOf<TNode>(node: HierarchyNode<TNode>): HierarchyNode<TNode> {\n\tlet current = node;\n\twhile (current.parent?.parent) {\n\t\tcurrent = current.parent;\n\t}\n\treturn current;\n}\n\nfunction pathName<TNode>(\n\tnode: HierarchyNode<TNode>,\n\toptions: HierarchyOptions<TNode>,\n): string {\n\treturn node\n\t\t.ancestors()\n\t\t.reverse()\n\t\t.slice(1)\n\t\t.map((ancestor) => options.name(ancestor.data))\n\t\t.join(\" \u203A \");\n}\n\nfunction branchNames<TNode>(\n\troot: TNode,\n\toptions: HierarchyOptions<TNode>,\n): string[] {\n\treturn (rooted(root, options).children ?? []).map((child) =>\n\t\toptions.name(child.data),\n\t);\n}\n\nfunction nodeKey<TNode>(\n\tnode: HierarchyNode<TNode>,\n\toptions: HierarchyOptions<TNode>,\n): string {\n\treturn node\n\t\t.ancestors()\n\t\t.map((ancestor) => options.name(ancestor.data))\n\t\t.reverse()\n\t\t.join(\"/\");\n}\n\nfunction colorFor<TNode>(\n\tnode: HierarchyNode<TNode>,\n\toptions: HierarchyOptions<TNode>,\n\tcontext: ChartMarkContext,\n): string {\n\treturn node.depth === 0\n\t\t? context.theme.muted\n\t\t: context.colorOf(options.name(branchOf(node).data));\n}\n\nfunction pointFor<TNode>(\n\tid: string,\n\tnode: HierarchyNode<TNode>,\n\toptions: HierarchyOptions<TNode>,\n\tcontext: ChartMarkContext,\n\tindex: number,\n\tat: { x: number; y: number },\n\thit?: ChartPoint[\"hit\"],\n): ChartPoint {\n\treturn {\n\t\tkey: `${id}:${nodeKey(node, options)}`,\n\t\tmarkId: id,\n\t\tindex,\n\t\tdatum: node.data,\n\t\tx: at.x,\n\t\ty: at.y,\n\t\txValue: options.name(node.data),\n\t\tyValue: node.value ?? 0,\n\t\tseries: options.name(branchOf(node).data),\n\t\tseriesLabel: options.label,\n\t\tcolor: colorFor(node, options, context),\n\t\ttitle: pathName(node, options) || options.name(node.data),\n\t\tvalue: context.formatY(node.value ?? 0),\n\t\thit,\n\t};\n}\n\nconst LABEL_PADDING = 4;\nconst MIN_LABEL_HEIGHT = 16;\n\n/** Rectangles nested inside rectangles, each leaf as large as its value. */\nexport function treemapMark<TNode>(\n\troot: TNode,\n\toptions: HierarchyOptions<TNode>,\n): ChartMark {\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"frame\",\n\t\tfocusOrder: \"point\",\n\t\ttitles: { value: options.label },\n\t\tchannels: { color: { values: branchNames(root, options) } },\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"treemap\", context);\n\t\t\tconst { plot, theme } = context;\n\t\t\tconst laid = treemap<TNode>()\n\t\t\t\t.size([plot.width, plot.height])\n\t\t\t\t.paddingInner(2)\n\t\t\t\t.round(true)(rooted(root, options));\n\t\t\tconst nodes: SceneNode[] = [];\n\t\t\tconst points: ChartPoint[] = [];\n\t\t\tfor (const [index, leaf] of laid.leaves().entries()) {\n\t\t\t\tconst rect = {\n\t\t\t\t\tx: plot.x + leaf.x0,\n\t\t\t\t\ty: plot.y + leaf.y0,\n\t\t\t\t\twidth: Math.max(0, leaf.x1 - leaf.x0),\n\t\t\t\t\theight: Math.max(0, leaf.y1 - leaf.y0),\n\t\t\t\t};\n\t\t\t\tconst key = nodeKey(leaf, options);\n\t\t\t\tconst series = options.name(branchOf(leaf).data);\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"rect\",\n\t\t\t\t\tkey: `${id}:${key}`,\n\t\t\t\t\tseries,\n\t\t\t\t\trole: \"mark\",\n\t\t\t\t\t...rect,\n\t\t\t\t\tcorners: [3, 3, 3, 3],\n\t\t\t\t\tpaint: {\n\t\t\t\t\t\tfill: colorFor(leaf, options, context),\n\t\t\t\t\t\tfillOpacity: 0.55 + 0.45 / leaf.depth,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tif (\n\t\t\t\t\trect.height >= MIN_LABEL_HEIGHT &&\n\t\t\t\t\trect.width > theme.fontSize * 2\n\t\t\t\t) {\n\t\t\t\t\tnodes.push({\n\t\t\t\t\t\tkind: \"text\",\n\t\t\t\t\t\tkey: `${id}:label:${key}`,\n\t\t\t\t\t\tseries,\n\t\t\t\t\t\trole: \"label\",\n\t\t\t\t\t\tx: rect.x + LABEL_PADDING,\n\t\t\t\t\t\ty: rect.y + LABEL_PADDING,\n\t\t\t\t\t\ttext: truncate(\n\t\t\t\t\t\t\toptions.name(leaf.data),\n\t\t\t\t\t\t\trect.width - LABEL_PADDING * 2,\n\t\t\t\t\t\t\ttheme.fontSize,\n\t\t\t\t\t\t\tcontext.measureText,\n\t\t\t\t\t\t),\n\t\t\t\t\t\tpaint: {\n\t\t\t\t\t\t\tfill: theme.background,\n\t\t\t\t\t\t\tfontSize: theme.fontSize,\n\t\t\t\t\t\t\tfontWeight: 600,\n\t\t\t\t\t\t\tbaseline: \"top\",\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (options.tip !== false) {\n\t\t\t\t\tpoints.push(\n\t\t\t\t\t\tpointFor(\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\tleaf,\n\t\t\t\t\t\t\toptions,\n\t\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t\tindex,\n\t\t\t\t\t\t\t{ x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 },\n\t\t\t\t\t\t\t{ kind: \"rect\", rect },\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { nodes, points };\n\t\t},\n\t};\n}\n\n/** Rings of arcs: the root's children on the first ring, theirs on the next. */\nexport function sunburstMark<TNode>(\n\troot: TNode,\n\toptions: HierarchyOptions<TNode>,\n): ChartMark {\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"frame\",\n\t\tfocusOrder: \"point\",\n\t\ttitles: { value: options.label },\n\t\tchannels: { color: { values: branchNames(root, options) } },\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"sunburst\", context);\n\t\t\tconst { plot } = context;\n\t\t\tconst radius = Math.max(0, Math.min(plot.width, plot.height) / 2 - 4);\n\t\t\tconst cx = plot.x + plot.width / 2;\n\t\t\tconst cy = plot.y + plot.height / 2;\n\t\t\tconst laid = partition<TNode>().size([360, radius])(\n\t\t\t\trooted(root, options),\n\t\t\t);\n\t\t\t// The root's ring would be a disc in the middle: leave it empty.\n\t\t\tconst ringWidth = radius / (laid.height + 1);\n\t\t\tconst nodes: SceneNode[] = [];\n\t\t\tconst points: ChartPoint[] = [];\n\t\t\tconst descendants = laid\n\t\t\t\t.descendants()\n\t\t\t\t.filter((node) => node.depth > 0 && node.x1 - node.x0 > 0.2);\n\t\t\tfor (const [index, node] of descendants.entries()) {\n\t\t\t\tconst shape = {\n\t\t\t\t\tcx,\n\t\t\t\t\tcy,\n\t\t\t\t\tinnerRadius: node.depth * ringWidth,\n\t\t\t\t\touterRadius: (node.depth + 1) * ringWidth - 1,\n\t\t\t\t\tstartAngle: node.x0 + 0.25,\n\t\t\t\t\tendAngle: node.x1 - 0.25,\n\t\t\t\t};\n\t\t\t\tconst series = options.name(branchOf(node).data);\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"path\",\n\t\t\t\t\tkey: `${id}:${nodeKey(node, options)}`,\n\t\t\t\t\tseries,\n\t\t\t\t\trole: \"mark\",\n\t\t\t\t\td: arcPath(shape),\n\t\t\t\t\tpaint: {\n\t\t\t\t\t\tfill: colorFor(node, options, context),\n\t\t\t\t\t\tfillOpacity: 1 - (node.depth - 1) * 0.18,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tif (options.tip !== false) {\n\t\t\t\t\tconst anchor = polarToCartesian(\n\t\t\t\t\t\tcx,\n\t\t\t\t\t\tcy,\n\t\t\t\t\t\t(shape.innerRadius + shape.outerRadius) / 2,\n\t\t\t\t\t\t(shape.startAngle + shape.endAngle) / 2,\n\t\t\t\t\t);\n\t\t\t\t\tpoints.push(\n\t\t\t\t\t\tpointFor(id, node, options, context, index, anchor, {\n\t\t\t\t\t\t\tkind: \"arc\",\n\t\t\t\t\t\t\t...shape,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { nodes, points };\n\t\t},\n\t};\n}\n\nconst NODE_RADIUS = 4;\nconst LABEL_GAP = 6;\n\n/** A node-link tree, root on the left, leaves on the right, every node named. */\nexport function treeMark<TNode>(\n\troot: TNode,\n\toptions: HierarchyOptions<TNode>,\n): ChartMark {\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"frame\",\n\t\tfocusOrder: \"point\",\n\t\ttitles: { value: options.label },\n\t\tchannels: {},\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"tree\", context);\n\t\t\tconst { plot, theme, measureText } = context;\n\t\t\tconst rootNode = rooted(root, options);\n\t\t\tconst leafLabel = Math.max(\n\t\t\t\t0,\n\t\t\t\t...rootNode\n\t\t\t\t\t.leaves()\n\t\t\t\t\t.map((leaf) => measureText(options.name(leaf.data), theme.fontSize)),\n\t\t\t);\n\t\t\tconst width = Math.max(\n\t\t\t\t0,\n\t\t\t\tplot.width - leafLabel - LABEL_GAP - NODE_RADIUS * 2,\n\t\t\t);\n\t\t\tconst laid = tree<TNode>().size([plot.height, width])(\n\t\t\t\trootNode,\n\t\t\t) as HierarchyRectangularNode<TNode> & HierarchyNode<TNode>;\n\t\t\tconst position = (\n\t\t\t\tnode: HierarchyNode<TNode> & { x?: number; y?: number },\n\t\t\t) => ({\n\t\t\t\tx: plot.x + NODE_RADIUS + (node.y ?? 0),\n\t\t\t\ty: plot.y + (node.x ?? 0),\n\t\t\t});\n\t\t\tconst nodes: SceneNode[] = [];\n\t\t\tconst points: ChartPoint[] = [];\n\t\t\tfor (const link of laid.links()) {\n\t\t\t\tconst from = position(link.source);\n\t\t\t\tconst to = position(link.target);\n\t\t\t\tconst middle = (from.x + to.x) / 2;\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"path\",\n\t\t\t\t\tkey: `${id}:link:${nodeKey(link.target, options)}`,\n\t\t\t\t\trole: \"grid\",\n\t\t\t\t\td: `M${from.x},${from.y}C${middle},${from.y} ${middle},${to.y} ${to.x},${to.y}`,\n\t\t\t\t\tpaint: { fill: \"none\", stroke: theme.grid, strokeWidth: 1.5 },\n\t\t\t\t});\n\t\t\t}\n\t\t\tfor (const [index, node] of laid.descendants().entries()) {\n\t\t\t\tconst at = position(node);\n\t\t\t\tconst leaf = node.children === undefined;\n\t\t\t\tnodes.push(\n\t\t\t\t\t{\n\t\t\t\t\t\tkind: \"circle\",\n\t\t\t\t\t\tkey: `${id}:${nodeKey(node, options)}`,\n\t\t\t\t\t\trole: \"mark\",\n\t\t\t\t\t\tcx: at.x,\n\t\t\t\t\t\tcy: at.y,\n\t\t\t\t\t\tr: NODE_RADIUS,\n\t\t\t\t\t\tpaint: {\n\t\t\t\t\t\t\tfill: leaf ? theme.background : colorFor(node, options, context),\n\t\t\t\t\t\t\tstroke: colorFor(node, options, context),\n\t\t\t\t\t\t\tstrokeWidth: 1.5,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tkind: \"text\",\n\t\t\t\t\t\tkey: `${id}:label:${nodeKey(node, options)}`,\n\t\t\t\t\t\trole: \"label\",\n\t\t\t\t\t\tx:\n\t\t\t\t\t\t\tat.x +\n\t\t\t\t\t\t\t(leaf ? LABEL_GAP + NODE_RADIUS : -(LABEL_GAP + NODE_RADIUS)),\n\t\t\t\t\t\ty: at.y,\n\t\t\t\t\t\ttext: options.name(node.data),\n\t\t\t\t\t\tpaint: {\n\t\t\t\t\t\t\tfill: theme.foreground,\n\t\t\t\t\t\t\tfontSize: theme.fontSize,\n\t\t\t\t\t\t\ttextAnchor: leaf ? \"start\" : \"end\",\n\t\t\t\t\t\t\tbaseline: \"middle\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\tif (options.tip !== false) {\n\t\t\t\t\tpoints.push(pointFor(id, node, options, context, index, at));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { nodes, points };\n\t\t},\n\t};\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import {\n\ttype HierarchyNode,\n\ttype HierarchyRectangularNode,\n\thierarchy,\n\tpartition,\n\ttree,\n\ttreemap,\n} from \"d3-hierarchy\";\nimport { truncate } from \"#/core/guides/axes.ts\";\nimport { markId } from \"#/core/marks/shared.ts\";\nimport { arcPath, polarToCartesian } from \"#/core/paths.ts\";\nimport type {\n\tChartMark,\n\tChartMarkContext,\n\tChartPoint,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\n/**\n * Nested data: a treemap, a sunburst, a tree. Each takes one root and reads\n * its children, value and name through accessors, so any nested shape works.\n * Colours follow the root's direct children (the branches the legend lists);\n * deeper levels keep their branch's colour.\n */\n\nexport interface HierarchyOptions<TNode> {\n\treadonly id?: string;\n\treadonly children?: (node: TNode) => ReadonlyArray<TNode> | undefined;\n\t/** A leaf's size. Branches sum their leaves. */\n\treadonly value?: (node: TNode) => number;\n\treadonly name: (node: TNode) => string;\n\t/** What the values are, for the table heading. */\n\treadonly label?: string;\n\treadonly tip?: boolean;\n}\n\nfunction rooted<TNode>(\n\troot: TNode,\n\toptions: HierarchyOptions<TNode>,\n): HierarchyNode<TNode> {\n\tconst node = hierarchy(\n\t\troot,\n\t\t(datum) =>\n\t\t\t(options.children?.(datum) as TNode[] | undefined) ??\n\t\t\t(datum as { children?: TNode[] }).children,\n\t);\n\treturn node\n\t\t.sum((datum) => (options.value ? Math.max(0, options.value(datum)) : 0))\n\t\t.sort((left, right) => (right.value ?? 0) - (left.value ?? 0));\n}\n\nfunction branchOf<TNode>(node: HierarchyNode<TNode>): HierarchyNode<TNode> {\n\tlet current = node;\n\twhile (current.parent?.parent) {\n\t\tcurrent = current.parent;\n\t}\n\treturn current;\n}\n\nfunction pathName<TNode>(\n\tnode: HierarchyNode<TNode>,\n\toptions: HierarchyOptions<TNode>,\n): string {\n\treturn node\n\t\t.ancestors()\n\t\t.reverse()\n\t\t.slice(1)\n\t\t.map((ancestor) => options.name(ancestor.data))\n\t\t.join(\" \u203A \");\n}\n\nfunction branchNames<TNode>(\n\troot: TNode,\n\toptions: HierarchyOptions<TNode>,\n): string[] {\n\treturn (rooted(root, options).children ?? []).map((child) =>\n\t\toptions.name(child.data),\n\t);\n}\n\nfunction nodeKey<TNode>(\n\tnode: HierarchyNode<TNode>,\n\toptions: HierarchyOptions<TNode>,\n): string {\n\treturn node\n\t\t.ancestors()\n\t\t.map((ancestor) => options.name(ancestor.data))\n\t\t.reverse()\n\t\t.join(\"/\");\n}\n\nfunction colorFor<TNode>(\n\tnode: HierarchyNode<TNode>,\n\toptions: HierarchyOptions<TNode>,\n\tcontext: ChartMarkContext,\n): string {\n\treturn node.depth === 0\n\t\t? context.theme.muted\n\t\t: context.colorOf(options.name(branchOf(node).data));\n}\n\nfunction pointFor<TNode>(\n\tid: string,\n\tnode: HierarchyNode<TNode>,\n\toptions: HierarchyOptions<TNode>,\n\tcontext: ChartMarkContext,\n\tindex: number,\n\tat: { x: number; y: number },\n\thit?: ChartPoint[\"hit\"],\n): ChartPoint {\n\treturn {\n\t\tkey: `${id}:${nodeKey(node, options)}`,\n\t\tmarkId: id,\n\t\tindex,\n\t\tdatum: node.data,\n\t\tx: at.x,\n\t\ty: at.y,\n\t\txValue: options.name(node.data),\n\t\tyValue: node.value ?? 0,\n\t\tseries: options.name(branchOf(node).data),\n\t\tseriesLabel: options.label,\n\t\tcolor: colorFor(node, options, context),\n\t\ttitle: pathName(node, options) || options.name(node.data),\n\t\tvalue: context.formatY(node.value ?? 0),\n\t\thit,\n\t};\n}\n\nconst LABEL_PADDING = 4;\nconst MIN_LABEL_HEIGHT = 16;\n\n/** Rectangles nested inside rectangles, each leaf as large as its value. */\nexport function treemapMark<TNode>(\n\troot: TNode,\n\toptions: HierarchyOptions<TNode>,\n): ChartMark {\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"frame\",\n\t\tfocusOrder: \"point\",\n\t\ttitles: { value: options.label },\n\t\tchannels: { color: { values: branchNames(root, options) } },\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"treemap\", context);\n\t\t\tconst { plot, theme } = context;\n\t\t\tconst laid = treemap<TNode>()\n\t\t\t\t.size([plot.width, plot.height])\n\t\t\t\t.paddingInner(2)\n\t\t\t\t.round(true)(rooted(root, options));\n\t\t\tconst nodes: SceneNode[] = [];\n\t\t\tconst points: ChartPoint[] = [];\n\t\t\tfor (const [index, leaf] of laid.leaves().entries()) {\n\t\t\t\tconst rect = {\n\t\t\t\t\tx: plot.x + leaf.x0,\n\t\t\t\t\ty: plot.y + leaf.y0,\n\t\t\t\t\twidth: Math.max(0, leaf.x1 - leaf.x0),\n\t\t\t\t\theight: Math.max(0, leaf.y1 - leaf.y0),\n\t\t\t\t};\n\t\t\t\tconst key = nodeKey(leaf, options);\n\t\t\t\tconst series = options.name(branchOf(leaf).data);\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"rect\",\n\t\t\t\t\tkey: `${id}:${key}`,\n\t\t\t\t\tseries,\n\t\t\t\t\trole: \"mark\",\n\t\t\t\t\t...rect,\n\t\t\t\t\tcorners: [3, 3, 3, 3],\n\t\t\t\t\tpaint: {\n\t\t\t\t\t\tfill: colorFor(leaf, options, context),\n\t\t\t\t\t\tfillOpacity: 0.55 + 0.45 / leaf.depth,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tif (\n\t\t\t\t\trect.height >= MIN_LABEL_HEIGHT &&\n\t\t\t\t\trect.width > theme.fontSize * 2\n\t\t\t\t) {\n\t\t\t\t\tnodes.push({\n\t\t\t\t\t\tkind: \"text\",\n\t\t\t\t\t\tkey: `${id}:label:${key}`,\n\t\t\t\t\t\tseries,\n\t\t\t\t\t\trole: \"label\",\n\t\t\t\t\t\tx: rect.x + LABEL_PADDING,\n\t\t\t\t\t\ty: rect.y + LABEL_PADDING,\n\t\t\t\t\t\ttext: truncate(\n\t\t\t\t\t\t\toptions.name(leaf.data),\n\t\t\t\t\t\t\trect.width - LABEL_PADDING * 2,\n\t\t\t\t\t\t\ttheme.fontSize,\n\t\t\t\t\t\t\tcontext.measureText,\n\t\t\t\t\t\t),\n\t\t\t\t\t\tpaint: {\n\t\t\t\t\t\t\tfill: theme.background,\n\t\t\t\t\t\t\tfontSize: theme.fontSize,\n\t\t\t\t\t\t\tfontWeight: 600,\n\t\t\t\t\t\t\tbaseline: \"top\",\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (options.tip !== false) {\n\t\t\t\t\tpoints.push(\n\t\t\t\t\t\tpointFor(\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\tleaf,\n\t\t\t\t\t\t\toptions,\n\t\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t\tindex,\n\t\t\t\t\t\t\t{ x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 },\n\t\t\t\t\t\t\t{ kind: \"rect\", rect },\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { nodes, points };\n\t\t},\n\t};\n}\n\n/** Rings of arcs: the root's children on the first ring, theirs on the next. */\nexport function sunburstMark<TNode>(\n\troot: TNode,\n\toptions: HierarchyOptions<TNode>,\n): ChartMark {\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"frame\",\n\t\tfocusOrder: \"point\",\n\t\ttitles: { value: options.label },\n\t\tchannels: { color: { values: branchNames(root, options) } },\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"sunburst\", context);\n\t\t\tconst { plot } = context;\n\t\t\tconst radius = Math.max(0, Math.min(plot.width, plot.height) / 2 - 4);\n\t\t\tconst cx = plot.x + plot.width / 2;\n\t\t\tconst cy = plot.y + plot.height / 2;\n\t\t\tconst laid = partition<TNode>().size([360, radius])(\n\t\t\t\trooted(root, options),\n\t\t\t);\n\t\t\t// The root's ring would be a disc in the middle: leave it empty.\n\t\t\tconst ringWidth = radius / (laid.height + 1);\n\t\t\tconst nodes: SceneNode[] = [];\n\t\t\tconst points: ChartPoint[] = [];\n\t\t\tconst descendants = laid\n\t\t\t\t.descendants()\n\t\t\t\t.filter((node) => node.depth > 0 && node.x1 - node.x0 > 0.2);\n\t\t\tfor (const [index, node] of descendants.entries()) {\n\t\t\t\tconst shape = {\n\t\t\t\t\tcx,\n\t\t\t\t\tcy,\n\t\t\t\t\tinnerRadius: node.depth * ringWidth,\n\t\t\t\t\touterRadius: (node.depth + 1) * ringWidth - 1,\n\t\t\t\t\tstartAngle: node.x0 + 0.25,\n\t\t\t\t\tendAngle: node.x1 - 0.25,\n\t\t\t\t};\n\t\t\t\tconst series = options.name(branchOf(node).data);\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"path\",\n\t\t\t\t\tkey: `${id}:${nodeKey(node, options)}`,\n\t\t\t\t\tseries,\n\t\t\t\t\trole: \"mark\",\n\t\t\t\t\td: arcPath(shape),\n\t\t\t\t\tpaint: {\n\t\t\t\t\t\tfill: colorFor(node, options, context),\n\t\t\t\t\t\tfillOpacity: 1 - (node.depth - 1) * 0.18,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tif (options.tip !== false) {\n\t\t\t\t\tconst anchor = polarToCartesian(\n\t\t\t\t\t\tcx,\n\t\t\t\t\t\tcy,\n\t\t\t\t\t\t(shape.innerRadius + shape.outerRadius) / 2,\n\t\t\t\t\t\t(shape.startAngle + shape.endAngle) / 2,\n\t\t\t\t\t);\n\t\t\t\t\tpoints.push(\n\t\t\t\t\t\tpointFor(id, node, options, context, index, anchor, {\n\t\t\t\t\t\t\tkind: \"arc\",\n\t\t\t\t\t\t\t...shape,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { nodes, points };\n\t\t},\n\t};\n}\n\nconst NODE_RADIUS = 4;\nconst LABEL_GAP = 6;\n\n/** A node-link tree, root on the left, leaves on the right, every node named. */\nexport function treeMark<TNode>(\n\troot: TNode,\n\toptions: HierarchyOptions<TNode>,\n): ChartMark {\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"frame\",\n\t\tfocusOrder: \"point\",\n\t\ttitles: { value: options.label },\n\t\tchannels: {},\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"tree\", context);\n\t\t\tconst { plot, theme, measureText } = context;\n\t\t\tconst rootNode = rooted(root, options);\n\t\t\tconst leafLabel = Math.max(\n\t\t\t\t0,\n\t\t\t\t...rootNode\n\t\t\t\t\t.leaves()\n\t\t\t\t\t.map((leaf) => measureText(options.name(leaf.data), theme.fontSize)),\n\t\t\t);\n\t\t\t// The root's label sits left of it, the leaves' right of them: both inside the plot.\n\t\t\tconst rootLabel =\n\t\t\t\tmeasureText(options.name(rootNode.data), theme.fontSize) + LABEL_GAP;\n\t\t\tconst width = Math.max(\n\t\t\t\t0,\n\t\t\t\tplot.width - rootLabel - leafLabel - LABEL_GAP - NODE_RADIUS * 2,\n\t\t\t);\n\t\t\tconst laid = tree<TNode>().size([plot.height, width])(\n\t\t\t\trootNode,\n\t\t\t) as HierarchyRectangularNode<TNode> & HierarchyNode<TNode>;\n\t\t\tconst position = (\n\t\t\t\tnode: HierarchyNode<TNode> & { x?: number; y?: number },\n\t\t\t) => ({\n\t\t\t\tx: plot.x + rootLabel + NODE_RADIUS + (node.y ?? 0),\n\t\t\t\ty: plot.y + (node.x ?? 0),\n\t\t\t});\n\t\t\tconst nodes: SceneNode[] = [];\n\t\t\tconst points: ChartPoint[] = [];\n\t\t\tfor (const link of laid.links()) {\n\t\t\t\tconst from = position(link.source);\n\t\t\t\tconst to = position(link.target);\n\t\t\t\tconst middle = (from.x + to.x) / 2;\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"path\",\n\t\t\t\t\tkey: `${id}:link:${nodeKey(link.target, options)}`,\n\t\t\t\t\trole: \"grid\",\n\t\t\t\t\td: `M${from.x},${from.y}C${middle},${from.y} ${middle},${to.y} ${to.x},${to.y}`,\n\t\t\t\t\tpaint: { fill: \"none\", stroke: theme.grid, strokeWidth: 1.5 },\n\t\t\t\t});\n\t\t\t}\n\t\t\tfor (const [index, node] of laid.descendants().entries()) {\n\t\t\t\tconst at = position(node);\n\t\t\t\tconst leaf = node.children === undefined;\n\t\t\t\tnodes.push(\n\t\t\t\t\t{\n\t\t\t\t\t\tkind: \"circle\",\n\t\t\t\t\t\tkey: `${id}:${nodeKey(node, options)}`,\n\t\t\t\t\t\trole: \"mark\",\n\t\t\t\t\t\tcx: at.x,\n\t\t\t\t\t\tcy: at.y,\n\t\t\t\t\t\tr: NODE_RADIUS,\n\t\t\t\t\t\tpaint: {\n\t\t\t\t\t\t\tfill: leaf ? theme.background : colorFor(node, options, context),\n\t\t\t\t\t\t\tstroke: colorFor(node, options, context),\n\t\t\t\t\t\t\tstrokeWidth: 1.5,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tkind: \"text\",\n\t\t\t\t\t\tkey: `${id}:label:${nodeKey(node, options)}`,\n\t\t\t\t\t\trole: \"label\",\n\t\t\t\t\t\tx:\n\t\t\t\t\t\t\tat.x +\n\t\t\t\t\t\t\t(leaf ? LABEL_GAP + NODE_RADIUS : -(LABEL_GAP + NODE_RADIUS)),\n\t\t\t\t\t\ty: at.y,\n\t\t\t\t\t\ttext: options.name(node.data),\n\t\t\t\t\t\tpaint: {\n\t\t\t\t\t\t\tfill: theme.foreground,\n\t\t\t\t\t\t\tfontSize: theme.fontSize,\n\t\t\t\t\t\t\ttextAnchor: leaf ? \"start\" : \"end\",\n\t\t\t\t\t\t\tbaseline: \"middle\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\tif (options.tip !== false) {\n\t\t\t\t\tpoints.push(pointFor(id, node, options, context, index, at));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { nodes, points };\n\t\t},\n\t};\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export { type RegressionOptions, regressionY, } from "./core/marks/stats/regress
|
|
|
21
21
|
export { boxSummary, kernelDensity, linearFit, quantile, } from "./core/marks/stats/statistics.js";
|
|
22
22
|
export { type WaffleOptions, waffleY } from "./core/marks/stats/waffle.js";
|
|
23
23
|
export { type TextOptions, text } from "./core/marks/text.js";
|
|
24
|
+
export { type TickOptions, tickX, tickY } from "./core/marks/tick.js";
|
|
24
25
|
export { easeOutCubic, tweenScene } from "./core/motion/tween.js";
|
|
25
26
|
export { type ChartFocusStop, findNearest, focusStops, } from "./core/nearest.js";
|
|
26
27
|
export { estimateTextWidth } from "./core/text.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,SAAS,EACd,WAAW,GACX,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,KAAK,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,KAAK,WAAW,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,KAAK,UAAU,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EACN,KAAK,UAAU,EACf,GAAG,EACH,KAAK,EACL,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,EACL,SAAS,GACT,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACtE,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EACN,KAAK,iBAAiB,EACtB,WAAW,GACX,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACN,KAAK,UAAU,EACf,IAAI,EACJ,KAAK,gBAAgB,EACrB,SAAS,EACT,OAAO,GACP,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EACN,KAAK,gBAAgB,EACrB,SAAS,GACT,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACN,KAAK,iBAAiB,EACtB,WAAW,GACX,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACN,UAAU,EACV,aAAa,EACb,SAAS,EACT,QAAQ,GACR,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,KAAK,aAAa,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AAC3E,OAAO,EAAE,KAAK,WAAW,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EACN,KAAK,cAAc,EACnB,WAAW,EACX,UAAU,GACV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,mBAAmB,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,SAAS,EACd,WAAW,GACX,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,KAAK,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,KAAK,WAAW,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,KAAK,UAAU,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EACN,KAAK,UAAU,EACf,GAAG,EACH,KAAK,EACL,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,EACL,SAAS,GACT,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACtE,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EACN,KAAK,iBAAiB,EACtB,WAAW,GACX,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACN,KAAK,UAAU,EACf,IAAI,EACJ,KAAK,gBAAgB,EACrB,SAAS,EACT,OAAO,GACP,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EACN,KAAK,gBAAgB,EACrB,SAAS,GACT,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACN,KAAK,iBAAiB,EACtB,WAAW,GACX,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACN,UAAU,EACV,aAAa,EACb,SAAS,EACT,QAAQ,GACR,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,KAAK,aAAa,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AAC3E,OAAO,EAAE,KAAK,WAAW,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EACN,KAAK,cAAc,EACnB,WAAW,EACX,UAAU,GACV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,mBAAmB,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
} from "./core/marks/stats/statistics.js";
|
|
43
43
|
import { waffleY } from "./core/marks/stats/waffle.js";
|
|
44
44
|
import { text } from "./core/marks/text.js";
|
|
45
|
+
import { tickX, tickY } from "./core/marks/tick.js";
|
|
45
46
|
import { easeOutCubic, tweenScene } from "./core/motion/tween.js";
|
|
46
47
|
import {
|
|
47
48
|
findNearest,
|
|
@@ -89,6 +90,8 @@ export {
|
|
|
89
90
|
ruleX,
|
|
90
91
|
ruleY,
|
|
91
92
|
text,
|
|
93
|
+
tickX,
|
|
94
|
+
tickY,
|
|
92
95
|
tweenScene,
|
|
93
96
|
violinY,
|
|
94
97
|
waffleY
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["export { type CompileOptions, compileChart } from \"#/core/compile.ts\";\nexport {\n\ttype ChartAxisOptions,\n\ttype ChartBuildContext,\n\ttype ChartColorOptions,\n\ttype ChartDefinition,\n\ttype ChartFacet,\n\ttype ChartFacetOptions,\n\ttype ChartPositionScaleOptions,\n\ttype ChartSpec,\n\tdefineChart,\n} from \"#/core/define-chart.ts\";\nexport { facet } from \"#/core/facet.ts\";\nexport { formatDate, formatNumber, formatValue } from \"#/core/format.ts\";\nexport { type AreaOptions, areaX, areaY } from \"#/core/marks/area.ts\";\nexport { type BarOptions, barX, barY } from \"#/core/marks/bar.ts\";\nexport { type CellOptions, cell } from \"#/core/marks/cell.ts\";\nexport { type DotOptions, dot } from \"#/core/marks/dot.ts\";\nexport { type FunnelOptions, funnel } from \"#/core/marks/funnel.ts\";\nexport { type LineOptions, lineX, lineY } from \"#/core/marks/line.ts\";\nexport {\n\ttype ArcOptions,\n\tarc,\n\tdonut,\n\ttype RadarOptions,\n\ttype RadialBarOptions,\n\tradar,\n\tradialBar,\n} from \"#/core/marks/polar.ts\";\nexport { type RectOptions, rectY } from \"#/core/marks/rect.ts\";\nexport { type RuleOptions, ruleX, ruleY } from \"#/core/marks/rule.ts\";\nexport type { ChartMarkOptions } from \"#/core/marks/shared.ts\";\nexport {\n\ttype DifferenceOptions,\n\tdifferenceY,\n} from \"#/core/marks/stats/difference.ts\";\nexport {\n\ttype BoxOptions,\n\tboxY,\n\ttype RidgelineOptions,\n\tridgeline,\n\tviolinY,\n} from \"#/core/marks/stats/distribution.ts\";\nexport { type DodgeOptions, dodgeY } from \"#/core/marks/stats/dodge.ts\";\nexport { type HexbinOptions, hexbin } from \"#/core/marks/stats/hexbin.ts\";\nexport {\n\ttype HistogramOptions,\n\thistogram,\n} from \"#/core/marks/stats/histogram.ts\";\nexport {\n\ttype RegressionOptions,\n\tregressionY,\n} from \"#/core/marks/stats/regression.ts\";\nexport {\n\tboxSummary,\n\tkernelDensity,\n\tlinearFit,\n\tquantile,\n} from \"#/core/marks/stats/statistics.ts\";\nexport { type WaffleOptions, waffleY } from \"#/core/marks/stats/waffle.ts\";\nexport { type TextOptions, text } from \"#/core/marks/text.ts\";\nexport { easeOutCubic, tweenScene } from \"#/core/motion/tween.ts\";\nexport {\n\ttype ChartFocusStop,\n\tfindNearest,\n\tfocusStops,\n} from \"#/core/nearest.ts\";\nexport { estimateTextWidth } from \"#/core/text.ts\";\nexport { DEFAULT_THEME } from \"#/core/theme.ts\";\nexport type * from \"#/core/types.ts\";\n"],
|
|
5
|
-
"mappings": "AAAA,SAA8B,oBAAoB;AAClD;AAAA,EASC;AAAA,OACM;AACP,SAAS,aAAa;AACtB,SAAS,YAAY,cAAc,mBAAmB;AACtD,SAA2B,OAAO,aAAa;AAC/C,SAA0B,MAAM,YAAY;AAC5C,SAA2B,YAAY;AACvC,SAA0B,WAAW;AACrC,SAA6B,cAAc;AAC3C,SAA2B,OAAO,aAAa;AAC/C;AAAA,EAEC;AAAA,EACA;AAAA,EAGA;AAAA,EACA;AAAA,OACM;AACP,SAA2B,aAAa;AACxC,SAA2B,OAAO,aAAa;AAE/C;AAAA,EAEC;AAAA,OACM;AACP;AAAA,EAEC;AAAA,EAEA;AAAA,EACA;AAAA,OACM;AACP,SAA4B,cAAc;AAC1C,SAA6B,cAAc;AAC3C;AAAA,EAEC;AAAA,OACM;AACP;AAAA,EAEC;AAAA,OACM;AACP;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAA6B,eAAe;AAC5C,SAA2B,YAAY;AACvC,SAAS,cAAc,kBAAkB;AACzC;AAAA,EAEC;AAAA,EACA;AAAA,OACM;AACP,SAAS,yBAAyB;AAClC,SAAS,qBAAqB;",
|
|
4
|
+
"sourcesContent": ["export { type CompileOptions, compileChart } from \"#/core/compile.ts\";\nexport {\n\ttype ChartAxisOptions,\n\ttype ChartBuildContext,\n\ttype ChartColorOptions,\n\ttype ChartDefinition,\n\ttype ChartFacet,\n\ttype ChartFacetOptions,\n\ttype ChartPositionScaleOptions,\n\ttype ChartSpec,\n\tdefineChart,\n} from \"#/core/define-chart.ts\";\nexport { facet } from \"#/core/facet.ts\";\nexport { formatDate, formatNumber, formatValue } from \"#/core/format.ts\";\nexport { type AreaOptions, areaX, areaY } from \"#/core/marks/area.ts\";\nexport { type BarOptions, barX, barY } from \"#/core/marks/bar.ts\";\nexport { type CellOptions, cell } from \"#/core/marks/cell.ts\";\nexport { type DotOptions, dot } from \"#/core/marks/dot.ts\";\nexport { type FunnelOptions, funnel } from \"#/core/marks/funnel.ts\";\nexport { type LineOptions, lineX, lineY } from \"#/core/marks/line.ts\";\nexport {\n\ttype ArcOptions,\n\tarc,\n\tdonut,\n\ttype RadarOptions,\n\ttype RadialBarOptions,\n\tradar,\n\tradialBar,\n} from \"#/core/marks/polar.ts\";\nexport { type RectOptions, rectY } from \"#/core/marks/rect.ts\";\nexport { type RuleOptions, ruleX, ruleY } from \"#/core/marks/rule.ts\";\nexport type { ChartMarkOptions } from \"#/core/marks/shared.ts\";\nexport {\n\ttype DifferenceOptions,\n\tdifferenceY,\n} from \"#/core/marks/stats/difference.ts\";\nexport {\n\ttype BoxOptions,\n\tboxY,\n\ttype RidgelineOptions,\n\tridgeline,\n\tviolinY,\n} from \"#/core/marks/stats/distribution.ts\";\nexport { type DodgeOptions, dodgeY } from \"#/core/marks/stats/dodge.ts\";\nexport { type HexbinOptions, hexbin } from \"#/core/marks/stats/hexbin.ts\";\nexport {\n\ttype HistogramOptions,\n\thistogram,\n} from \"#/core/marks/stats/histogram.ts\";\nexport {\n\ttype RegressionOptions,\n\tregressionY,\n} from \"#/core/marks/stats/regression.ts\";\nexport {\n\tboxSummary,\n\tkernelDensity,\n\tlinearFit,\n\tquantile,\n} from \"#/core/marks/stats/statistics.ts\";\nexport { type WaffleOptions, waffleY } from \"#/core/marks/stats/waffle.ts\";\nexport { type TextOptions, text } from \"#/core/marks/text.ts\";\nexport { type TickOptions, tickX, tickY } from \"#/core/marks/tick.ts\";\nexport { easeOutCubic, tweenScene } from \"#/core/motion/tween.ts\";\nexport {\n\ttype ChartFocusStop,\n\tfindNearest,\n\tfocusStops,\n} from \"#/core/nearest.ts\";\nexport { estimateTextWidth } from \"#/core/text.ts\";\nexport { DEFAULT_THEME } from \"#/core/theme.ts\";\nexport type * from \"#/core/types.ts\";\n"],
|
|
5
|
+
"mappings": "AAAA,SAA8B,oBAAoB;AAClD;AAAA,EASC;AAAA,OACM;AACP,SAAS,aAAa;AACtB,SAAS,YAAY,cAAc,mBAAmB;AACtD,SAA2B,OAAO,aAAa;AAC/C,SAA0B,MAAM,YAAY;AAC5C,SAA2B,YAAY;AACvC,SAA0B,WAAW;AACrC,SAA6B,cAAc;AAC3C,SAA2B,OAAO,aAAa;AAC/C;AAAA,EAEC;AAAA,EACA;AAAA,EAGA;AAAA,EACA;AAAA,OACM;AACP,SAA2B,aAAa;AACxC,SAA2B,OAAO,aAAa;AAE/C;AAAA,EAEC;AAAA,OACM;AACP;AAAA,EAEC;AAAA,EAEA;AAAA,EACA;AAAA,OACM;AACP,SAA4B,cAAc;AAC1C,SAA6B,cAAc;AAC3C;AAAA,EAEC;AAAA,OACM;AACP;AAAA,EAEC;AAAA,OACM;AACP;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAA6B,eAAe;AAC5C,SAA2B,YAAY;AACvC,SAA2B,OAAO,aAAa;AAC/C,SAAS,cAAc,kBAAkB;AACzC;AAAA,EAEC;AAAA,EACA;AAAA,OACM;AACP,SAAS,yBAAyB;AAClC,SAAS,qBAAqB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/react/svg-scene.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/react/svg-scene.tsx"],
|
|
4
|
-
"sourcesContent": ["import type * as React from \"react\";\nimport { roundedBarPath } from \"#/core/paths.ts\";\nimport type {\n\tChartPaint,\n\tChartScene,\n\tChartTextPaint,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\nconst BASELINE: Record<\n\tNonNullable<ChartTextPaint[\"baseline\"]>,\n\tReact.SVGAttributes<SVGTextElement>[\"dominantBaseline\"]\n> = {\n\ttop: \"
|
|
4
|
+
"sourcesContent": ["import type * as React from \"react\";\nimport { roundedBarPath } from \"#/core/paths.ts\";\nimport type {\n\tChartPaint,\n\tChartScene,\n\tChartTextPaint,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\nconst BASELINE: Record<\n\tNonNullable<ChartTextPaint[\"baseline\"]>,\n\tReact.SVGAttributes<SVGTextElement>[\"dominantBaseline\"]\n> = {\n\ttop: \"text-before-edge\",\n\tmiddle: \"central\",\n\tbottom: \"text-after-edge\",\n\talphabetic: \"alphabetic\",\n};\n\nconst HATCH_STEP = 6;\n\n/** A stable id per colour (djb2), the same on the server and in the browser. */\nfunction hatchId(prefix: string, color: string): string {\n\tlet hash = 5381;\n\tfor (const character of color) {\n\t\thash = ((hash << 5) + hash + character.charCodeAt(0)) | 0;\n\t}\n\treturn `${prefix}-hatch-${(hash >>> 0).toString(36)}`;\n}\n\nfunction hatchColors(\n\tnodes: ReadonlyArray<SceneNode>,\n\tinto = new Set<string>(),\n): Set<string> {\n\tfor (const node of nodes) {\n\t\tif (node.kind === \"group\") {\n\t\t\thatchColors(node.children, into);\n\t\t} else if (node.paint.hatch && node.paint.fill) {\n\t\t\tinto.add(node.paint.fill);\n\t\t}\n\t}\n\treturn into;\n}\n\nfunction paintProps(\n\tpaint: ChartPaint,\n\tprefix: string,\n): React.SVGAttributes<SVGElement> {\n\treturn {\n\t\tfill:\n\t\t\tpaint.hatch && paint.fill\n\t\t\t\t? `url(#${hatchId(prefix, paint.fill)})`\n\t\t\t\t: (paint.fill ?? \"none\"),\n\t\tstroke: paint.stroke,\n\t\tstrokeWidth: paint.strokeWidth,\n\t\tstrokeDasharray: paint.strokeDasharray,\n\t\tstrokeLinecap: paint.strokeLinecap,\n\t\tstrokeLinejoin: paint.strokeLinejoin,\n\t\topacity: paint.opacity,\n\t\tfillOpacity: paint.fillOpacity,\n\t\tstrokeOpacity: paint.strokeOpacity,\n\t};\n}\n\nfunction SceneElement({\n\tnode,\n\tclipPrefix,\n}: {\n\tnode: SceneNode;\n\tclipPrefix: string;\n}) {\n\tconst data = { \"data-role\": node.role, \"data-series\": node.series };\n\tswitch (node.kind) {\n\t\tcase \"group\": {\n\t\t\tconst clipId = node.clip ? `${clipPrefix}-${node.key}` : undefined;\n\t\t\treturn (\n\t\t\t\t<g\n\t\t\t\t\t{...data}\n\t\t\t\t\ttransform={\n\t\t\t\t\t\tnode.translate\n\t\t\t\t\t\t\t? `translate(${node.translate.x} ${node.translate.y})`\n\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t}\n\t\t\t\t\tclipPath={clipId ? `url(#${clipId})` : undefined}\n\t\t\t\t>\n\t\t\t\t\t{node.clip && clipId ? (\n\t\t\t\t\t\t<clipPath id={clipId}>\n\t\t\t\t\t\t\t<rect {...node.clip} />\n\t\t\t\t\t\t</clipPath>\n\t\t\t\t\t) : null}\n\t\t\t\t\t{node.children.map((child) => (\n\t\t\t\t\t\t<SceneElement\n\t\t\t\t\t\t\tkey={child.key}\n\t\t\t\t\t\t\tnode={child}\n\t\t\t\t\t\t\tclipPrefix={clipPrefix}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))}\n\t\t\t\t</g>\n\t\t\t);\n\t\t}\n\t\tcase \"rect\":\n\t\t\treturn node.corners ? (\n\t\t\t\t<path\n\t\t\t\t\t{...data}\n\t\t\t\t\td={roundedBarPath({ ...node, radius: node.corners })}\n\t\t\t\t\t{...paintProps(node.paint, clipPrefix)}\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<rect\n\t\t\t\t\t{...data}\n\t\t\t\t\tx={node.x}\n\t\t\t\t\ty={node.y}\n\t\t\t\t\twidth={node.width}\n\t\t\t\t\theight={node.height}\n\t\t\t\t\t{...paintProps(node.paint, clipPrefix)}\n\t\t\t\t/>\n\t\t\t);\n\t\tcase \"path\":\n\t\t\treturn (\n\t\t\t\t<path {...data} d={node.d} {...paintProps(node.paint, clipPrefix)} />\n\t\t\t);\n\t\tcase \"circle\":\n\t\t\treturn (\n\t\t\t\t<circle\n\t\t\t\t\t{...data}\n\t\t\t\t\tcx={node.cx}\n\t\t\t\t\tcy={node.cy}\n\t\t\t\t\tr={node.r}\n\t\t\t\t\t{...paintProps(node.paint, clipPrefix)}\n\t\t\t\t/>\n\t\t\t);\n\t\tcase \"line\":\n\t\t\treturn (\n\t\t\t\t<line\n\t\t\t\t\t{...data}\n\t\t\t\t\tx1={node.x1}\n\t\t\t\t\ty1={node.y1}\n\t\t\t\t\tx2={node.x2}\n\t\t\t\t\ty2={node.y2}\n\t\t\t\t\t{...paintProps(node.paint, clipPrefix)}\n\t\t\t\t/>\n\t\t\t);\n\t\tcase \"text\":\n\t\t\treturn (\n\t\t\t\t<text\n\t\t\t\t\t{...data}\n\t\t\t\t\tx={node.x}\n\t\t\t\t\ty={node.y}\n\t\t\t\t\ttransform={\n\t\t\t\t\t\tnode.rotate\n\t\t\t\t\t\t\t? `rotate(${node.rotate} ${node.x} ${node.y})`\n\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t}\n\t\t\t\t\tfontSize={node.paint.fontSize}\n\t\t\t\t\tfontWeight={node.paint.fontWeight}\n\t\t\t\t\ttextAnchor={node.paint.textAnchor}\n\t\t\t\t\tdominantBaseline={\n\t\t\t\t\t\tnode.paint.baseline ? BASELINE[node.paint.baseline] : undefined\n\t\t\t\t\t}\n\t\t\t\t\t{...paintProps(node.paint, clipPrefix)}\n\t\t\t\t>\n\t\t\t\t\t{node.text}\n\t\t\t\t</text>\n\t\t\t);\n\t}\n}\n\n/** The scene's nodes as SVG elements, keyed so React reconciles a data update in place. */\nexport function SvgSceneNodes({\n\tscene,\n\tclipPrefix,\n}: {\n\tscene: ChartScene;\n\tclipPrefix: string;\n}) {\n\tconst hatches = [...hatchColors(scene.nodes)];\n\treturn (\n\t\t<>\n\t\t\t{hatches.length > 0 ? (\n\t\t\t\t<defs>\n\t\t\t\t\t{hatches.map((color) => (\n\t\t\t\t\t\t<pattern\n\t\t\t\t\t\t\tkey={color}\n\t\t\t\t\t\t\tid={hatchId(clipPrefix, color)}\n\t\t\t\t\t\t\twidth={HATCH_STEP}\n\t\t\t\t\t\t\theight={HATCH_STEP}\n\t\t\t\t\t\t\tpatternUnits=\"userSpaceOnUse\"\n\t\t\t\t\t\t\tpatternTransform=\"rotate(45)\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<line\n\t\t\t\t\t\t\t\tx1={0}\n\t\t\t\t\t\t\t\ty1={0}\n\t\t\t\t\t\t\t\tx2={0}\n\t\t\t\t\t\t\t\ty2={HATCH_STEP}\n\t\t\t\t\t\t\t\tstroke={color}\n\t\t\t\t\t\t\t\tstrokeWidth={1.5}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</pattern>\n\t\t\t\t\t))}\n\t\t\t\t</defs>\n\t\t\t) : null}\n\t\t\t{scene.nodes.map((node) => (\n\t\t\t\t<SceneElement key={node.key} node={node} clipPrefix={clipPrefix} />\n\t\t\t))}\n\t\t</>\n\t);\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voila.dev/chart",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.111",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Charts as data: a definition of marks, channels and scales, drawn to SVG or Canvas, keyboard and screen-reader accessible. The core has no dependencies; the heavy layouts (geo, hierarchy, sankey…) sit behind their own subpaths.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { channelLabel, isPlaceable, readChannel } from "#/core/channel.ts";
|
|
2
|
+
import {
|
|
3
|
+
type ChartMarkOptions,
|
|
4
|
+
colorChannel,
|
|
5
|
+
markId,
|
|
6
|
+
paint,
|
|
7
|
+
seriesResolver,
|
|
8
|
+
tips,
|
|
9
|
+
valueOn,
|
|
10
|
+
} from "#/core/marks/shared.ts";
|
|
11
|
+
import type {
|
|
12
|
+
ChartAccessor,
|
|
13
|
+
ChartMark,
|
|
14
|
+
ChartPoint,
|
|
15
|
+
ChartPositionScale,
|
|
16
|
+
ChartValue,
|
|
17
|
+
SceneNode,
|
|
18
|
+
} from "#/core/types.ts";
|
|
19
|
+
|
|
20
|
+
export interface TickOptions<TDatum> extends ChartMarkOptions<TDatum> {
|
|
21
|
+
readonly x?: ChartAccessor<TDatum, ChartValue>;
|
|
22
|
+
readonly y?: ChartAccessor<TDatum, ChartValue>;
|
|
23
|
+
readonly stroke?: string;
|
|
24
|
+
readonly strokeWidth?: number;
|
|
25
|
+
/** The tick's length in pixels where the cross axis is continuous; on a band it spans the band. */
|
|
26
|
+
readonly length?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const DEFAULT_LENGTH = 12;
|
|
30
|
+
const DEFAULT_STROKE_WIDTH = 2;
|
|
31
|
+
|
|
32
|
+
/** Where the tick spans on the cross axis: the band, or `length` around the value. */
|
|
33
|
+
function span(
|
|
34
|
+
scale: ChartPositionScale | undefined,
|
|
35
|
+
value: ChartValue | undefined,
|
|
36
|
+
length: number,
|
|
37
|
+
plot: { readonly start: number; readonly size: number },
|
|
38
|
+
): readonly [number, number] {
|
|
39
|
+
if (scale === undefined || value === undefined) {
|
|
40
|
+
return [plot.start, plot.start + plot.size];
|
|
41
|
+
}
|
|
42
|
+
if (scale.kind === "band") {
|
|
43
|
+
const start = scale.map(value);
|
|
44
|
+
return [start, start + scale.bandwidth];
|
|
45
|
+
}
|
|
46
|
+
const center = scale.center(value);
|
|
47
|
+
return [center - length / 2, center + length / 2];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function tickMark<TDatum>(
|
|
51
|
+
kind: "tickX" | "tickY",
|
|
52
|
+
data: ReadonlyArray<TDatum>,
|
|
53
|
+
options: TickOptions<TDatum>,
|
|
54
|
+
): ChartMark {
|
|
55
|
+
const vertical = kind === "tickX";
|
|
56
|
+
const xs = readChannel(data, options.x);
|
|
57
|
+
const ys = readChannel(data, options.y);
|
|
58
|
+
const colors = colorChannel(data, options);
|
|
59
|
+
const valueKey = vertical ? options.x : options.y;
|
|
60
|
+
const crossKey = vertical ? options.y : options.x;
|
|
61
|
+
const values = vertical ? xs : ys;
|
|
62
|
+
const crosses = vertical ? ys : xs;
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
id: options.id,
|
|
66
|
+
coordinate: "cartesian",
|
|
67
|
+
focusOrder: "point",
|
|
68
|
+
channels: {
|
|
69
|
+
[vertical ? "x" : "y"]: {
|
|
70
|
+
values: values.filter(isPlaceable),
|
|
71
|
+
label: channelLabel(valueKey) ?? options.label,
|
|
72
|
+
},
|
|
73
|
+
...(crossKey === undefined
|
|
74
|
+
? {}
|
|
75
|
+
: {
|
|
76
|
+
[vertical ? "y" : "x"]: {
|
|
77
|
+
values: crosses.filter(isPlaceable),
|
|
78
|
+
label: channelLabel(crossKey),
|
|
79
|
+
discrete: "band" as const,
|
|
80
|
+
},
|
|
81
|
+
}),
|
|
82
|
+
color: colors.channel,
|
|
83
|
+
},
|
|
84
|
+
render(context) {
|
|
85
|
+
const id = markId(options.id, kind, context);
|
|
86
|
+
const valueScale = vertical ? context.scales.x : context.scales.y;
|
|
87
|
+
const crossScale = vertical ? context.scales.y : context.scales.x;
|
|
88
|
+
if (valueScale === undefined) {
|
|
89
|
+
return { nodes: [] };
|
|
90
|
+
}
|
|
91
|
+
const { plot } = context;
|
|
92
|
+
const seriesOf = seriesResolver(
|
|
93
|
+
colors.values,
|
|
94
|
+
{ ...options, fixedColor: options.stroke },
|
|
95
|
+
id,
|
|
96
|
+
context,
|
|
97
|
+
);
|
|
98
|
+
const length = options.length ?? DEFAULT_LENGTH;
|
|
99
|
+
const strokeWidth = options.strokeWidth ?? DEFAULT_STROKE_WIDTH;
|
|
100
|
+
const nodes: SceneNode[] = [];
|
|
101
|
+
const points: ChartPoint[] = [];
|
|
102
|
+
for (const [index, datum] of data.entries()) {
|
|
103
|
+
const value = valueOn(valueScale, values[index]);
|
|
104
|
+
if (value === undefined) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const cross =
|
|
108
|
+
crossKey === undefined || crossScale === undefined
|
|
109
|
+
? undefined
|
|
110
|
+
: valueOn(crossScale, crosses[index]);
|
|
111
|
+
if (crossKey !== undefined && cross === undefined) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const at = valueScale.center(value);
|
|
115
|
+
const [from, to] = span(
|
|
116
|
+
crossKey === undefined ? undefined : crossScale,
|
|
117
|
+
cross,
|
|
118
|
+
length,
|
|
119
|
+
vertical
|
|
120
|
+
? { start: plot.y, size: plot.height }
|
|
121
|
+
: { start: plot.x, size: plot.width },
|
|
122
|
+
);
|
|
123
|
+
const series = seriesOf(index);
|
|
124
|
+
nodes.push({
|
|
125
|
+
kind: "line",
|
|
126
|
+
key: `${id}:${index}`,
|
|
127
|
+
series: series.key,
|
|
128
|
+
role: "mark",
|
|
129
|
+
...(vertical
|
|
130
|
+
? { x1: at, y1: from, x2: at, y2: to }
|
|
131
|
+
: { x1: from, y1: at, x2: to, y2: at }),
|
|
132
|
+
paint: paint({
|
|
133
|
+
stroke: series.color,
|
|
134
|
+
strokeWidth,
|
|
135
|
+
opacity: options.opacity,
|
|
136
|
+
}),
|
|
137
|
+
});
|
|
138
|
+
if (tips(options.tip, datum, index)) {
|
|
139
|
+
const middle = (from + to) / 2;
|
|
140
|
+
const xValue = vertical ? value : cross;
|
|
141
|
+
const yValue = vertical ? cross : value;
|
|
142
|
+
points.push({
|
|
143
|
+
key: `${id}:${index}`,
|
|
144
|
+
markId: id,
|
|
145
|
+
index,
|
|
146
|
+
datum,
|
|
147
|
+
x: vertical ? at : middle,
|
|
148
|
+
y: vertical ? middle : at,
|
|
149
|
+
xValue,
|
|
150
|
+
yValue,
|
|
151
|
+
series: series.key,
|
|
152
|
+
seriesLabel: series.label,
|
|
153
|
+
color: series.color,
|
|
154
|
+
title:
|
|
155
|
+
cross === undefined
|
|
156
|
+
? (series.label ?? "")
|
|
157
|
+
: vertical
|
|
158
|
+
? context.formatY(cross)
|
|
159
|
+
: context.formatX(cross),
|
|
160
|
+
value: vertical ? context.formatX(value) : context.formatY(value),
|
|
161
|
+
hit: {
|
|
162
|
+
kind: "rect",
|
|
163
|
+
rect: vertical
|
|
164
|
+
? { x: at - 4, y: from, width: 8, height: to - from }
|
|
165
|
+
: { x: from, y: at - 4, width: to - from, height: 8 },
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return { nodes, points };
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** A short vertical stroke at each x: a strip plot, a rug, one value per category band. */
|
|
176
|
+
export function tickX<TDatum>(
|
|
177
|
+
data: ReadonlyArray<TDatum>,
|
|
178
|
+
options: TickOptions<TDatum> = {},
|
|
179
|
+
): ChartMark {
|
|
180
|
+
return tickMark("tickX", data, options);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** A short horizontal stroke at each y: a target per bar, a median per category. */
|
|
184
|
+
export function tickY<TDatum>(
|
|
185
|
+
data: ReadonlyArray<TDatum>,
|
|
186
|
+
options: TickOptions<TDatum> = {},
|
|
187
|
+
): ChartMark {
|
|
188
|
+
return tickMark("tickY", data, options);
|
|
189
|
+
}
|
|
@@ -306,9 +306,12 @@ export function treeMark<TNode>(
|
|
|
306
306
|
.leaves()
|
|
307
307
|
.map((leaf) => measureText(options.name(leaf.data), theme.fontSize)),
|
|
308
308
|
);
|
|
309
|
+
// The root's label sits left of it, the leaves' right of them: both inside the plot.
|
|
310
|
+
const rootLabel =
|
|
311
|
+
measureText(options.name(rootNode.data), theme.fontSize) + LABEL_GAP;
|
|
309
312
|
const width = Math.max(
|
|
310
313
|
0,
|
|
311
|
-
plot.width - leafLabel - LABEL_GAP - NODE_RADIUS * 2,
|
|
314
|
+
plot.width - rootLabel - leafLabel - LABEL_GAP - NODE_RADIUS * 2,
|
|
312
315
|
);
|
|
313
316
|
const laid = tree<TNode>().size([plot.height, width])(
|
|
314
317
|
rootNode,
|
|
@@ -316,7 +319,7 @@ export function treeMark<TNode>(
|
|
|
316
319
|
const position = (
|
|
317
320
|
node: HierarchyNode<TNode> & { x?: number; y?: number },
|
|
318
321
|
) => ({
|
|
319
|
-
x: plot.x + NODE_RADIUS + (node.y ?? 0),
|
|
322
|
+
x: plot.x + rootLabel + NODE_RADIUS + (node.y ?? 0),
|
|
320
323
|
y: plot.y + (node.x ?? 0),
|
|
321
324
|
});
|
|
322
325
|
const nodes: SceneNode[] = [];
|
package/src/index.ts
CHANGED
|
@@ -59,6 +59,7 @@ export {
|
|
|
59
59
|
} from "#/core/marks/stats/statistics.ts";
|
|
60
60
|
export { type WaffleOptions, waffleY } from "#/core/marks/stats/waffle.ts";
|
|
61
61
|
export { type TextOptions, text } from "#/core/marks/text.ts";
|
|
62
|
+
export { type TickOptions, tickX, tickY } from "#/core/marks/tick.ts";
|
|
62
63
|
export { easeOutCubic, tweenScene } from "#/core/motion/tween.ts";
|
|
63
64
|
export {
|
|
64
65
|
type ChartFocusStop,
|
package/src/react/svg-scene.tsx
CHANGED
|
@@ -11,7 +11,7 @@ const BASELINE: Record<
|
|
|
11
11
|
NonNullable<ChartTextPaint["baseline"]>,
|
|
12
12
|
React.SVGAttributes<SVGTextElement>["dominantBaseline"]
|
|
13
13
|
> = {
|
|
14
|
-
top: "
|
|
14
|
+
top: "text-before-edge",
|
|
15
15
|
middle: "central",
|
|
16
16
|
bottom: "text-after-edge",
|
|
17
17
|
alphabetic: "alphabetic",
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import "vitest/browser";
|
|
2
|
+
|
|
3
|
+
declare module "vitest/browser" {
|
|
4
|
+
interface BrowserCommands {
|
|
5
|
+
emulateMedia(media: {
|
|
6
|
+
readonly colorScheme?: "light" | "dark" | null;
|
|
7
|
+
readonly forcedColors?: "active" | "none" | null;
|
|
8
|
+
readonly reducedMotion?: "reduce" | "no-preference" | null;
|
|
9
|
+
}): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
}
|