@voila.dev/chart 4.0.107 → 4.0.108
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/dist/canvas/paint-scene.d.ts.map +1 -1
- package/dist/canvas/paint-scene.js +31 -1
- package/dist/canvas/paint-scene.js.map +2 -2
- package/dist/core/marks/area.d.ts.map +1 -1
- package/dist/core/marks/area.js +3 -2
- package/dist/core/marks/area.js.map +2 -2
- package/dist/core/marks/bar.d.ts +5 -0
- package/dist/core/marks/bar.d.ts.map +1 -1
- package/dist/core/marks/bar.js +19 -9
- package/dist/core/marks/bar.js.map +2 -2
- package/dist/core/marks/cell.d.ts.map +1 -1
- package/dist/core/marks/cell.js +2 -1
- package/dist/core/marks/cell.js.map +2 -2
- package/dist/core/marks/dot.d.ts.map +1 -1
- package/dist/core/marks/dot.js +2 -1
- package/dist/core/marks/dot.js.map +2 -2
- package/dist/core/marks/funnel.d.ts.map +1 -1
- package/dist/core/marks/funnel.js +3 -2
- package/dist/core/marks/funnel.js.map +2 -2
- package/dist/core/marks/line.d.ts.map +1 -1
- package/dist/core/marks/line.js +2 -1
- package/dist/core/marks/line.js.map +2 -2
- package/dist/core/marks/polar.d.ts.map +1 -1
- package/dist/core/marks/polar.js +5 -4
- package/dist/core/marks/polar.js.map +2 -2
- package/dist/core/marks/rect.d.ts.map +1 -1
- package/dist/core/marks/rect.js +2 -1
- package/dist/core/marks/rect.js.map +2 -2
- package/dist/core/marks/shared.d.ts +8 -2
- package/dist/core/marks/shared.d.ts.map +1 -1
- package/dist/core/marks/shared.js +4 -0
- package/dist/core/marks/shared.js.map +2 -2
- package/dist/core/marks/stats/dodge.d.ts.map +1 -1
- package/dist/core/marks/stats/dodge.js +2 -1
- package/dist/core/marks/stats/dodge.js.map +2 -2
- package/dist/core/marks/stats/histogram.d.ts.map +1 -1
- package/dist/core/marks/stats/histogram.js +2 -1
- package/dist/core/marks/stats/histogram.js.map +2 -2
- package/dist/core/marks/stats/waffle.d.ts.map +1 -1
- package/dist/core/marks/stats/waffle.js +3 -2
- package/dist/core/marks/stats/waffle.js.map +2 -2
- package/dist/core/types.d.ts +2 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/react/chart-placeholders.d.ts +13 -0
- package/dist/react/chart-placeholders.d.ts.map +1 -0
- package/dist/react/chart-placeholders.js +77 -0
- package/dist/react/chart-placeholders.js.map +7 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +3 -0
- package/dist/react/index.js.map +2 -2
- package/dist/react/svg-scene.d.ts +1 -1
- package/dist/react/svg-scene.d.ts.map +1 -1
- package/dist/react/svg-scene.js +53 -10
- package/dist/react/svg-scene.js.map +2 -2
- package/package.json +1 -1
- package/src/canvas/paint-scene.ts +45 -1
- package/src/core/marks/area.ts +2 -1
- package/src/core/marks/bar.ts +25 -8
- package/src/core/marks/cell.ts +2 -1
- package/src/core/marks/dot.ts +2 -1
- package/src/core/marks/funnel.ts +2 -1
- package/src/core/marks/line.ts +2 -1
- package/src/core/marks/polar.ts +4 -3
- package/src/core/marks/rect.ts +2 -1
- package/src/core/marks/shared.ts +15 -2
- package/src/core/marks/stats/dodge.ts +2 -1
- package/src/core/marks/stats/histogram.ts +2 -1
- package/src/core/marks/stats/waffle.ts +2 -1
- package/src/core/types.ts +2 -0
- package/src/react/chart-placeholders.tsx +93 -0
- package/src/react/index.ts +1 -0
- package/src/react/svg-scene.tsx +72 -11
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paint-scene.d.ts","sourceRoot":"","sources":["../../src/canvas/paint-scene.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAc,UAAU,EAAa,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"paint-scene.d.ts","sourceRoot":"","sources":["../../src/canvas/paint-scene.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAc,UAAU,EAAa,MAAM,iBAAiB,CAAC;AAsLzE,6EAA6E;AAC7E,wBAAgB,UAAU,CACzB,OAAO,EAAE,wBAAwB,EACjC,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,EAClC,UAAU,EAAE,MAAM,QAOlB"}
|
|
@@ -10,6 +10,36 @@ const CANVAS_ALIGN = {
|
|
|
10
10
|
middle: "center",
|
|
11
11
|
end: "right"
|
|
12
12
|
};
|
|
13
|
+
const HATCH_STEP = 6;
|
|
14
|
+
const hatchCache = /* @__PURE__ */ new WeakMap();
|
|
15
|
+
function hatchPattern(context, color) {
|
|
16
|
+
const cache = hatchCache.get(context) ?? /* @__PURE__ */ new Map();
|
|
17
|
+
hatchCache.set(context, cache);
|
|
18
|
+
const cached = cache.get(color);
|
|
19
|
+
if (cached !== void 0) {
|
|
20
|
+
return cached;
|
|
21
|
+
}
|
|
22
|
+
const tile = document.createElement("canvas");
|
|
23
|
+
tile.width = HATCH_STEP;
|
|
24
|
+
tile.height = HATCH_STEP;
|
|
25
|
+
const pen = tile.getContext("2d");
|
|
26
|
+
if (pen === null) {
|
|
27
|
+
return color;
|
|
28
|
+
}
|
|
29
|
+
pen.strokeStyle = color;
|
|
30
|
+
pen.lineWidth = 1.5;
|
|
31
|
+
pen.beginPath();
|
|
32
|
+
pen.moveTo(0, HATCH_STEP);
|
|
33
|
+
pen.lineTo(HATCH_STEP, 0);
|
|
34
|
+
pen.moveTo(-1, 1);
|
|
35
|
+
pen.lineTo(1, -1);
|
|
36
|
+
pen.moveTo(HATCH_STEP - 1, HATCH_STEP + 1);
|
|
37
|
+
pen.lineTo(HATCH_STEP + 1, HATCH_STEP - 1);
|
|
38
|
+
pen.stroke();
|
|
39
|
+
const pattern = context.createPattern(tile, "repeat") ?? color;
|
|
40
|
+
cache.set(color, pattern);
|
|
41
|
+
return pattern;
|
|
42
|
+
}
|
|
13
43
|
function dashes(dasharray) {
|
|
14
44
|
if (!dasharray) {
|
|
15
45
|
return [];
|
|
@@ -21,7 +51,7 @@ function fillAndStroke(environment, paint, draw) {
|
|
|
21
51
|
const opacity = paint.opacity ?? 1;
|
|
22
52
|
if (paint.fill && paint.fill !== "none") {
|
|
23
53
|
context.globalAlpha = opacity * (paint.fillOpacity ?? 1);
|
|
24
|
-
context.fillStyle = resolve(paint.fill);
|
|
54
|
+
context.fillStyle = paint.hatch ? hatchPattern(context, resolve(paint.fill)) : resolve(paint.fill);
|
|
25
55
|
draw("fill");
|
|
26
56
|
}
|
|
27
57
|
if (paint.stroke && paint.stroke !== "none") {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/canvas/paint-scene.ts"],
|
|
4
|
-
"sourcesContent": ["import { roundedBarPath } from \"#/core/paths.ts\";\nimport type { ChartPaint, ChartScene, SceneNode } from \"#/core/types.ts\";\n\nconst CANVAS_BASELINE = {\n\ttop: \"top\",\n\tmiddle: \"middle\",\n\tbottom: \"bottom\",\n\talphabetic: \"alphabetic\",\n} as const satisfies Record<string, CanvasTextBaseline>;\n\nconst CANVAS_ALIGN = {\n\tstart: \"left\",\n\tmiddle: \"center\",\n\tend: \"right\",\n} as const satisfies Record<string, CanvasTextAlign>;\n\nfunction dashes(dasharray: string | undefined): number[] {\n\tif (!dasharray) {\n\t\treturn [];\n\t}\n\treturn dasharray\n\t\t.split(/[\\s,]+/)\n\t\t.map(Number)\n\t\t.filter((value) => Number.isFinite(value));\n}\n\ninterface PaintEnvironment {\n\treadonly context: CanvasRenderingContext2D;\n\treadonly resolve: (color: string) => string;\n\treadonly fontFamily: string;\n}\n\nfunction fillAndStroke(\n\tenvironment: PaintEnvironment,\n\tpaint: ChartPaint,\n\tdraw: (mode: \"fill\" | \"stroke\") => void,\n) {\n\tconst { context, resolve } = environment;\n\tconst opacity = paint.opacity ?? 1;\n\tif (paint.fill && paint.fill !== \"none\") {\n\t\tcontext.globalAlpha = opacity * (paint.fillOpacity ?? 1);\n\t\tcontext.fillStyle = resolve(paint.fill);\n\t\tdraw(\"fill\");\n\t}\n\tif (paint.stroke && paint.stroke !== \"none\") {\n\t\tcontext.globalAlpha = opacity * (paint.strokeOpacity ?? 1);\n\t\tcontext.strokeStyle = resolve(paint.stroke);\n\t\tcontext.lineWidth = paint.strokeWidth ?? 1;\n\t\tcontext.lineCap = paint.strokeLinecap ?? \"butt\";\n\t\tcontext.lineJoin = paint.strokeLinejoin ?? \"miter\";\n\t\tcontext.setLineDash(dashes(paint.strokeDasharray));\n\t\tdraw(\"stroke\");\n\t\tcontext.setLineDash([]);\n\t}\n\tcontext.globalAlpha = 1;\n}\n\nfunction paintNode(environment: PaintEnvironment, node: SceneNode) {\n\tconst { context } = environment;\n\tswitch (node.kind) {\n\t\tcase \"group\":\n\t\t\tcontext.save();\n\t\t\tif (node.translate) {\n\t\t\t\tcontext.translate(node.translate.x, node.translate.y);\n\t\t\t}\n\t\t\tif (node.clip) {\n\t\t\t\tcontext.beginPath();\n\t\t\t\tcontext.rect(\n\t\t\t\t\tnode.clip.x,\n\t\t\t\t\tnode.clip.y,\n\t\t\t\t\tnode.clip.width,\n\t\t\t\t\tnode.clip.height,\n\t\t\t\t);\n\t\t\t\tcontext.clip();\n\t\t\t}\n\t\t\tfor (const child of node.children) {\n\t\t\t\tpaintNode(environment, child);\n\t\t\t}\n\t\t\tcontext.restore();\n\t\t\treturn;\n\t\tcase \"rect\": {\n\t\t\tconst path = node.corners\n\t\t\t\t? new Path2D(roundedBarPath({ ...node, radius: node.corners }))\n\t\t\t\t: undefined;\n\t\t\tfillAndStroke(environment, node.paint, (mode) => {\n\t\t\t\tif (path) {\n\t\t\t\t\tcontext[mode](path);\n\t\t\t\t} else if (mode === \"fill\") {\n\t\t\t\t\tcontext.fillRect(node.x, node.y, node.width, node.height);\n\t\t\t\t} else {\n\t\t\t\t\tcontext.strokeRect(node.x, node.y, node.width, node.height);\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tcase \"path\": {\n\t\t\tif (node.d === \"\") {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst path = new Path2D(node.d);\n\t\t\tfillAndStroke(environment, node.paint, (mode) => context[mode](path));\n\t\t\treturn;\n\t\t}\n\t\tcase \"circle\": {\n\t\t\tconst path = new Path2D();\n\t\t\tpath.arc(node.cx, node.cy, node.r, 0, Math.PI * 2);\n\t\t\tfillAndStroke(environment, node.paint, (mode) => context[mode](path));\n\t\t\treturn;\n\t\t}\n\t\tcase \"line\": {\n\t\t\tconst path = new Path2D();\n\t\t\tpath.moveTo(node.x1, node.y1);\n\t\t\tpath.lineTo(node.x2, node.y2);\n\t\t\tfillAndStroke(environment, { ...node.paint, fill: undefined }, () =>\n\t\t\t\tcontext.stroke(path),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tcase \"text\": {\n\t\t\tconst { paint } = node;\n\t\t\tcontext.save();\n\t\t\tcontext.translate(node.x, node.y);\n\t\t\tif (node.rotate) {\n\t\t\t\tcontext.rotate((node.rotate * Math.PI) / 180);\n\t\t\t}\n\t\t\tcontext.font = `${paint.fontWeight ?? 400} ${paint.fontSize ?? 12}px ${environment.fontFamily}`;\n\t\t\tcontext.textAlign = CANVAS_ALIGN[paint.textAnchor ?? \"start\"];\n\t\t\tcontext.textBaseline = CANVAS_BASELINE[paint.baseline ?? \"alphabetic\"];\n\t\t\tfillAndStroke(environment, paint, (mode) =>\n\t\t\t\tmode === \"fill\"\n\t\t\t\t\t? context.fillText(node.text, 0, 0)\n\t\t\t\t\t: context.strokeText(node.text, 0, 0),\n\t\t\t);\n\t\t\tcontext.restore();\n\t\t\treturn;\n\t\t}\n\t}\n}\n\n/** Paints the whole scene. The canvas is already scaled to device pixels. */\nexport function paintScene(\n\tcontext: CanvasRenderingContext2D,\n\tscene: ChartScene,\n\tresolve: (color: string) => string,\n\tfontFamily: string,\n) {\n\tcontext.clearRect(0, 0, scene.width, scene.height);\n\tconst environment = { context, resolve, fontFamily };\n\tfor (const node of scene.nodes) {\n\t\tpaintNode(environment, node);\n\t}\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,sBAAsB;AAG/B,MAAM,kBAAkB;AAAA,EACvB,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AACb;AAEA,MAAM,eAAe;AAAA,EACpB,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,KAAK;AACN;AAEA,SAAS,OAAO,WAAyC;AACxD,MAAI,CAAC,WAAW;AACf,WAAO,CAAC;AAAA,EACT;AACA,SAAO,UACL,MAAM,QAAQ,EACd,IAAI,MAAM,EACV,OAAO,CAAC,UAAU,OAAO,SAAS,KAAK,CAAC;AAC3C;AAQA,SAAS,cACR,aACA,OACA,MACC;AACD,QAAM,EAAE,SAAS,QAAQ,IAAI;AAC7B,QAAM,UAAU,MAAM,WAAW;AACjC,MAAI,MAAM,QAAQ,MAAM,SAAS,QAAQ;AACxC,YAAQ,cAAc,WAAW,MAAM,eAAe;AACtD,YAAQ,YAAY,QAAQ,MAAM,IAAI;
|
|
4
|
+
"sourcesContent": ["import { roundedBarPath } from \"#/core/paths.ts\";\nimport type { ChartPaint, ChartScene, SceneNode } from \"#/core/types.ts\";\n\nconst CANVAS_BASELINE = {\n\ttop: \"top\",\n\tmiddle: \"middle\",\n\tbottom: \"bottom\",\n\talphabetic: \"alphabetic\",\n} as const satisfies Record<string, CanvasTextBaseline>;\n\nconst CANVAS_ALIGN = {\n\tstart: \"left\",\n\tmiddle: \"center\",\n\tend: \"right\",\n} as const satisfies Record<string, CanvasTextAlign>;\n\nconst HATCH_STEP = 6;\nconst hatchCache = new WeakMap<\n\tCanvasRenderingContext2D,\n\tMap<string, CanvasPattern | string>\n>();\n\n/** Diagonal lines every few pixels in the colour, as the SVG pattern draws them. */\nfunction hatchPattern(\n\tcontext: CanvasRenderingContext2D,\n\tcolor: string,\n): CanvasPattern | string {\n\tconst cache =\n\t\thatchCache.get(context) ?? new Map<string, CanvasPattern | string>();\n\thatchCache.set(context, cache);\n\tconst cached = cache.get(color);\n\tif (cached !== undefined) {\n\t\treturn cached;\n\t}\n\tconst tile = document.createElement(\"canvas\");\n\ttile.width = HATCH_STEP;\n\ttile.height = HATCH_STEP;\n\tconst pen = tile.getContext(\"2d\");\n\tif (pen === null) {\n\t\treturn color;\n\t}\n\tpen.strokeStyle = color;\n\tpen.lineWidth = 1.5;\n\tpen.beginPath();\n\t// One diagonal across the tile, plus the two corner stubs that join it to\n\t// the neighbouring tiles' lines.\n\tpen.moveTo(0, HATCH_STEP);\n\tpen.lineTo(HATCH_STEP, 0);\n\tpen.moveTo(-1, 1);\n\tpen.lineTo(1, -1);\n\tpen.moveTo(HATCH_STEP - 1, HATCH_STEP + 1);\n\tpen.lineTo(HATCH_STEP + 1, HATCH_STEP - 1);\n\tpen.stroke();\n\tconst pattern = context.createPattern(tile, \"repeat\") ?? color;\n\tcache.set(color, pattern);\n\treturn pattern;\n}\n\nfunction dashes(dasharray: string | undefined): number[] {\n\tif (!dasharray) {\n\t\treturn [];\n\t}\n\treturn dasharray\n\t\t.split(/[\\s,]+/)\n\t\t.map(Number)\n\t\t.filter((value) => Number.isFinite(value));\n}\n\ninterface PaintEnvironment {\n\treadonly context: CanvasRenderingContext2D;\n\treadonly resolve: (color: string) => string;\n\treadonly fontFamily: string;\n}\n\nfunction fillAndStroke(\n\tenvironment: PaintEnvironment,\n\tpaint: ChartPaint,\n\tdraw: (mode: \"fill\" | \"stroke\") => void,\n) {\n\tconst { context, resolve } = environment;\n\tconst opacity = paint.opacity ?? 1;\n\tif (paint.fill && paint.fill !== \"none\") {\n\t\tcontext.globalAlpha = opacity * (paint.fillOpacity ?? 1);\n\t\tcontext.fillStyle = paint.hatch\n\t\t\t? hatchPattern(context, resolve(paint.fill))\n\t\t\t: resolve(paint.fill);\n\t\tdraw(\"fill\");\n\t}\n\tif (paint.stroke && paint.stroke !== \"none\") {\n\t\tcontext.globalAlpha = opacity * (paint.strokeOpacity ?? 1);\n\t\tcontext.strokeStyle = resolve(paint.stroke);\n\t\tcontext.lineWidth = paint.strokeWidth ?? 1;\n\t\tcontext.lineCap = paint.strokeLinecap ?? \"butt\";\n\t\tcontext.lineJoin = paint.strokeLinejoin ?? \"miter\";\n\t\tcontext.setLineDash(dashes(paint.strokeDasharray));\n\t\tdraw(\"stroke\");\n\t\tcontext.setLineDash([]);\n\t}\n\tcontext.globalAlpha = 1;\n}\n\nfunction paintNode(environment: PaintEnvironment, node: SceneNode) {\n\tconst { context } = environment;\n\tswitch (node.kind) {\n\t\tcase \"group\":\n\t\t\tcontext.save();\n\t\t\tif (node.translate) {\n\t\t\t\tcontext.translate(node.translate.x, node.translate.y);\n\t\t\t}\n\t\t\tif (node.clip) {\n\t\t\t\tcontext.beginPath();\n\t\t\t\tcontext.rect(\n\t\t\t\t\tnode.clip.x,\n\t\t\t\t\tnode.clip.y,\n\t\t\t\t\tnode.clip.width,\n\t\t\t\t\tnode.clip.height,\n\t\t\t\t);\n\t\t\t\tcontext.clip();\n\t\t\t}\n\t\t\tfor (const child of node.children) {\n\t\t\t\tpaintNode(environment, child);\n\t\t\t}\n\t\t\tcontext.restore();\n\t\t\treturn;\n\t\tcase \"rect\": {\n\t\t\tconst path = node.corners\n\t\t\t\t? new Path2D(roundedBarPath({ ...node, radius: node.corners }))\n\t\t\t\t: undefined;\n\t\t\tfillAndStroke(environment, node.paint, (mode) => {\n\t\t\t\tif (path) {\n\t\t\t\t\tcontext[mode](path);\n\t\t\t\t} else if (mode === \"fill\") {\n\t\t\t\t\tcontext.fillRect(node.x, node.y, node.width, node.height);\n\t\t\t\t} else {\n\t\t\t\t\tcontext.strokeRect(node.x, node.y, node.width, node.height);\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tcase \"path\": {\n\t\t\tif (node.d === \"\") {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst path = new Path2D(node.d);\n\t\t\tfillAndStroke(environment, node.paint, (mode) => context[mode](path));\n\t\t\treturn;\n\t\t}\n\t\tcase \"circle\": {\n\t\t\tconst path = new Path2D();\n\t\t\tpath.arc(node.cx, node.cy, node.r, 0, Math.PI * 2);\n\t\t\tfillAndStroke(environment, node.paint, (mode) => context[mode](path));\n\t\t\treturn;\n\t\t}\n\t\tcase \"line\": {\n\t\t\tconst path = new Path2D();\n\t\t\tpath.moveTo(node.x1, node.y1);\n\t\t\tpath.lineTo(node.x2, node.y2);\n\t\t\tfillAndStroke(environment, { ...node.paint, fill: undefined }, () =>\n\t\t\t\tcontext.stroke(path),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tcase \"text\": {\n\t\t\tconst { paint } = node;\n\t\t\tcontext.save();\n\t\t\tcontext.translate(node.x, node.y);\n\t\t\tif (node.rotate) {\n\t\t\t\tcontext.rotate((node.rotate * Math.PI) / 180);\n\t\t\t}\n\t\t\tcontext.font = `${paint.fontWeight ?? 400} ${paint.fontSize ?? 12}px ${environment.fontFamily}`;\n\t\t\tcontext.textAlign = CANVAS_ALIGN[paint.textAnchor ?? \"start\"];\n\t\t\tcontext.textBaseline = CANVAS_BASELINE[paint.baseline ?? \"alphabetic\"];\n\t\t\tfillAndStroke(environment, paint, (mode) =>\n\t\t\t\tmode === \"fill\"\n\t\t\t\t\t? context.fillText(node.text, 0, 0)\n\t\t\t\t\t: context.strokeText(node.text, 0, 0),\n\t\t\t);\n\t\t\tcontext.restore();\n\t\t\treturn;\n\t\t}\n\t}\n}\n\n/** Paints the whole scene. The canvas is already scaled to device pixels. */\nexport function paintScene(\n\tcontext: CanvasRenderingContext2D,\n\tscene: ChartScene,\n\tresolve: (color: string) => string,\n\tfontFamily: string,\n) {\n\tcontext.clearRect(0, 0, scene.width, scene.height);\n\tconst environment = { context, resolve, fontFamily };\n\tfor (const node of scene.nodes) {\n\t\tpaintNode(environment, node);\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"area.d.ts","sourceRoot":"","sources":["../../../src/core/marks/area.ts"],"names":[],"mappings":"AAMA,OAAO,EACN,KAAK,gBAAgB,
|
|
1
|
+
{"version":3,"file":"area.d.ts","sourceRoot":"","sources":["../../../src/core/marks/area.ts"],"names":[],"mappings":"AAMA,OAAO,EACN,KAAK,gBAAgB,EAOrB,MAAM,wBAAwB,CAAC;AAIhC,OAAO,KAAK,EACX,aAAa,EACb,UAAU,EACV,SAAS,EAET,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,0EAA0E;IAC1E,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAClD,QAAQ,CAAC,EAAE,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAChD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,0DAA0D;IAC1D,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAC5B,gFAAgF;IAChF,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CACzB;AAsLD,sDAAsD;AACtD,wBAAgB,KAAK,CAAC,MAAM,EAC3B,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,OAAO,GAAE,WAAW,CAAC,MAAM,CAAM,GAC/B,SAAS,CAEX;AAED,+CAA+C;AAC/C,wBAAgB,KAAK,CAAC,MAAM,EAC3B,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,OAAO,GAAE,WAAW,CAAC,MAAM,CAAM,GAC/B,SAAS,CAEX"}
|
package/dist/core/marks/area.js
CHANGED
|
@@ -9,7 +9,8 @@ import {
|
|
|
9
9
|
groupBySeries,
|
|
10
10
|
markId,
|
|
11
11
|
paint,
|
|
12
|
-
seriesResolver
|
|
12
|
+
seriesResolver,
|
|
13
|
+
tips
|
|
13
14
|
} from "./shared.js";
|
|
14
15
|
import { stackSegments } from "./stack.js";
|
|
15
16
|
import { areaPath, linePath } from "../paths.js";
|
|
@@ -110,7 +111,7 @@ function areaMark(kind, data, options) {
|
|
|
110
111
|
const run = runs[runs.length - 1];
|
|
111
112
|
run.top.push(top);
|
|
112
113
|
run.bottom.push(place(along, edge.low));
|
|
113
|
-
if (options.tip
|
|
114
|
+
if (!tips(options.tip, data[index], index)) {
|
|
114
115
|
continue;
|
|
115
116
|
}
|
|
116
117
|
const value = explicit ? edge.high : values[index] ?? 0;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/core/marks/area.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n\tchannelLabel,\n\tisPlaceable,\n\tnumericValue,\n\treadChannel,\n} from \"#/core/channel.ts\";\nimport {\n\ttype ChartMarkOptions,\n\tcolorChannel,\n\tgroupBySeries,\n\tmarkId,\n\tpaint,\n\tseriesResolver,\n} from \"#/core/marks/shared.ts\";\nimport { stackSegments } from \"#/core/marks/stack.ts\";\nimport { areaPath, type ChartXY, linePath } from \"#/core/paths.ts\";\nimport { categoryKey } from \"#/core/scales/discrete.ts\";\nimport type {\n\tChartAccessor,\n\tChartCurve,\n\tChartMark,\n\tChartPoint,\n\tChartValue,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\nexport interface AreaOptions<TDatum> extends ChartMarkOptions<TDatum> {\n\treadonly x?: ChartAccessor<TDatum, ChartValue>;\n\treadonly y?: ChartAccessor<TDatum, ChartValue>;\n\t/** Explicit lower and upper edges: a confidence band, a min-max range. */\n\treadonly from?: ChartAccessor<TDatum, ChartValue>;\n\treadonly to?: ChartAccessor<TDatum, ChartValue>;\n\treadonly fill?: string;\n\treadonly fillOpacity?: number;\n\t/** Draw the upper edge as a line in the series colour. */\n\treadonly line?: boolean;\n\treadonly strokeWidth?: number;\n\treadonly strokeDasharray?: string;\n\treadonly curve?: ChartCurve;\n\t/** Series stack on one another. On by default when there is a color channel. */\n\treadonly stack?: boolean;\n}\n\nconst DEFAULT_FILL_OPACITY = 0.3;\nconst DEFAULT_STROKE_WIDTH = 2;\n\nfunction areaMark<TDatum>(\n\tkind: \"areaY\" | \"areaX\",\n\tdata: ReadonlyArray<TDatum>,\n\toptions: AreaOptions<TDatum>,\n): ChartMark {\n\tconst vertical = kind === \"areaY\";\n\tconst positionKey = vertical ? options.x : options.y;\n\tconst valueKey = vertical ? options.y : options.x;\n\tconst positions = readChannel(\n\t\tdata,\n\t\tpositionKey ?? ((_datum: TDatum, index: number) => index),\n\t);\n\tconst values = readChannel(\n\t\tdata,\n\t\tvalueKey ?? ((datum: TDatum) => datum as unknown as ChartValue),\n\t).map(numericValue);\n\tconst colors = colorChannel(data, options);\n\tconst explicit = options.from !== undefined && options.to !== undefined;\n\tconst froms = readChannel(data, options.from).map(numericValue);\n\tconst tos = readChannel(data, options.to).map(numericValue);\n\tconst stacked = !explicit && (options.stack ?? options.color !== undefined);\n\tconst segments = stacked\n\t\t? stackSegments(\n\t\t\t\tpositions.map((position) =>\n\t\t\t\t\tisPlaceable(position) ? categoryKey(position) : undefined,\n\t\t\t\t),\n\t\t\t\tvalues,\n\t\t\t)\n\t\t: [];\n\tconst edges = positions.map((position, index) => {\n\t\tif (!isPlaceable(position)) {\n\t\t\treturn undefined;\n\t\t}\n\t\tif (explicit) {\n\t\t\tconst low = froms[index];\n\t\t\tconst high = tos[index];\n\t\t\treturn low === undefined || high === undefined\n\t\t\t\t? undefined\n\t\t\t\t: { low, high };\n\t\t}\n\t\tconst value = values[index];\n\t\tif (value === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn segments[index] ?? { low: 0, high: value };\n\t});\n\n\tconst positionChannel = {\n\t\tvalues: positions.filter(isPlaceable),\n\t\tlabel: channelLabel(positionKey),\n\t};\n\tconst valueChannel = {\n\t\tvalues: edges.flatMap((edge) => (edge ? [edge.low, edge.high] : [])),\n\t\tincludeZero: !explicit,\n\t\tlabel: channelLabel(valueKey) ?? options.label,\n\t};\n\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"cartesian\",\n\t\tfocusOrder: vertical ? \"x\" : \"y\",\n\t\tchannels: {\n\t\t\tx: vertical ? positionChannel : valueChannel,\n\t\t\ty: vertical ? valueChannel : positionChannel,\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 positionScale = vertical ? context.scales.x : context.scales.y;\n\t\t\tconst valueScale = vertical ? context.scales.y : context.scales.x;\n\t\t\tif (positionScale === undefined || valueScale === undefined) {\n\t\t\t\treturn { nodes: [] };\n\t\t\t}\n\t\t\tconst seriesOf = seriesResolver(\n\t\t\t\tcolors.values,\n\t\t\t\t{ ...options, fixedColor: options.fill },\n\t\t\t\tid,\n\t\t\t\tcontext,\n\t\t\t);\n\t\t\tfunction place(along: number, value: number): ChartXY {\n\t\t\t\tconst across = valueScale?.map(value) ?? 0;\n\t\t\t\treturn vertical ? { x: along, y: across } : { x: across, y: along };\n\t\t\t}\n\n\t\t\tconst nodes: SceneNode[] = [];\n\t\t\tconst points: ChartPoint[] = [];\n\t\t\tfor (const { series, indices } of groupBySeries(\n\t\t\t\tdata.length,\n\t\t\t\tseriesOf,\n\t\t\t).values()) {\n\t\t\t\tconst runs: Array<{ top: ChartXY[]; bottom: ChartXY[] }> = [\n\t\t\t\t\t{ top: [], bottom: [] },\n\t\t\t\t];\n\t\t\t\tfor (const index of indices) {\n\t\t\t\t\tconst position = positions[index];\n\t\t\t\t\tconst edge = edges[index];\n\t\t\t\t\tif (!isPlaceable(position) || edge === undefined) {\n\t\t\t\t\t\truns.push({ top: [], bottom: [] });\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst along = positionScale.center(position);\n\t\t\t\t\tconst top = place(along, edge.high);\n\t\t\t\t\tconst run = runs[runs.length - 1];\n\t\t\t\t\trun.top.push(top);\n\t\t\t\t\trun.bottom.push(place(along, edge.low));\n\t\t\t\t\tif (options.tip
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import {\n\tchannelLabel,\n\tisPlaceable,\n\tnumericValue,\n\treadChannel,\n} from \"#/core/channel.ts\";\nimport {\n\ttype ChartMarkOptions,\n\tcolorChannel,\n\tgroupBySeries,\n\tmarkId,\n\tpaint,\n\tseriesResolver,\n\ttips,\n} from \"#/core/marks/shared.ts\";\nimport { stackSegments } from \"#/core/marks/stack.ts\";\nimport { areaPath, type ChartXY, linePath } from \"#/core/paths.ts\";\nimport { categoryKey } from \"#/core/scales/discrete.ts\";\nimport type {\n\tChartAccessor,\n\tChartCurve,\n\tChartMark,\n\tChartPoint,\n\tChartValue,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\nexport interface AreaOptions<TDatum> extends ChartMarkOptions<TDatum> {\n\treadonly x?: ChartAccessor<TDatum, ChartValue>;\n\treadonly y?: ChartAccessor<TDatum, ChartValue>;\n\t/** Explicit lower and upper edges: a confidence band, a min-max range. */\n\treadonly from?: ChartAccessor<TDatum, ChartValue>;\n\treadonly to?: ChartAccessor<TDatum, ChartValue>;\n\treadonly fill?: string;\n\treadonly fillOpacity?: number;\n\t/** Draw the upper edge as a line in the series colour. */\n\treadonly line?: boolean;\n\treadonly strokeWidth?: number;\n\treadonly strokeDasharray?: string;\n\treadonly curve?: ChartCurve;\n\t/** Series stack on one another. On by default when there is a color channel. */\n\treadonly stack?: boolean;\n}\n\nconst DEFAULT_FILL_OPACITY = 0.3;\nconst DEFAULT_STROKE_WIDTH = 2;\n\nfunction areaMark<TDatum>(\n\tkind: \"areaY\" | \"areaX\",\n\tdata: ReadonlyArray<TDatum>,\n\toptions: AreaOptions<TDatum>,\n): ChartMark {\n\tconst vertical = kind === \"areaY\";\n\tconst positionKey = vertical ? options.x : options.y;\n\tconst valueKey = vertical ? options.y : options.x;\n\tconst positions = readChannel(\n\t\tdata,\n\t\tpositionKey ?? ((_datum: TDatum, index: number) => index),\n\t);\n\tconst values = readChannel(\n\t\tdata,\n\t\tvalueKey ?? ((datum: TDatum) => datum as unknown as ChartValue),\n\t).map(numericValue);\n\tconst colors = colorChannel(data, options);\n\tconst explicit = options.from !== undefined && options.to !== undefined;\n\tconst froms = readChannel(data, options.from).map(numericValue);\n\tconst tos = readChannel(data, options.to).map(numericValue);\n\tconst stacked = !explicit && (options.stack ?? options.color !== undefined);\n\tconst segments = stacked\n\t\t? stackSegments(\n\t\t\t\tpositions.map((position) =>\n\t\t\t\t\tisPlaceable(position) ? categoryKey(position) : undefined,\n\t\t\t\t),\n\t\t\t\tvalues,\n\t\t\t)\n\t\t: [];\n\tconst edges = positions.map((position, index) => {\n\t\tif (!isPlaceable(position)) {\n\t\t\treturn undefined;\n\t\t}\n\t\tif (explicit) {\n\t\t\tconst low = froms[index];\n\t\t\tconst high = tos[index];\n\t\t\treturn low === undefined || high === undefined\n\t\t\t\t? undefined\n\t\t\t\t: { low, high };\n\t\t}\n\t\tconst value = values[index];\n\t\tif (value === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn segments[index] ?? { low: 0, high: value };\n\t});\n\n\tconst positionChannel = {\n\t\tvalues: positions.filter(isPlaceable),\n\t\tlabel: channelLabel(positionKey),\n\t};\n\tconst valueChannel = {\n\t\tvalues: edges.flatMap((edge) => (edge ? [edge.low, edge.high] : [])),\n\t\tincludeZero: !explicit,\n\t\tlabel: channelLabel(valueKey) ?? options.label,\n\t};\n\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"cartesian\",\n\t\tfocusOrder: vertical ? \"x\" : \"y\",\n\t\tchannels: {\n\t\t\tx: vertical ? positionChannel : valueChannel,\n\t\t\ty: vertical ? valueChannel : positionChannel,\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 positionScale = vertical ? context.scales.x : context.scales.y;\n\t\t\tconst valueScale = vertical ? context.scales.y : context.scales.x;\n\t\t\tif (positionScale === undefined || valueScale === undefined) {\n\t\t\t\treturn { nodes: [] };\n\t\t\t}\n\t\t\tconst seriesOf = seriesResolver(\n\t\t\t\tcolors.values,\n\t\t\t\t{ ...options, fixedColor: options.fill },\n\t\t\t\tid,\n\t\t\t\tcontext,\n\t\t\t);\n\t\t\tfunction place(along: number, value: number): ChartXY {\n\t\t\t\tconst across = valueScale?.map(value) ?? 0;\n\t\t\t\treturn vertical ? { x: along, y: across } : { x: across, y: along };\n\t\t\t}\n\n\t\t\tconst nodes: SceneNode[] = [];\n\t\t\tconst points: ChartPoint[] = [];\n\t\t\tfor (const { series, indices } of groupBySeries(\n\t\t\t\tdata.length,\n\t\t\t\tseriesOf,\n\t\t\t).values()) {\n\t\t\t\tconst runs: Array<{ top: ChartXY[]; bottom: ChartXY[] }> = [\n\t\t\t\t\t{ top: [], bottom: [] },\n\t\t\t\t];\n\t\t\t\tfor (const index of indices) {\n\t\t\t\t\tconst position = positions[index];\n\t\t\t\t\tconst edge = edges[index];\n\t\t\t\t\tif (!isPlaceable(position) || edge === undefined) {\n\t\t\t\t\t\truns.push({ top: [], bottom: [] });\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst along = positionScale.center(position);\n\t\t\t\t\tconst top = place(along, edge.high);\n\t\t\t\t\tconst run = runs[runs.length - 1];\n\t\t\t\t\trun.top.push(top);\n\t\t\t\t\trun.bottom.push(place(along, edge.low));\n\t\t\t\t\tif (!tips(options.tip, data[index], index)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst value = explicit ? edge.high : (values[index] ?? 0);\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: data[index],\n\t\t\t\t\t\tx: top.x,\n\t\t\t\t\t\ty: top.y,\n\t\t\t\t\t\txValue: vertical ? position : value,\n\t\t\t\t\t\tyValue: vertical ? value : position,\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: vertical\n\t\t\t\t\t\t\t? context.formatX(position)\n\t\t\t\t\t\t\t: context.formatY(position),\n\t\t\t\t\t\tvalue: vertical ? context.formatY(value) : context.formatX(value),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tconst shaped = runs.filter((run) => run.top.length > 0);\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"path\",\n\t\t\t\t\tkey: `${id}:area:${series.key}`,\n\t\t\t\t\tseries: series.key,\n\t\t\t\t\trole: \"mark\",\n\t\t\t\t\td: shaped\n\t\t\t\t\t\t.map((run) => areaPath(run.top, run.bottom, options.curve))\n\t\t\t\t\t\t.join(\"\"),\n\t\t\t\t\tpaint: paint({\n\t\t\t\t\t\tfill: series.color,\n\t\t\t\t\t\tfillOpacity: options.fillOpacity ?? DEFAULT_FILL_OPACITY,\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 (options.line) {\n\t\t\t\t\tnodes.push({\n\t\t\t\t\t\tkind: \"path\",\n\t\t\t\t\t\tkey: `${id}:line:${series.key}`,\n\t\t\t\t\t\tseries: series.key,\n\t\t\t\t\t\trole: \"mark\",\n\t\t\t\t\t\td: shaped.map((run) => linePath(run.top, options.curve)).join(\"\"),\n\t\t\t\t\t\tpaint: paint({\n\t\t\t\t\t\t\tfill: \"none\",\n\t\t\t\t\t\t\tstroke: series.color,\n\t\t\t\t\t\t\tstrokeWidth: options.strokeWidth ?? DEFAULT_STROKE_WIDTH,\n\t\t\t\t\t\t\tstrokeDasharray: options.strokeDasharray,\n\t\t\t\t\t\t\tstrokeLinecap: \"round\",\n\t\t\t\t\t\t\tstrokeLinejoin: \"round\",\n\t\t\t\t\t\t\topacity: options.opacity,\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\tconst legend =\n\t\t\t\toptions.color === undefined && options.label !== undefined\n\t\t\t\t\t? [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tkey: id,\n\t\t\t\t\t\t\t\tlabel: options.label,\n\t\t\t\t\t\t\t\tcolor: options.fill ?? context.paletteColor(context.markIndex),\n\t\t\t\t\t\t\t\tshape: \"square\" as const,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t]\n\t\t\t\t\t: [];\n\t\t\treturn { nodes, points, legend };\n\t\t},\n\t};\n}\n\n/** A filled band under a series, over x positions. */\nexport function areaY<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: AreaOptions<TDatum> = {},\n): ChartMark {\n\treturn areaMark(\"areaY\", data, options);\n}\n\n/** A filled band along x, over y positions. */\nexport function areaX<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: AreaOptions<TDatum> = {},\n): ChartMark {\n\treturn areaMark(\"areaX\", data, options);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/core/marks/bar.d.ts
CHANGED
|
@@ -18,6 +18,11 @@ export interface BarOptions<TDatum> extends ChartMarkOptions<TDatum> {
|
|
|
18
18
|
readonly radius?: number;
|
|
19
19
|
/** Pixels kept clear on each side of the bar inside its band. */
|
|
20
20
|
readonly inset?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Bars to draw hatched and outlined: projections, stages still in flight,
|
|
23
|
+
* anything pencilled in rather than recorded.
|
|
24
|
+
*/
|
|
25
|
+
readonly projected?: (datum: TDatum, index: number) => boolean;
|
|
21
26
|
}
|
|
22
27
|
/** Upright bars: categories along x, values up y. */
|
|
23
28
|
export declare function barY<TDatum>(data: ReadonlyArray<TDatum>, options?: BarOptions<TDatum>): ChartMark;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bar.d.ts","sourceRoot":"","sources":["../../../src/core/marks/bar.ts"],"names":[],"mappings":"AAMA,OAAO,EACN,KAAK,gBAAgB,
|
|
1
|
+
{"version":3,"file":"bar.d.ts","sourceRoot":"","sources":["../../../src/core/marks/bar.ts"],"names":[],"mappings":"AAMA,OAAO,EACN,KAAK,gBAAgB,EAMrB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,KAAK,EACX,aAAa,EAEb,SAAS,EAET,UAAU,EAEV,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,UAAU,CAAC,MAAM,CAAE,SAAQ,gBAAgB,CAAC,MAAM,CAAC;IACnE,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,8FAA8F;IAC9F,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAClD,QAAQ,CAAC,EAAE,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAChD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,gFAAgF;IAChF,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,qEAAqE;IACrE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;CAC/D;AAwOD,qDAAqD;AACrD,wBAAgB,IAAI,CAAC,MAAM,EAC1B,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,OAAO,GAAE,UAAU,CAAC,MAAM,CAAM,GAC9B,SAAS,CAEX;AAED,6DAA6D;AAC7D,wBAAgB,IAAI,CAAC,MAAM,EAC1B,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,OAAO,GAAE,UAAU,CAAC,MAAM,CAAM,GAC9B,SAAS,CAEX"}
|
package/dist/core/marks/bar.js
CHANGED
|
@@ -8,7 +8,8 @@ import {
|
|
|
8
8
|
colorChannel,
|
|
9
9
|
markId,
|
|
10
10
|
paint,
|
|
11
|
-
seriesResolver
|
|
11
|
+
seriesResolver,
|
|
12
|
+
tips
|
|
12
13
|
} from "./shared.js";
|
|
13
14
|
import { stackSegments } from "./stack.js";
|
|
14
15
|
import { categoryKey } from "../scales/discrete.js";
|
|
@@ -133,15 +134,24 @@ function barMark(kind, data, options) {
|
|
|
133
134
|
role: "mark",
|
|
134
135
|
...rect,
|
|
135
136
|
corners: corners(vertical, bar, radius, explicit),
|
|
136
|
-
paint: paint(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
137
|
+
paint: paint(
|
|
138
|
+
options.projected?.(data[bar.index], bar.index) ? {
|
|
139
|
+
fill: series.color,
|
|
140
|
+
hatch: true,
|
|
141
|
+
stroke: series.color,
|
|
142
|
+
strokeWidth: 1,
|
|
143
|
+
strokeDasharray: "3 2",
|
|
144
|
+
opacity: options.opacity
|
|
145
|
+
} : {
|
|
146
|
+
fill: series.color,
|
|
147
|
+
fillOpacity: options.fillOpacity,
|
|
148
|
+
stroke: options.stroke,
|
|
149
|
+
strokeDasharray: options.strokeDasharray,
|
|
150
|
+
opacity: options.opacity
|
|
151
|
+
}
|
|
152
|
+
)
|
|
143
153
|
});
|
|
144
|
-
if (options.tip
|
|
154
|
+
if (!tips(options.tip, data[bar.index], bar.index)) {
|
|
145
155
|
continue;
|
|
146
156
|
}
|
|
147
157
|
const along = alongStart + alongSize / 2;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/core/marks/bar.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n\tchannelLabel,\n\tisPlaceable,\n\tnumericValue,\n\treadChannel,\n} from \"#/core/channel.ts\";\nimport {\n\ttype ChartMarkOptions,\n\tcolorChannel,\n\tmarkId,\n\tpaint,\n\tseriesResolver,\n} from \"#/core/marks/shared.ts\";\nimport { stackSegments } from \"#/core/marks/stack.ts\";\nimport { categoryKey } from \"#/core/scales/discrete.ts\";\nimport type {\n\tChartAccessor,\n\tChartCorners,\n\tChartMark,\n\tChartPoint,\n\tChartValue,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\nexport interface BarOptions<TDatum> extends ChartMarkOptions<TDatum> {\n\treadonly x?: ChartAccessor<TDatum, ChartValue>;\n\treadonly y?: ChartAccessor<TDatum, ChartValue>;\n\t/** Explicit start and end of the bar on the value axis: floating bars, ranges, Gantt rows. */\n\treadonly from?: ChartAccessor<TDatum, ChartValue>;\n\treadonly to?: ChartAccessor<TDatum, ChartValue>;\n\treadonly fill?: string;\n\treadonly fillOpacity?: number;\n\treadonly stroke?: string;\n\treadonly strokeDasharray?: string;\n\t/** Series stack on one another. On by default when there is a color channel. */\n\treadonly stack?: boolean;\n\t/** Series stand side by side inside the band instead of stacking. */\n\treadonly group?: boolean;\n\t/** Corner radius of the bar's far end. */\n\treadonly radius?: number;\n\t/** Pixels kept clear on each side of the bar inside its band. */\n\treadonly inset?: number;\n}\n\nconst DEFAULT_RADIUS = 4;\n\ninterface BarDatum {\n\treadonly index: number;\n\treadonly position: ChartValue;\n\treadonly low: number;\n\treadonly high: number;\n\treadonly value: number;\n\t/** Round the far end: the outer segment of a stack, or any unstacked bar. */\n\treadonly outer: boolean;\n}\n\nfunction barMark<TDatum>(\n\tkind: \"barY\" | \"barX\",\n\tdata: ReadonlyArray<TDatum>,\n\toptions: BarOptions<TDatum>,\n): ChartMark {\n\tconst vertical = kind === \"barY\";\n\tconst positionKey = vertical ? options.x : options.y;\n\tconst valueKey = vertical ? options.y : options.x;\n\tconst positions = readChannel(\n\t\tdata,\n\t\tpositionKey ?? ((_datum: TDatum, index: number) => index),\n\t);\n\tconst values = readChannel(\n\t\tdata,\n\t\tvalueKey ?? ((datum: TDatum) => datum as unknown as ChartValue),\n\t).map(numericValue);\n\tconst colors = colorChannel(data, options);\n\tconst explicit = options.from !== undefined && options.to !== undefined;\n\tconst froms = readChannel(data, options.from).map(numericValue);\n\tconst tos = readChannel(data, options.to).map(numericValue);\n\tconst stacked =\n\t\t!explicit &&\n\t\t!options.group &&\n\t\t(options.stack ?? options.color !== undefined);\n\tconst segments = stacked\n\t\t? stackSegments(\n\t\t\t\tpositions.map((position) =>\n\t\t\t\t\tisPlaceable(position) ? categoryKey(position) : undefined,\n\t\t\t\t),\n\t\t\t\tvalues,\n\t\t\t)\n\t\t: [];\n\n\tconst bars: BarDatum[] = [];\n\tfor (const [index, position] of positions.entries()) {\n\t\tif (!isPlaceable(position)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (explicit) {\n\t\t\tconst low = froms[index];\n\t\t\tconst high = tos[index];\n\t\t\tif (low !== undefined && high !== undefined) {\n\t\t\t\tbars.push({\n\t\t\t\t\tindex,\n\t\t\t\t\tposition,\n\t\t\t\t\tlow,\n\t\t\t\t\thigh,\n\t\t\t\t\tvalue: high - low,\n\t\t\t\t\touter: true,\n\t\t\t\t});\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tconst value = values[index];\n\t\tif (value === undefined) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst segment = segments[index];\n\t\tbars.push(\n\t\t\tsegment === undefined\n\t\t\t\t? { index, position, low: 0, high: value, value, outer: true }\n\t\t\t\t: { index, position, ...segment, value },\n\t\t);\n\t}\n\n\tconst positionChannel = {\n\t\tvalues: bars.map((bar) => bar.position),\n\t\tdiscrete: \"band\" as const,\n\t\tlabel: channelLabel(positionKey),\n\t};\n\tconst valueChannel = {\n\t\tvalues: bars.flatMap((bar) => [bar.low, bar.high]),\n\t\tincludeZero: !explicit,\n\t\tlabel: channelLabel(valueKey) ?? options.label,\n\t};\n\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"cartesian\",\n\t\tfocusOrder: vertical ? \"x\" : \"y\",\n\t\tchannels: {\n\t\t\tx: vertical ? positionChannel : valueChannel,\n\t\t\ty: vertical ? valueChannel : positionChannel,\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 positionScale = vertical ? context.scales.x : context.scales.y;\n\t\t\tconst valueScale = vertical ? context.scales.y : context.scales.x;\n\t\t\tif (positionScale === undefined || valueScale === undefined) {\n\t\t\t\treturn { nodes: [] };\n\t\t\t}\n\t\t\tconst seriesOf = seriesResolver(\n\t\t\t\tcolors.values,\n\t\t\t\t{ ...options, fixedColor: options.fill },\n\t\t\t\tid,\n\t\t\t\tcontext,\n\t\t\t);\n\t\t\tconst seriesOrder = [\n\t\t\t\t...new Set(bars.map((bar) => seriesOf(bar.index).key)),\n\t\t\t];\n\t\t\tconst lanes = options.group ? Math.max(1, seriesOrder.length) : 1;\n\t\t\tconst laneWidth = positionScale.bandwidth / lanes;\n\t\t\tconst inset = options.inset ?? 0;\n\t\t\tconst radius = options.radius ?? DEFAULT_RADIUS;\n\n\t\t\tconst nodes: SceneNode[] = [];\n\t\t\tconst points: ChartPoint[] = [];\n\t\t\tfor (const bar of bars) {\n\t\t\t\tconst series = seriesOf(bar.index);\n\t\t\t\tconst lane = options.group ? seriesOrder.indexOf(series.key) : 0;\n\t\t\t\tconst alongStart =\n\t\t\t\t\tpositionScale.map(bar.position) + lane * laneWidth + inset;\n\t\t\t\tconst alongSize = Math.max(0, laneWidth - inset * 2);\n\t\t\t\tconst lowPixel = valueScale.map(bar.low);\n\t\t\t\tconst highPixel = valueScale.map(bar.high);\n\t\t\t\tconst acrossStart = Math.min(lowPixel, highPixel);\n\t\t\t\tconst acrossSize = Math.abs(highPixel - lowPixel);\n\t\t\t\tconst rect = vertical\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tx: alongStart,\n\t\t\t\t\t\t\ty: acrossStart,\n\t\t\t\t\t\t\twidth: alongSize,\n\t\t\t\t\t\t\theight: acrossSize,\n\t\t\t\t\t\t}\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tx: acrossStart,\n\t\t\t\t\t\t\ty: alongStart,\n\t\t\t\t\t\t\twidth: acrossSize,\n\t\t\t\t\t\t\theight: alongSize,\n\t\t\t\t\t\t};\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"rect\",\n\t\t\t\t\tkey: `${id}:${bar.index}`,\n\t\t\t\t\tseries: series.key,\n\t\t\t\t\trole: \"mark\",\n\t\t\t\t\t...rect,\n\t\t\t\t\tcorners: corners(vertical, bar, radius, explicit),\n\t\t\t\t\tpaint: paint({\n\t\t\t\t\t\tfill: series.color,\n\t\t\t\t\t\tfillOpacity: options.fillOpacity,\n\t\t\t\t\t\tstroke: options.stroke,\n\t\t\t\t\t\tstrokeDasharray: options.strokeDasharray,\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 (options.tip
|
|
5
|
-
"mappings": "AAAA;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP;AAAA,EAEC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,qBAAqB;AAC9B,SAAS,mBAAmB;
|
|
4
|
+
"sourcesContent": ["import {\n\tchannelLabel,\n\tisPlaceable,\n\tnumericValue,\n\treadChannel,\n} from \"#/core/channel.ts\";\nimport {\n\ttype ChartMarkOptions,\n\tcolorChannel,\n\tmarkId,\n\tpaint,\n\tseriesResolver,\n\ttips,\n} from \"#/core/marks/shared.ts\";\nimport { stackSegments } from \"#/core/marks/stack.ts\";\nimport { categoryKey } from \"#/core/scales/discrete.ts\";\nimport type {\n\tChartAccessor,\n\tChartCorners,\n\tChartMark,\n\tChartPoint,\n\tChartValue,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\nexport interface BarOptions<TDatum> extends ChartMarkOptions<TDatum> {\n\treadonly x?: ChartAccessor<TDatum, ChartValue>;\n\treadonly y?: ChartAccessor<TDatum, ChartValue>;\n\t/** Explicit start and end of the bar on the value axis: floating bars, ranges, Gantt rows. */\n\treadonly from?: ChartAccessor<TDatum, ChartValue>;\n\treadonly to?: ChartAccessor<TDatum, ChartValue>;\n\treadonly fill?: string;\n\treadonly fillOpacity?: number;\n\treadonly stroke?: string;\n\treadonly strokeDasharray?: string;\n\t/** Series stack on one another. On by default when there is a color channel. */\n\treadonly stack?: boolean;\n\t/** Series stand side by side inside the band instead of stacking. */\n\treadonly group?: boolean;\n\t/** Corner radius of the bar's far end. */\n\treadonly radius?: number;\n\t/** Pixels kept clear on each side of the bar inside its band. */\n\treadonly inset?: number;\n\t/**\n\t * Bars to draw hatched and outlined: projections, stages still in flight,\n\t * anything pencilled in rather than recorded.\n\t */\n\treadonly projected?: (datum: TDatum, index: number) => boolean;\n}\n\nconst DEFAULT_RADIUS = 4;\n\ninterface BarDatum {\n\treadonly index: number;\n\treadonly position: ChartValue;\n\treadonly low: number;\n\treadonly high: number;\n\treadonly value: number;\n\t/** Round the far end: the outer segment of a stack, or any unstacked bar. */\n\treadonly outer: boolean;\n}\n\nfunction barMark<TDatum>(\n\tkind: \"barY\" | \"barX\",\n\tdata: ReadonlyArray<TDatum>,\n\toptions: BarOptions<TDatum>,\n): ChartMark {\n\tconst vertical = kind === \"barY\";\n\tconst positionKey = vertical ? options.x : options.y;\n\tconst valueKey = vertical ? options.y : options.x;\n\tconst positions = readChannel(\n\t\tdata,\n\t\tpositionKey ?? ((_datum: TDatum, index: number) => index),\n\t);\n\tconst values = readChannel(\n\t\tdata,\n\t\tvalueKey ?? ((datum: TDatum) => datum as unknown as ChartValue),\n\t).map(numericValue);\n\tconst colors = colorChannel(data, options);\n\tconst explicit = options.from !== undefined && options.to !== undefined;\n\tconst froms = readChannel(data, options.from).map(numericValue);\n\tconst tos = readChannel(data, options.to).map(numericValue);\n\tconst stacked =\n\t\t!explicit &&\n\t\t!options.group &&\n\t\t(options.stack ?? options.color !== undefined);\n\tconst segments = stacked\n\t\t? stackSegments(\n\t\t\t\tpositions.map((position) =>\n\t\t\t\t\tisPlaceable(position) ? categoryKey(position) : undefined,\n\t\t\t\t),\n\t\t\t\tvalues,\n\t\t\t)\n\t\t: [];\n\n\tconst bars: BarDatum[] = [];\n\tfor (const [index, position] of positions.entries()) {\n\t\tif (!isPlaceable(position)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (explicit) {\n\t\t\tconst low = froms[index];\n\t\t\tconst high = tos[index];\n\t\t\tif (low !== undefined && high !== undefined) {\n\t\t\t\tbars.push({\n\t\t\t\t\tindex,\n\t\t\t\t\tposition,\n\t\t\t\t\tlow,\n\t\t\t\t\thigh,\n\t\t\t\t\tvalue: high - low,\n\t\t\t\t\touter: true,\n\t\t\t\t});\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tconst value = values[index];\n\t\tif (value === undefined) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst segment = segments[index];\n\t\tbars.push(\n\t\t\tsegment === undefined\n\t\t\t\t? { index, position, low: 0, high: value, value, outer: true }\n\t\t\t\t: { index, position, ...segment, value },\n\t\t);\n\t}\n\n\tconst positionChannel = {\n\t\tvalues: bars.map((bar) => bar.position),\n\t\tdiscrete: \"band\" as const,\n\t\tlabel: channelLabel(positionKey),\n\t};\n\tconst valueChannel = {\n\t\tvalues: bars.flatMap((bar) => [bar.low, bar.high]),\n\t\tincludeZero: !explicit,\n\t\tlabel: channelLabel(valueKey) ?? options.label,\n\t};\n\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"cartesian\",\n\t\tfocusOrder: vertical ? \"x\" : \"y\",\n\t\tchannels: {\n\t\t\tx: vertical ? positionChannel : valueChannel,\n\t\t\ty: vertical ? valueChannel : positionChannel,\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 positionScale = vertical ? context.scales.x : context.scales.y;\n\t\t\tconst valueScale = vertical ? context.scales.y : context.scales.x;\n\t\t\tif (positionScale === undefined || valueScale === undefined) {\n\t\t\t\treturn { nodes: [] };\n\t\t\t}\n\t\t\tconst seriesOf = seriesResolver(\n\t\t\t\tcolors.values,\n\t\t\t\t{ ...options, fixedColor: options.fill },\n\t\t\t\tid,\n\t\t\t\tcontext,\n\t\t\t);\n\t\t\tconst seriesOrder = [\n\t\t\t\t...new Set(bars.map((bar) => seriesOf(bar.index).key)),\n\t\t\t];\n\t\t\tconst lanes = options.group ? Math.max(1, seriesOrder.length) : 1;\n\t\t\tconst laneWidth = positionScale.bandwidth / lanes;\n\t\t\tconst inset = options.inset ?? 0;\n\t\t\tconst radius = options.radius ?? DEFAULT_RADIUS;\n\n\t\t\tconst nodes: SceneNode[] = [];\n\t\t\tconst points: ChartPoint[] = [];\n\t\t\tfor (const bar of bars) {\n\t\t\t\tconst series = seriesOf(bar.index);\n\t\t\t\tconst lane = options.group ? seriesOrder.indexOf(series.key) : 0;\n\t\t\t\tconst alongStart =\n\t\t\t\t\tpositionScale.map(bar.position) + lane * laneWidth + inset;\n\t\t\t\tconst alongSize = Math.max(0, laneWidth - inset * 2);\n\t\t\t\tconst lowPixel = valueScale.map(bar.low);\n\t\t\t\tconst highPixel = valueScale.map(bar.high);\n\t\t\t\tconst acrossStart = Math.min(lowPixel, highPixel);\n\t\t\t\tconst acrossSize = Math.abs(highPixel - lowPixel);\n\t\t\t\tconst rect = vertical\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tx: alongStart,\n\t\t\t\t\t\t\ty: acrossStart,\n\t\t\t\t\t\t\twidth: alongSize,\n\t\t\t\t\t\t\theight: acrossSize,\n\t\t\t\t\t\t}\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tx: acrossStart,\n\t\t\t\t\t\t\ty: alongStart,\n\t\t\t\t\t\t\twidth: acrossSize,\n\t\t\t\t\t\t\theight: alongSize,\n\t\t\t\t\t\t};\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"rect\",\n\t\t\t\t\tkey: `${id}:${bar.index}`,\n\t\t\t\t\tseries: series.key,\n\t\t\t\t\trole: \"mark\",\n\t\t\t\t\t...rect,\n\t\t\t\t\tcorners: corners(vertical, bar, radius, explicit),\n\t\t\t\t\tpaint: paint(\n\t\t\t\t\t\toptions.projected?.(data[bar.index], bar.index)\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tfill: series.color,\n\t\t\t\t\t\t\t\t\thatch: true,\n\t\t\t\t\t\t\t\t\tstroke: series.color,\n\t\t\t\t\t\t\t\t\tstrokeWidth: 1,\n\t\t\t\t\t\t\t\t\tstrokeDasharray: \"3 2\",\n\t\t\t\t\t\t\t\t\topacity: options.opacity,\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t\tfill: series.color,\n\t\t\t\t\t\t\t\t\tfillOpacity: options.fillOpacity,\n\t\t\t\t\t\t\t\t\tstroke: options.stroke,\n\t\t\t\t\t\t\t\t\tstrokeDasharray: options.strokeDasharray,\n\t\t\t\t\t\t\t\t\topacity: options.opacity,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t),\n\t\t\t\t});\n\t\t\t\tif (!tips(options.tip, data[bar.index], bar.index)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst along = alongStart + alongSize / 2;\n\t\t\t\tpoints.push({\n\t\t\t\t\tkey: `${id}:${bar.index}`,\n\t\t\t\t\tmarkId: id,\n\t\t\t\t\tindex: bar.index,\n\t\t\t\t\tdatum: data[bar.index],\n\t\t\t\t\tx: vertical ? along : highPixel,\n\t\t\t\t\ty: vertical ? highPixel : along,\n\t\t\t\t\txValue: vertical ? bar.position : bar.value,\n\t\t\t\t\tyValue: vertical ? bar.value : bar.position,\n\t\t\t\t\tseries: series.key,\n\t\t\t\t\tseriesLabel: series.label,\n\t\t\t\t\tcolor: series.color,\n\t\t\t\t\ttitle: vertical\n\t\t\t\t\t\t? context.formatX(bar.position)\n\t\t\t\t\t\t: context.formatY(bar.position),\n\t\t\t\t\tvalue: vertical\n\t\t\t\t\t\t? context.formatY(bar.value)\n\t\t\t\t\t\t: context.formatX(bar.value),\n\t\t\t\t\thit: { kind: \"rect\", rect },\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst legend =\n\t\t\t\toptions.color === undefined && options.label !== undefined\n\t\t\t\t\t? [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tkey: id,\n\t\t\t\t\t\t\t\tlabel: options.label,\n\t\t\t\t\t\t\t\tcolor: options.fill ?? context.paletteColor(context.markIndex),\n\t\t\t\t\t\t\t\tshape: \"square\" as const,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t]\n\t\t\t\t\t: [];\n\t\t\treturn { nodes, points, legend };\n\t\t},\n\t};\n}\n\n/** Rounds the end of the bar away from zero, and only that end: stacked joins stay square. */\nfunction corners(\n\tvertical: boolean,\n\tbar: BarDatum,\n\tradius: number,\n\texplicit: boolean,\n): ChartCorners | undefined {\n\tif (radius === 0 || !bar.outer) {\n\t\treturn undefined;\n\t}\n\tif (explicit) {\n\t\treturn [radius, radius, radius, radius];\n\t}\n\tconst positive = bar.high >= bar.low;\n\tif (vertical) {\n\t\treturn positive ? [radius, radius, 0, 0] : [0, 0, radius, radius];\n\t}\n\treturn positive ? [0, radius, radius, 0] : [radius, 0, 0, radius];\n}\n\n/** Upright bars: categories along x, values up y. */\nexport function barY<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: BarOptions<TDatum> = {},\n): ChartMark {\n\treturn barMark(\"barY\", data, options);\n}\n\n/** Bars on their side: categories down y, values along x. */\nexport function barX<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: BarOptions<TDatum> = {},\n): ChartMark {\n\treturn barMark(\"barX\", data, options);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell.d.ts","sourceRoot":"","sources":["../../../src/core/marks/cell.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,KAAK,gBAAgB,
|
|
1
|
+
{"version":3,"file":"cell.d.ts","sourceRoot":"","sources":["../../../src/core/marks/cell.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,KAAK,gBAAgB,EAOrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EACX,aAAa,EACb,SAAS,EAET,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,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,0CAA0C;IAC1C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CACzB;AAKD;;;GAGG;AACH,wBAAgB,IAAI,CAAC,MAAM,EAC1B,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAC1B,SAAS,CAqFX"}
|
package/dist/core/marks/cell.js
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
markId,
|
|
6
6
|
paint,
|
|
7
7
|
seriesResolver,
|
|
8
|
+
tips,
|
|
8
9
|
valueOn
|
|
9
10
|
} from "./shared.js";
|
|
10
11
|
const DEFAULT_INSET = 1;
|
|
@@ -70,7 +71,7 @@ function cell(data, options) {
|
|
|
70
71
|
corners: [radius, radius, radius, radius],
|
|
71
72
|
paint: paint({ fill: series.color, opacity: options.opacity })
|
|
72
73
|
});
|
|
73
|
-
if (options.tip
|
|
74
|
+
if (tips(options.tip, datum, index)) {
|
|
74
75
|
points.push({
|
|
75
76
|
key: `${id}:${index}`,
|
|
76
77
|
markId: id,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/core/marks/cell.ts"],
|
|
4
|
-
"sourcesContent": ["import { channelLabel, isPlaceable, readChannel } from \"#/core/channel.ts\";\nimport { formatValue } from \"#/core/format.ts\";\nimport {\n\ttype ChartMarkOptions,\n\tcolorChannel,\n\tmarkId,\n\tpaint,\n\tseriesResolver,\n\tvalueOn,\n} from \"#/core/marks/shared.ts\";\nimport type {\n\tChartAccessor,\n\tChartMark,\n\tChartPoint,\n\tChartValue,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\nexport interface CellOptions<TDatum> extends ChartMarkOptions<TDatum> {\n\treadonly x?: ChartAccessor<TDatum, ChartValue>;\n\treadonly y?: ChartAccessor<TDatum, ChartValue>;\n\treadonly fill?: string;\n\t/** Pixels kept clear around each cell. */\n\treadonly inset?: number;\n\treadonly radius?: number;\n}\n\nconst DEFAULT_INSET = 1;\nconst DEFAULT_RADIUS = 2;\n\n/**\n * A rectangle per (x, y) pair of categories, filled by its color value: a\n * heatmap, a calendar, a matrix.\n */\nexport function cell<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: CellOptions<TDatum>,\n): ChartMark {\n\tconst xs = readChannel(data, options.x);\n\tconst ys = readChannel(data, options.y);\n\tconst colors = colorChannel(data, options);\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"cartesian\",\n\t\tfocusOrder: \"point\",\n\t\tchannels: {\n\t\t\tx: {\n\t\t\t\tvalues: xs.filter(isPlaceable),\n\t\t\t\tdiscrete: \"band\",\n\t\t\t\tpadding: 0,\n\t\t\t\tlabel: channelLabel(options.x),\n\t\t\t},\n\t\t\ty: {\n\t\t\t\tvalues: ys.filter(isPlaceable),\n\t\t\t\tdiscrete: \"band\",\n\t\t\t\tpadding: 0,\n\t\t\t\tlabel: channelLabel(options.y),\n\t\t\t},\n\t\t\tcolor: colors.channel,\n\t\t},\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"cell\", context);\n\t\t\tconst { x: xScale, y: yScale } = context.scales;\n\t\t\tif (xScale === undefined || yScale === undefined) {\n\t\t\t\treturn { nodes: [] };\n\t\t\t}\n\t\t\tconst seriesOf = seriesResolver(\n\t\t\t\tcolors.values,\n\t\t\t\t{ ...options, fixedColor: options.fill },\n\t\t\t\tid,\n\t\t\t\tcontext,\n\t\t\t);\n\t\t\tconst inset = options.inset ?? DEFAULT_INSET;\n\t\t\tconst radius = options.radius ?? DEFAULT_RADIUS;\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 xValue = valueOn(xScale, xs[index]);\n\t\t\t\tconst yValue = valueOn(yScale, ys[index]);\n\t\t\t\tif (xValue === undefined || yValue === undefined) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst rect = {\n\t\t\t\t\tx: xScale.map(xValue) + inset,\n\t\t\t\t\ty: yScale.map(yValue) + inset,\n\t\t\t\t\twidth: Math.max(0, xScale.bandwidth - inset * 2),\n\t\t\t\t\theight: Math.max(0, yScale.bandwidth - inset * 2),\n\t\t\t\t};\n\t\t\t\tconst series = seriesOf(index);\n\t\t\t\tconst colorValue = colors.values[index];\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"rect\",\n\t\t\t\t\tkey: `${id}:${index}`,\n\t\t\t\t\trole: \"mark\",\n\t\t\t\t\t...rect,\n\t\t\t\t\tcorners: [radius, radius, radius, radius],\n\t\t\t\t\tpaint: paint({ fill: series.color, opacity: options.opacity }),\n\t\t\t\t});\n\t\t\t\tif (options.tip
|
|
5
|
-
"mappings": "AAAA,SAAS,cAAc,aAAa,mBAAmB;AACvD,SAAS,mBAAmB;AAC5B;AAAA,EAEC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AAkBP,MAAM,gBAAgB;AACtB,MAAM,iBAAiB;AAMhB,SAAS,KACf,MACA,SACY;AACZ,QAAM,KAAK,YAAY,MAAM,QAAQ,CAAC;AACtC,QAAM,KAAK,YAAY,MAAM,QAAQ,CAAC;AACtC,QAAM,SAAS,aAAa,MAAM,OAAO;AACzC,SAAO;AAAA,IACN,IAAI,QAAQ;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,UAAU;AAAA,MACT,GAAG;AAAA,QACF,QAAQ,GAAG,OAAO,WAAW;AAAA,QAC7B,UAAU;AAAA,QACV,SAAS;AAAA,QACT,OAAO,aAAa,QAAQ,CAAC;AAAA,MAC9B;AAAA,MACA,GAAG;AAAA,QACF,QAAQ,GAAG,OAAO,WAAW;AAAA,QAC7B,UAAU;AAAA,QACV,SAAS;AAAA,QACT,OAAO,aAAa,QAAQ,CAAC;AAAA,MAC9B;AAAA,MACA,OAAO,OAAO;AAAA,IACf;AAAA,IACA,OAAO,SAAS;AACf,YAAM,KAAK,OAAO,QAAQ,IAAI,QAAQ,OAAO;AAC7C,YAAM,EAAE,GAAG,QAAQ,GAAG,OAAO,IAAI,QAAQ;AACzC,UAAI,WAAW,UAAa,WAAW,QAAW;AACjD,eAAO,EAAE,OAAO,CAAC,EAAE;AAAA,MACpB;AACA,YAAM,WAAW;AAAA,QAChB,OAAO;AAAA,QACP,EAAE,GAAG,SAAS,YAAY,QAAQ,KAAK;AAAA,QACvC;AAAA,QACA;AAAA,MACD;AACA,YAAM,QAAQ,QAAQ,SAAS;AAC/B,YAAM,SAAS,QAAQ,UAAU;AACjC,YAAM,QAAqB,CAAC;AAC5B,YAAM,SAAuB,CAAC;AAC9B,iBAAW,CAAC,OAAO,KAAK,KAAK,KAAK,QAAQ,GAAG;AAC5C,cAAM,SAAS,QAAQ,QAAQ,GAAG,KAAK,CAAC;AACxC,cAAM,SAAS,QAAQ,QAAQ,GAAG,KAAK,CAAC;AACxC,YAAI,WAAW,UAAa,WAAW,QAAW;AACjD;AAAA,QACD;AACA,cAAM,OAAO;AAAA,UACZ,GAAG,OAAO,IAAI,MAAM,IAAI;AAAA,UACxB,GAAG,OAAO,IAAI,MAAM,IAAI;AAAA,UACxB,OAAO,KAAK,IAAI,GAAG,OAAO,YAAY,QAAQ,CAAC;AAAA,UAC/C,QAAQ,KAAK,IAAI,GAAG,OAAO,YAAY,QAAQ,CAAC;AAAA,QACjD;AACA,cAAM,SAAS,SAAS,KAAK;AAC7B,cAAM,aAAa,OAAO,OAAO,KAAK;AACtC,cAAM,KAAK;AAAA,UACV,MAAM;AAAA,UACN,KAAK,GAAG,EAAE,IAAI,KAAK;AAAA,UACnB,MAAM;AAAA,UACN,GAAG;AAAA,UACH,SAAS,CAAC,QAAQ,QAAQ,QAAQ,MAAM;AAAA,UACxC,OAAO,MAAM,EAAE,MAAM,OAAO,OAAO,SAAS,QAAQ,QAAQ,CAAC;AAAA,QAC9D,CAAC;AACD,YAAI,
|
|
4
|
+
"sourcesContent": ["import { channelLabel, isPlaceable, readChannel } from \"#/core/channel.ts\";\nimport { formatValue } from \"#/core/format.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\tChartValue,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\nexport interface CellOptions<TDatum> extends ChartMarkOptions<TDatum> {\n\treadonly x?: ChartAccessor<TDatum, ChartValue>;\n\treadonly y?: ChartAccessor<TDatum, ChartValue>;\n\treadonly fill?: string;\n\t/** Pixels kept clear around each cell. */\n\treadonly inset?: number;\n\treadonly radius?: number;\n}\n\nconst DEFAULT_INSET = 1;\nconst DEFAULT_RADIUS = 2;\n\n/**\n * A rectangle per (x, y) pair of categories, filled by its color value: a\n * heatmap, a calendar, a matrix.\n */\nexport function cell<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: CellOptions<TDatum>,\n): ChartMark {\n\tconst xs = readChannel(data, options.x);\n\tconst ys = readChannel(data, options.y);\n\tconst colors = colorChannel(data, options);\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"cartesian\",\n\t\tfocusOrder: \"point\",\n\t\tchannels: {\n\t\t\tx: {\n\t\t\t\tvalues: xs.filter(isPlaceable),\n\t\t\t\tdiscrete: \"band\",\n\t\t\t\tpadding: 0,\n\t\t\t\tlabel: channelLabel(options.x),\n\t\t\t},\n\t\t\ty: {\n\t\t\t\tvalues: ys.filter(isPlaceable),\n\t\t\t\tdiscrete: \"band\",\n\t\t\t\tpadding: 0,\n\t\t\t\tlabel: channelLabel(options.y),\n\t\t\t},\n\t\t\tcolor: colors.channel,\n\t\t},\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"cell\", context);\n\t\t\tconst { x: xScale, y: yScale } = context.scales;\n\t\t\tif (xScale === undefined || yScale === undefined) {\n\t\t\t\treturn { nodes: [] };\n\t\t\t}\n\t\t\tconst seriesOf = seriesResolver(\n\t\t\t\tcolors.values,\n\t\t\t\t{ ...options, fixedColor: options.fill },\n\t\t\t\tid,\n\t\t\t\tcontext,\n\t\t\t);\n\t\t\tconst inset = options.inset ?? DEFAULT_INSET;\n\t\t\tconst radius = options.radius ?? DEFAULT_RADIUS;\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 xValue = valueOn(xScale, xs[index]);\n\t\t\t\tconst yValue = valueOn(yScale, ys[index]);\n\t\t\t\tif (xValue === undefined || yValue === undefined) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst rect = {\n\t\t\t\t\tx: xScale.map(xValue) + inset,\n\t\t\t\t\ty: yScale.map(yValue) + inset,\n\t\t\t\t\twidth: Math.max(0, xScale.bandwidth - inset * 2),\n\t\t\t\t\theight: Math.max(0, yScale.bandwidth - inset * 2),\n\t\t\t\t};\n\t\t\t\tconst series = seriesOf(index);\n\t\t\t\tconst colorValue = colors.values[index];\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"rect\",\n\t\t\t\t\tkey: `${id}:${index}`,\n\t\t\t\t\trole: \"mark\",\n\t\t\t\t\t...rect,\n\t\t\t\t\tcorners: [radius, radius, radius, radius],\n\t\t\t\t\tpaint: paint({ fill: series.color, opacity: options.opacity }),\n\t\t\t\t});\n\t\t\t\tif (tips(options.tip, datum, index)) {\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: rect.x + rect.width / 2,\n\t\t\t\t\t\ty: rect.y + rect.height / 2,\n\t\t\t\t\t\txValue,\n\t\t\t\t\t\tyValue,\n\t\t\t\t\t\tcolor: series.color,\n\t\t\t\t\t\tseriesLabel: options.label,\n\t\t\t\t\t\ttitle: `${context.formatX(xValue)} \u00B7 ${context.formatY(yValue)}`,\n\t\t\t\t\t\tvalue:\n\t\t\t\t\t\t\tcolorValue === undefined\n\t\t\t\t\t\t\t\t? \"\"\n\t\t\t\t\t\t\t\t: formatValue(colorValue, context.locale),\n\t\t\t\t\t\thit: { kind: \"rect\", rect },\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"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dot.d.ts","sourceRoot":"","sources":["../../../src/core/marks/dot.ts"],"names":[],"mappings":"AAMA,OAAO,EACN,KAAK,gBAAgB,
|
|
1
|
+
{"version":3,"file":"dot.d.ts","sourceRoot":"","sources":["../../../src/core/marks/dot.ts"],"names":[],"mappings":"AAMA,OAAO,EACN,KAAK,gBAAgB,EAOrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EACX,aAAa,EACb,SAAS,EAET,UAAU,EAEV,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,UAAU,CAAC,MAAM,CAAE,SAAQ,gBAAgB,CAAC,MAAM,CAAC;IACnE,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,4EAA4E;IAC5E,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACxD,8CAA8C;IAC9C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAKD,8EAA8E;AAC9E,wBAAgB,GAAG,CAAC,MAAM,EACzB,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,OAAO,GAAE,UAAU,CAAC,MAAM,CAAM,GAC9B,SAAS,CAkHX"}
|
package/dist/core/marks/dot.js
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
markId,
|
|
10
10
|
paint,
|
|
11
11
|
seriesResolver,
|
|
12
|
+
tips,
|
|
12
13
|
valueOn
|
|
13
14
|
} from "./shared.js";
|
|
14
15
|
const DEFAULT_RADIUS = 4;
|
|
@@ -93,7 +94,7 @@ function dot(data, options = {}) {
|
|
|
93
94
|
opacity: options.opacity
|
|
94
95
|
})
|
|
95
96
|
});
|
|
96
|
-
if (options.tip
|
|
97
|
+
if (tips(options.tip, datum, index)) {
|
|
97
98
|
points.push({
|
|
98
99
|
key: `${id}:${index}`,
|
|
99
100
|
markId: id,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/core/marks/dot.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n\tchannelLabel,\n\tisPlaceable,\n\tnumericValue,\n\treadChannel,\n} from \"#/core/channel.ts\";\nimport {\n\ttype ChartMarkOptions,\n\tcolorChannel,\n\tmarkId,\n\tpaint,\n\tseriesResolver,\n\tvalueOn,\n} from \"#/core/marks/shared.ts\";\nimport type {\n\tChartAccessor,\n\tChartMark,\n\tChartPoint,\n\tChartValue,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\nexport interface DotOptions<TDatum> extends ChartMarkOptions<TDatum> {\n\treadonly x?: ChartAccessor<TDatum, ChartValue>;\n\treadonly y?: ChartAccessor<TDatum, ChartValue>;\n\t/** A radius in pixels, or a field whose values set the area of each dot. */\n\treadonly r?: number | ChartAccessor<TDatum, ChartValue>;\n\t/** The largest radius a sized dot reaches. */\n\treadonly maxRadius?: number;\n\treadonly fill?: string;\n\treadonly fillOpacity?: number;\n\treadonly stroke?: string;\n\treadonly strokeWidth?: number;\n}\n\nconst DEFAULT_RADIUS = 4;\nconst DEFAULT_MAX_RADIUS = 16;\n\n/** A dot per datum: a scatter plot, or a bubble chart when `r` is a field. */\nexport function dot<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: DotOptions<TDatum> = {},\n): ChartMark {\n\tconst xs = readChannel(\n\t\tdata,\n\t\toptions.x ?? ((_datum: TDatum, index: number) => index),\n\t);\n\tconst ys = readChannel(\n\t\tdata,\n\t\toptions.y ?? ((datum: TDatum) => datum as unknown as ChartValue),\n\t);\n\tconst colors = colorChannel(data, options);\n\tconst sized = options.r !== undefined && typeof options.r !== \"number\";\n\tconst sizes = sized\n\t\t? readChannel(data, options.r as ChartAccessor<TDatum, ChartValue>).map(\n\t\t\t\tnumericValue,\n\t\t\t)\n\t\t: [];\n\tconst largest = Math.max(0, ...sizes.map((size) => Math.abs(size ?? 0)));\n\tconst reach =\n\t\ttypeof options.r === \"number\"\n\t\t\t? options.r\n\t\t\t: sized\n\t\t\t\t? (options.maxRadius ?? DEFAULT_MAX_RADIUS)\n\t\t\t\t: DEFAULT_RADIUS;\n\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"cartesian\",\n\t\tfocusOrder: \"point\",\n\t\tchannels: {\n\t\t\tx: {\n\t\t\t\tvalues: xs.filter(isPlaceable),\n\t\t\t\tlabel: channelLabel(options.x),\n\t\t\t\tinset: reach,\n\t\t\t},\n\t\t\ty: {\n\t\t\t\tvalues: ys.filter(isPlaceable),\n\t\t\t\tlabel: channelLabel(options.y) ?? options.label,\n\t\t\t\tinset: reach,\n\t\t\t},\n\t\t\tcolor: colors.channel,\n\t\t},\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"dot\", context);\n\t\t\tconst { x: xScale, y: yScale } = context.scales;\n\t\t\tif (xScale === undefined || yScale === undefined) {\n\t\t\t\treturn { nodes: [] };\n\t\t\t}\n\t\t\tconst seriesOf = seriesResolver(\n\t\t\t\tcolors.values,\n\t\t\t\t{ ...options, fixedColor: options.fill },\n\t\t\t\tid,\n\t\t\t\tcontext,\n\t\t\t);\n\t\t\tconst maxRadius = options.maxRadius ?? DEFAULT_MAX_RADIUS;\n\t\t\t// Area, not radius, carries the value: a dot twice the value looks twice as big.\n\t\t\tfunction radiusOf(index: number): number {\n\t\t\t\tif (typeof options.r === \"number\") return options.r;\n\t\t\t\tif (!sized) return DEFAULT_RADIUS;\n\t\t\t\tconst size = Math.abs(sizes[index] ?? 0);\n\t\t\t\treturn largest === 0 ? 0 : Math.sqrt(size / largest) * maxRadius;\n\t\t\t}\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 xValue = valueOn(xScale, xs[index]);\n\t\t\t\tconst yValue = valueOn(yScale, ys[index]);\n\t\t\t\tif (xValue === undefined || yValue === undefined) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst series = seriesOf(index);\n\t\t\t\tconst cx = xScale.center(xValue);\n\t\t\t\tconst cy = yScale.center(yValue);\n\t\t\t\tconst r = radiusOf(index);\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"circle\",\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\tcx,\n\t\t\t\t\tcy,\n\t\t\t\t\tr,\n\t\t\t\t\tpaint: paint({\n\t\t\t\t\t\tfill: series.color,\n\t\t\t\t\t\tfillOpacity: options.fillOpacity,\n\t\t\t\t\t\tstroke: options.stroke,\n\t\t\t\t\t\tstrokeWidth: options.strokeWidth,\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 (options.tip
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import {\n\tchannelLabel,\n\tisPlaceable,\n\tnumericValue,\n\treadChannel,\n} 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\tChartValue,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\nexport interface DotOptions<TDatum> extends ChartMarkOptions<TDatum> {\n\treadonly x?: ChartAccessor<TDatum, ChartValue>;\n\treadonly y?: ChartAccessor<TDatum, ChartValue>;\n\t/** A radius in pixels, or a field whose values set the area of each dot. */\n\treadonly r?: number | ChartAccessor<TDatum, ChartValue>;\n\t/** The largest radius a sized dot reaches. */\n\treadonly maxRadius?: number;\n\treadonly fill?: string;\n\treadonly fillOpacity?: number;\n\treadonly stroke?: string;\n\treadonly strokeWidth?: number;\n}\n\nconst DEFAULT_RADIUS = 4;\nconst DEFAULT_MAX_RADIUS = 16;\n\n/** A dot per datum: a scatter plot, or a bubble chart when `r` is a field. */\nexport function dot<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: DotOptions<TDatum> = {},\n): ChartMark {\n\tconst xs = readChannel(\n\t\tdata,\n\t\toptions.x ?? ((_datum: TDatum, index: number) => index),\n\t);\n\tconst ys = readChannel(\n\t\tdata,\n\t\toptions.y ?? ((datum: TDatum) => datum as unknown as ChartValue),\n\t);\n\tconst colors = colorChannel(data, options);\n\tconst sized = options.r !== undefined && typeof options.r !== \"number\";\n\tconst sizes = sized\n\t\t? readChannel(data, options.r as ChartAccessor<TDatum, ChartValue>).map(\n\t\t\t\tnumericValue,\n\t\t\t)\n\t\t: [];\n\tconst largest = Math.max(0, ...sizes.map((size) => Math.abs(size ?? 0)));\n\tconst reach =\n\t\ttypeof options.r === \"number\"\n\t\t\t? options.r\n\t\t\t: sized\n\t\t\t\t? (options.maxRadius ?? DEFAULT_MAX_RADIUS)\n\t\t\t\t: DEFAULT_RADIUS;\n\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"cartesian\",\n\t\tfocusOrder: \"point\",\n\t\tchannels: {\n\t\t\tx: {\n\t\t\t\tvalues: xs.filter(isPlaceable),\n\t\t\t\tlabel: channelLabel(options.x),\n\t\t\t\tinset: reach,\n\t\t\t},\n\t\t\ty: {\n\t\t\t\tvalues: ys.filter(isPlaceable),\n\t\t\t\tlabel: channelLabel(options.y) ?? options.label,\n\t\t\t\tinset: reach,\n\t\t\t},\n\t\t\tcolor: colors.channel,\n\t\t},\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"dot\", context);\n\t\t\tconst { x: xScale, y: yScale } = context.scales;\n\t\t\tif (xScale === undefined || yScale === undefined) {\n\t\t\t\treturn { nodes: [] };\n\t\t\t}\n\t\t\tconst seriesOf = seriesResolver(\n\t\t\t\tcolors.values,\n\t\t\t\t{ ...options, fixedColor: options.fill },\n\t\t\t\tid,\n\t\t\t\tcontext,\n\t\t\t);\n\t\t\tconst maxRadius = options.maxRadius ?? DEFAULT_MAX_RADIUS;\n\t\t\t// Area, not radius, carries the value: a dot twice the value looks twice as big.\n\t\t\tfunction radiusOf(index: number): number {\n\t\t\t\tif (typeof options.r === \"number\") return options.r;\n\t\t\t\tif (!sized) return DEFAULT_RADIUS;\n\t\t\t\tconst size = Math.abs(sizes[index] ?? 0);\n\t\t\t\treturn largest === 0 ? 0 : Math.sqrt(size / largest) * maxRadius;\n\t\t\t}\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 xValue = valueOn(xScale, xs[index]);\n\t\t\t\tconst yValue = valueOn(yScale, ys[index]);\n\t\t\t\tif (xValue === undefined || yValue === undefined) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst series = seriesOf(index);\n\t\t\t\tconst cx = xScale.center(xValue);\n\t\t\t\tconst cy = yScale.center(yValue);\n\t\t\t\tconst r = radiusOf(index);\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"circle\",\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\tcx,\n\t\t\t\t\tcy,\n\t\t\t\t\tr,\n\t\t\t\t\tpaint: paint({\n\t\t\t\t\t\tfill: series.color,\n\t\t\t\t\t\tfillOpacity: options.fillOpacity,\n\t\t\t\t\t\tstroke: options.stroke,\n\t\t\t\t\t\tstrokeWidth: options.strokeWidth,\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\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: cx,\n\t\t\t\t\t\ty: cy,\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: context.formatX(xValue),\n\t\t\t\t\t\tvalue: context.formatY(yValue),\n\t\t\t\t\t\thit: {\n\t\t\t\t\t\t\tkind: \"rect\",\n\t\t\t\t\t\t\trect: { x: cx - r, y: cy - r, width: r * 2, height: r * 2 },\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"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"funnel.d.ts","sourceRoot":"","sources":["../../../src/core/marks/funnel.ts"],"names":[],"mappings":"AAQA,OAAO,EACN,KAAK,gBAAgB,
|
|
1
|
+
{"version":3,"file":"funnel.d.ts","sourceRoot":"","sources":["../../../src/core/marks/funnel.ts"],"names":[],"mappings":"AAQA,OAAO,EACN,KAAK,gBAAgB,EAKrB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EACX,aAAa,EACb,SAAS,EAET,UAAU,EAEV,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,aAAa,CAAC,MAAM,CAAE,SAAQ,gBAAgB,CAAC,MAAM,CAAC;IACtE,uDAAuD;IACvD,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACrD,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAClD,yCAAyC;IACzC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC1B;AAKD;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAC5B,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,GAC5B,SAAS,CA8HX"}
|
|
@@ -9,7 +9,8 @@ import { funnelSlices } from "./funnel-layout.js";
|
|
|
9
9
|
import {
|
|
10
10
|
markId,
|
|
11
11
|
paint,
|
|
12
|
-
seriesResolver
|
|
12
|
+
seriesResolver,
|
|
13
|
+
tips
|
|
13
14
|
} from "./shared.js";
|
|
14
15
|
import { categoryKey } from "../scales/discrete.js";
|
|
15
16
|
const LABEL_ZONE = 36;
|
|
@@ -103,7 +104,7 @@ function funnel(data, options) {
|
|
|
103
104
|
}
|
|
104
105
|
);
|
|
105
106
|
}
|
|
106
|
-
if (options.tip
|
|
107
|
+
if (!tips(options.tip, data[slice.index], slice.index)) {
|
|
107
108
|
continue;
|
|
108
109
|
}
|
|
109
110
|
const previous = slice.index > 0 ? values[slice.index - 1] : void 0;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/core/marks/funnel.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n\tchannelLabel,\n\tisPlaceable,\n\tnumericValue,\n\treadChannel,\n} from \"#/core/channel.ts\";\nimport { formatPercentage } from \"#/core/format.ts\";\nimport { funnelSlices } from \"#/core/marks/funnel-layout.ts\";\nimport {\n\ttype ChartMarkOptions,\n\tmarkId,\n\tpaint,\n\tseriesResolver,\n} from \"#/core/marks/shared.ts\";\nimport { categoryKey } from \"#/core/scales/discrete.ts\";\nimport type {\n\tChartAccessor,\n\tChartMark,\n\tChartPoint,\n\tChartValue,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\nexport interface FunnelOptions<TDatum> extends ChartMarkOptions<TDatum> {\n\t/** The step: \"Visites\", \"Inscriptions\", \"Missions\". */\n\treadonly category: ChartAccessor<TDatum, ChartValue>;\n\treadonly value: ChartAccessor<TDatum, ChartValue>;\n\t/** Pixels of blank between two steps. */\n\treadonly gap?: number;\n\treadonly fill?: string;\n\t/** `false` drops the step name and value written above each step. */\n\treadonly labels?: boolean;\n}\n\n/** Room kept above the shape for each step's name and value. */\nconst LABEL_ZONE = 36;\n\n/**\n * Steps left to right, each as tall as its value, each sloping down to the\n * next: the drop between two stages is the shape itself. The tooltip gives\n * the conversion from the step before.\n */\nexport function funnel<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: FunnelOptions<TDatum>,\n): ChartMark {\n\tconst categories = readChannel(data, options.category);\n\tconst values = readChannel(data, options.value).map(\n\t\t(value) => numericValue(value) ?? 0,\n\t);\n\tconst colorAccessor = options.color ?? options.category;\n\tconst colors = readChannel(data, colorAccessor);\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"frame\",\n\t\tfocusOrder: \"point\",\n\t\tcolorLegend: options.labels === false,\n\t\ttitles: {\n\t\t\tcategory: channelLabel(options.category),\n\t\t\tvalue: channelLabel(options.value) ?? options.label,\n\t\t},\n\t\tchannels: {\n\t\t\tcolor: {\n\t\t\t\tvalues: colors.filter(isPlaceable),\n\t\t\t\tlabel: channelLabel(colorAccessor),\n\t\t\t},\n\t\t},\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"funnel\", context);\n\t\t\tconst { plot, theme } = context;\n\t\t\tconst labelled = options.labels !== false;\n\t\t\tconst top = plot.y + (labelled ? LABEL_ZONE : 0);\n\t\t\tconst height = Math.max(0, plot.height - (labelled ? LABEL_ZONE : 0));\n\t\t\tconst slices = funnelSlices({\n\t\t\t\tvalues,\n\t\t\t\tinnerWidth: plot.width,\n\t\t\t\tinnerHeight: height,\n\t\t\t\tcenterY: top + height / 2,\n\t\t\t\toriginX: plot.x,\n\t\t\t\tgap: options.gap,\n\t\t\t});\n\t\t\tconst seriesOf = seriesResolver(\n\t\t\t\tcolors,\n\t\t\t\t{ ...options, color: colorAccessor, fixedColor: options.fill },\n\t\t\t\tid,\n\t\t\t\tcontext,\n\t\t\t);\n\t\t\tconst nodes: SceneNode[] = [];\n\t\t\tconst points: ChartPoint[] = [];\n\t\t\tfor (const slice of slices) {\n\t\t\t\tconst category = categories[slice.index];\n\t\t\t\tif (!isPlaceable(category) || slice.path === \"\") {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst series = seriesOf(slice.index);\n\t\t\t\tconst key = categoryKey(category);\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"path\",\n\t\t\t\t\tkey: `${id}:${key}`,\n\t\t\t\t\tseries: series.key,\n\t\t\t\t\trole: \"mark\",\n\t\t\t\t\td: slice.path,\n\t\t\t\t\tpaint: paint({ fill: series.color, opacity: options.opacity }),\n\t\t\t\t});\n\t\t\t\tif (labelled) {\n\t\t\t\t\tnodes.push(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tkind: \"text\",\n\t\t\t\t\t\t\tkey: `${id}:name:${key}`,\n\t\t\t\t\t\t\trole: \"label\",\n\t\t\t\t\t\t\tx: slice.x,\n\t\t\t\t\t\t\ty: plot.y,\n\t\t\t\t\t\t\ttext: series.label ?? context.formatX(category),\n\t\t\t\t\t\t\tpaint: {\n\t\t\t\t\t\t\t\tfill: theme.muted,\n\t\t\t\t\t\t\t\tfontSize: theme.fontSize,\n\t\t\t\t\t\t\t\tbaseline: \"top\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tkind: \"text\",\n\t\t\t\t\t\t\tkey: `${id}:value:${key}`,\n\t\t\t\t\t\t\trole: \"label\",\n\t\t\t\t\t\t\tx: slice.x,\n\t\t\t\t\t\t\ty: plot.y + theme.fontSize + 4,\n\t\t\t\t\t\t\ttext: context.formatY(slice.value),\n\t\t\t\t\t\t\tpaint: {\n\t\t\t\t\t\t\t\tfill: theme.foreground,\n\t\t\t\t\t\t\t\tfontSize: theme.fontSize + 2,\n\t\t\t\t\t\t\t\tfontWeight: 600,\n\t\t\t\t\t\t\t\tbaseline: \"top\",\n\t\t\t\t\t\t\t},\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
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import {\n\tchannelLabel,\n\tisPlaceable,\n\tnumericValue,\n\treadChannel,\n} from \"#/core/channel.ts\";\nimport { formatPercentage } from \"#/core/format.ts\";\nimport { funnelSlices } from \"#/core/marks/funnel-layout.ts\";\nimport {\n\ttype ChartMarkOptions,\n\tmarkId,\n\tpaint,\n\tseriesResolver,\n\ttips,\n} from \"#/core/marks/shared.ts\";\nimport { categoryKey } from \"#/core/scales/discrete.ts\";\nimport type {\n\tChartAccessor,\n\tChartMark,\n\tChartPoint,\n\tChartValue,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\nexport interface FunnelOptions<TDatum> extends ChartMarkOptions<TDatum> {\n\t/** The step: \"Visites\", \"Inscriptions\", \"Missions\". */\n\treadonly category: ChartAccessor<TDatum, ChartValue>;\n\treadonly value: ChartAccessor<TDatum, ChartValue>;\n\t/** Pixels of blank between two steps. */\n\treadonly gap?: number;\n\treadonly fill?: string;\n\t/** `false` drops the step name and value written above each step. */\n\treadonly labels?: boolean;\n}\n\n/** Room kept above the shape for each step's name and value. */\nconst LABEL_ZONE = 36;\n\n/**\n * Steps left to right, each as tall as its value, each sloping down to the\n * next: the drop between two stages is the shape itself. The tooltip gives\n * the conversion from the step before.\n */\nexport function funnel<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: FunnelOptions<TDatum>,\n): ChartMark {\n\tconst categories = readChannel(data, options.category);\n\tconst values = readChannel(data, options.value).map(\n\t\t(value) => numericValue(value) ?? 0,\n\t);\n\tconst colorAccessor = options.color ?? options.category;\n\tconst colors = readChannel(data, colorAccessor);\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"frame\",\n\t\tfocusOrder: \"point\",\n\t\tcolorLegend: options.labels === false,\n\t\ttitles: {\n\t\t\tcategory: channelLabel(options.category),\n\t\t\tvalue: channelLabel(options.value) ?? options.label,\n\t\t},\n\t\tchannels: {\n\t\t\tcolor: {\n\t\t\t\tvalues: colors.filter(isPlaceable),\n\t\t\t\tlabel: channelLabel(colorAccessor),\n\t\t\t},\n\t\t},\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"funnel\", context);\n\t\t\tconst { plot, theme } = context;\n\t\t\tconst labelled = options.labels !== false;\n\t\t\tconst top = plot.y + (labelled ? LABEL_ZONE : 0);\n\t\t\tconst height = Math.max(0, plot.height - (labelled ? LABEL_ZONE : 0));\n\t\t\tconst slices = funnelSlices({\n\t\t\t\tvalues,\n\t\t\t\tinnerWidth: plot.width,\n\t\t\t\tinnerHeight: height,\n\t\t\t\tcenterY: top + height / 2,\n\t\t\t\toriginX: plot.x,\n\t\t\t\tgap: options.gap,\n\t\t\t});\n\t\t\tconst seriesOf = seriesResolver(\n\t\t\t\tcolors,\n\t\t\t\t{ ...options, color: colorAccessor, fixedColor: options.fill },\n\t\t\t\tid,\n\t\t\t\tcontext,\n\t\t\t);\n\t\t\tconst nodes: SceneNode[] = [];\n\t\t\tconst points: ChartPoint[] = [];\n\t\t\tfor (const slice of slices) {\n\t\t\t\tconst category = categories[slice.index];\n\t\t\t\tif (!isPlaceable(category) || slice.path === \"\") {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst series = seriesOf(slice.index);\n\t\t\t\tconst key = categoryKey(category);\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"path\",\n\t\t\t\t\tkey: `${id}:${key}`,\n\t\t\t\t\tseries: series.key,\n\t\t\t\t\trole: \"mark\",\n\t\t\t\t\td: slice.path,\n\t\t\t\t\tpaint: paint({ fill: series.color, opacity: options.opacity }),\n\t\t\t\t});\n\t\t\t\tif (labelled) {\n\t\t\t\t\tnodes.push(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tkind: \"text\",\n\t\t\t\t\t\t\tkey: `${id}:name:${key}`,\n\t\t\t\t\t\t\trole: \"label\",\n\t\t\t\t\t\t\tx: slice.x,\n\t\t\t\t\t\t\ty: plot.y,\n\t\t\t\t\t\t\ttext: series.label ?? context.formatX(category),\n\t\t\t\t\t\t\tpaint: {\n\t\t\t\t\t\t\t\tfill: theme.muted,\n\t\t\t\t\t\t\t\tfontSize: theme.fontSize,\n\t\t\t\t\t\t\t\tbaseline: \"top\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tkind: \"text\",\n\t\t\t\t\t\t\tkey: `${id}:value:${key}`,\n\t\t\t\t\t\t\trole: \"label\",\n\t\t\t\t\t\t\tx: slice.x,\n\t\t\t\t\t\t\ty: plot.y + theme.fontSize + 4,\n\t\t\t\t\t\t\ttext: context.formatY(slice.value),\n\t\t\t\t\t\t\tpaint: {\n\t\t\t\t\t\t\t\tfill: theme.foreground,\n\t\t\t\t\t\t\t\tfontSize: theme.fontSize + 2,\n\t\t\t\t\t\t\t\tfontWeight: 600,\n\t\t\t\t\t\t\t\tbaseline: \"top\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (!tips(options.tip, data[slice.index], slice.index)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst previous = slice.index > 0 ? values[slice.index - 1] : undefined;\n\t\t\t\tconst conversion =\n\t\t\t\t\tprevious !== undefined && previous > 0\n\t\t\t\t\t\t? ` (${formatPercentage(slice.value / previous, context.locale)})`\n\t\t\t\t\t\t: \"\";\n\t\t\t\tconst centerY = top + height / 2;\n\t\t\t\tpoints.push({\n\t\t\t\t\tkey: `${id}:${key}`,\n\t\t\t\t\tmarkId: id,\n\t\t\t\t\tindex: slice.index,\n\t\t\t\t\tdatum: data[slice.index],\n\t\t\t\t\tx: slice.x + slice.width / 2,\n\t\t\t\t\ty: centerY,\n\t\t\t\t\txValue: category,\n\t\t\t\t\tyValue: slice.value,\n\t\t\t\t\tseries: series.key,\n\t\t\t\t\tcolor: series.color,\n\t\t\t\t\ttitle: series.label ?? context.formatX(category),\n\t\t\t\t\tvalue: `${context.formatY(slice.value)}${conversion}`,\n\t\t\t\t\thit: {\n\t\t\t\t\t\tkind: \"rect\",\n\t\t\t\t\t\trect: {\n\t\t\t\t\t\t\tx: slice.x,\n\t\t\t\t\t\t\ty: centerY - slice.leftHeight / 2,\n\t\t\t\t\t\t\twidth: slice.width,\n\t\t\t\t\t\t\theight: slice.leftHeight,\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"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"line.d.ts","sourceRoot":"","sources":["../../../src/core/marks/line.ts"],"names":[],"mappings":"AACA,OAAO,EACN,KAAK,gBAAgB,
|
|
1
|
+
{"version":3,"file":"line.d.ts","sourceRoot":"","sources":["../../../src/core/marks/line.ts"],"names":[],"mappings":"AACA,OAAO,EACN,KAAK,gBAAgB,EAQrB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EACX,aAAa,EACb,UAAU,EACV,SAAS,EAET,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,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAC5B,yDAAyD;IACzD,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;CACxB;AAkKD,8EAA8E;AAC9E,wBAAgB,KAAK,CAAC,MAAM,EAC3B,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,OAAO,GAAE,WAAW,CAAC,MAAM,CAAM,GAC/B,SAAS,CAEX;AAED,yDAAyD;AACzD,wBAAgB,KAAK,CAAC,MAAM,EAC3B,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,OAAO,GAAE,WAAW,CAAC,MAAM,CAAM,GAC/B,SAAS,CAEX"}
|
package/dist/core/marks/line.js
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
markId,
|
|
6
6
|
paint,
|
|
7
7
|
seriesResolver,
|
|
8
|
+
tips,
|
|
8
9
|
valueOn
|
|
9
10
|
} from "./shared.js";
|
|
10
11
|
import { linePath } from "../paths.js";
|
|
@@ -72,7 +73,7 @@ function lineMark(kind, data, options) {
|
|
|
72
73
|
const across = valueScale.center(value);
|
|
73
74
|
const xy = vertical ? { x: along, y: across } : { x: across, y: along };
|
|
74
75
|
runs[runs.length - 1].push(xy);
|
|
75
|
-
if (options.tip
|
|
76
|
+
if (tips(options.tip, data[index], index)) {
|
|
76
77
|
points.push({
|
|
77
78
|
key: `${id}:${index}`,
|
|
78
79
|
markId: id,
|