ggaction 0.0.1
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/CHANGELOG.md +27 -0
- package/LICENSE +21 -0
- package/README.md +108 -0
- package/package.json +84 -0
- package/src/AGENTS.md +119 -0
- package/src/ChartProgram.js +10 -0
- package/src/actions/boxPlots/components.js +88 -0
- package/src/actions/boxPlots/create.js +84 -0
- package/src/actions/boxPlots/index.js +10 -0
- package/src/actions/boxPlots/materialize.js +183 -0
- package/src/actions/boxPlots/options.js +147 -0
- package/src/actions/boxPlots/resolve.js +38 -0
- package/src/actions/canvas/actions.js +144 -0
- package/src/actions/canvas/index.js +6 -0
- package/src/actions/coordinates/actions.js +85 -0
- package/src/actions/coordinates/index.js +5 -0
- package/src/actions/data/box.js +40 -0
- package/src/actions/data/create.js +33 -0
- package/src/actions/data/density.js +62 -0
- package/src/actions/data/derived.js +52 -0
- package/src/actions/data/filter.js +168 -0
- package/src/actions/data/index.js +32 -0
- package/src/actions/data/interval.js +69 -0
- package/src/actions/data/regression.js +71 -0
- package/src/actions/data/shared.js +25 -0
- package/src/actions/encodings/appearance.js +243 -0
- package/src/actions/encodings/barWidth.js +60 -0
- package/src/actions/encodings/color.js +329 -0
- package/src/actions/encodings/density.js +225 -0
- package/src/actions/encodings/histogram.js +50 -0
- package/src/actions/encodings/index.js +23 -0
- package/src/actions/encodings/offset.js +104 -0
- package/src/actions/encodings/position/policies/area.js +24 -0
- package/src/actions/encodings/position/policies/bar.js +110 -0
- package/src/actions/encodings/position/policies/common.js +17 -0
- package/src/actions/encodings/position/policies/index.js +25 -0
- package/src/actions/encodings/position/policies/line.js +74 -0
- package/src/actions/encodings/position/policies/point.js +19 -0
- package/src/actions/encodings/position/policies/rule.js +17 -0
- package/src/actions/encodings/position/resolve.js +170 -0
- package/src/actions/encodings/position.js +176 -0
- package/src/actions/encodings/ranged.js +276 -0
- package/src/actions/encodings/ruleAppearance.js +47 -0
- package/src/actions/encodings/shared.js +164 -0
- package/src/actions/encodings/strokeDash.js +145 -0
- package/src/actions/errorBands/components.js +81 -0
- package/src/actions/errorBands/create.js +159 -0
- package/src/actions/errorBands/index.js +7 -0
- package/src/actions/errorBands/resolve.js +28 -0
- package/src/actions/errorBars/components.js +62 -0
- package/src/actions/errorBars/create.js +144 -0
- package/src/actions/errorBars/index.js +7 -0
- package/src/actions/errorBars/resolve.js +25 -0
- package/src/actions/guides/axes/axes.js +204 -0
- package/src/actions/guides/axes/axis.js +131 -0
- package/src/actions/guides/axes/index.js +17 -0
- package/src/actions/guides/axes/labels.js +231 -0
- package/src/actions/guides/axes/lines.js +149 -0
- package/src/actions/guides/axes/policy.js +155 -0
- package/src/actions/guides/axes/tickGroups.js +149 -0
- package/src/actions/guides/axes/ticks.js +179 -0
- package/src/actions/guides/axes/titles.js +264 -0
- package/src/actions/guides/grids/grid.js +228 -0
- package/src/actions/guides/grids/index.js +1 -0
- package/src/actions/guides/grids/resolve.js +258 -0
- package/src/actions/guides/guides.js +138 -0
- package/src/actions/guides/index.js +11 -0
- package/src/actions/guides/legends/categorical/actions.js +329 -0
- package/src/actions/guides/legends/categorical/components.js +140 -0
- package/src/actions/guides/legends/categorical/index.js +49 -0
- package/src/actions/guides/legends/categorical/layout.js +445 -0
- package/src/actions/guides/legends/categorical/options.js +188 -0
- package/src/actions/guides/legends/categorical/recipes.js +120 -0
- package/src/actions/guides/legends/categorical/resolve.js +131 -0
- package/src/actions/guides/legends/categorical/symbols.js +280 -0
- package/src/actions/guides/legends/categorical/validation.js +27 -0
- package/src/actions/guides/legends/continuous/common.js +273 -0
- package/src/actions/guides/legends/continuous/gradient.js +363 -0
- package/src/actions/guides/legends/continuous/index.js +25 -0
- package/src/actions/guides/legends/continuous/interval.js +234 -0
- package/src/actions/guides/legends/continuous/opacity.js +382 -0
- package/src/actions/guides/legends/edit.js +314 -0
- package/src/actions/guides/legends/index.js +9 -0
- package/src/actions/guides/legends/size.js +220 -0
- package/src/actions/guides/tickValues.js +84 -0
- package/src/actions/index.js +31 -0
- package/src/actions/intervals/resolve.js +352 -0
- package/src/actions/marks/area.js +369 -0
- package/src/actions/marks/bar/create.js +46 -0
- package/src/actions/marks/bar/edit.js +90 -0
- package/src/actions/marks/bar/index.js +9 -0
- package/src/actions/marks/bar/materialize.js +130 -0
- package/src/actions/marks/index.js +13 -0
- package/src/actions/marks/line.js +248 -0
- package/src/actions/marks/point.js +420 -0
- package/src/actions/marks/rule.js +287 -0
- package/src/actions/marks/shared.js +112 -0
- package/src/actions/primitives/createGraphics.js +163 -0
- package/src/actions/primitives/editGraphics.js +208 -0
- package/src/actions/primitives/index.js +9 -0
- package/src/actions/primitives/semantic.js +168 -0
- package/src/actions/primitives/semanticValue.js +182 -0
- package/src/actions/regression/components.js +216 -0
- package/src/actions/regression/create.js +102 -0
- package/src/actions/regression/index.js +23 -0
- package/src/actions/regression/resolve.js +57 -0
- package/src/actions/scales/consumers.js +283 -0
- package/src/actions/scales/create.js +148 -0
- package/src/actions/scales/definitions.js +319 -0
- package/src/actions/scales/edit.js +290 -0
- package/src/actions/scales/index.js +11 -0
- package/src/actions/scales/materialize.js +466 -0
- package/src/actions/scales/quantitativeColor.js +45 -0
- package/src/actions/selection/actions.js +384 -0
- package/src/actions/selection/index.js +23 -0
- package/src/actions/titles/actions.js +235 -0
- package/src/actions/titles/index.js +19 -0
- package/src/actions/titles/resolve.js +321 -0
- package/src/core/ChartProgram.js +316 -0
- package/src/core/action.js +120 -0
- package/src/core/identifiers.js +28 -0
- package/src/core/immutable.js +94 -0
- package/src/core/specs.js +29 -0
- package/src/core/textMetrics.js +23 -0
- package/src/core/validation.js +54 -0
- package/src/core/vocabulary.js +58 -0
- package/src/extension.js +2 -0
- package/src/grammar/aggregate.js +259 -0
- package/src/grammar/areaSeries.js +217 -0
- package/src/grammar/bars/aggregate.js +98 -0
- package/src/grammar/bars/geometry.js +54 -0
- package/src/grammar/bars/policy.js +90 -0
- package/src/grammar/boxPlot.js +140 -0
- package/src/grammar/coordinates.js +25 -0
- package/src/grammar/curveCommands.js +356 -0
- package/src/grammar/density.js +296 -0
- package/src/grammar/filter.js +169 -0
- package/src/grammar/histogram.js +332 -0
- package/src/grammar/interval.js +343 -0
- package/src/grammar/lineSeries.js +197 -0
- package/src/grammar/markFilter.js +46 -0
- package/src/grammar/markSelection.js +326 -0
- package/src/grammar/palettes.js +208 -0
- package/src/grammar/pathCommands.js +86 -0
- package/src/grammar/pointShapes.js +164 -0
- package/src/grammar/positionCompatibility.js +32 -0
- package/src/grammar/regression.js +578 -0
- package/src/grammar/ruleAppearance.js +12 -0
- package/src/grammar/rules.js +90 -0
- package/src/grammar/scales/appearance.js +185 -0
- package/src/grammar/scales/color.js +349 -0
- package/src/grammar/scales/continuous.js +111 -0
- package/src/grammar/scales/definition.js +153 -0
- package/src/grammar/scales/discretized.js +159 -0
- package/src/grammar/scales/fields.js +133 -0
- package/src/grammar/scales/index.js +12 -0
- package/src/grammar/scales/mapping.js +27 -0
- package/src/grammar/scales/ordinal.js +221 -0
- package/src/grammar/scales/policies.js +50 -0
- package/src/grammar/scales/temporal.js +53 -0
- package/src/grammar/scales/transformed.js +236 -0
- package/src/grammar/scales/types.js +102 -0
- package/src/grammar/scales/validation.js +66 -0
- package/src/grammar/scales.js +1 -0
- package/src/grammar/schemas/concreteGraphic.js +73 -0
- package/src/grammar/schemas/graphic.js +131 -0
- package/src/grammar/schemas/graphicBounds.js +146 -0
- package/src/grammar/schemas/graphicTree.js +133 -0
- package/src/grammar/schemas/mark.js +4 -0
- package/src/grammar/schemas/semanticPath.js +174 -0
- package/src/grammar/seriesLayout.js +76 -0
- package/src/grammar/ticks.js +149 -0
- package/src/grammar/transforms.js +37 -0
- package/src/index.js +2 -0
- package/src/layout/canvas.js +114 -0
- package/src/layout/grid.js +17 -0
- package/src/layout/legend.js +51 -0
- package/src/layout/text.js +62 -0
- package/src/layout/title.js +107 -0
- package/src/materialization/bars/aggregate.js +184 -0
- package/src/materialization/bars/grouped.js +117 -0
- package/src/materialization/bars/histogram.js +163 -0
- package/src/materialization/bars/ranged.js +53 -0
- package/src/materialization/bars/resolve.js +56 -0
- package/src/materialization/dataProvenance.js +15 -0
- package/src/materialization/density.js +29 -0
- package/src/materialization/dependencies.js +134 -0
- package/src/materialization/encodings.js +50 -0
- package/src/materialization/graphicHierarchy.js +104 -0
- package/src/materialization/legends.js +20 -0
- package/src/materialization/marks.js +110 -0
- package/src/materialization/planner.js +38 -0
- package/src/materialization/selection/filter.js +40 -0
- package/src/materialization/selection/items.js +503 -0
- package/src/materialization/selection/path.js +37 -0
- package/src/materialization/selection/point.js +118 -0
- package/src/materialization/selection/policies/area.js +10 -0
- package/src/materialization/selection/policies/bar.js +10 -0
- package/src/materialization/selection/policies/index.js +40 -0
- package/src/materialization/selection/policies/line.js +10 -0
- package/src/materialization/selection/policies/point.js +10 -0
- package/src/materialization/selection/policies/rule.js +10 -0
- package/src/materialization/selection/state.js +67 -0
- package/src/materialization/selection/styles.js +183 -0
- package/src/renderers/canvas/circle.js +62 -0
- package/src/renderers/canvas/index.js +132 -0
- package/src/renderers/canvas/line.js +54 -0
- package/src/renderers/canvas/path.js +98 -0
- package/src/renderers/canvas/rect.js +57 -0
- package/src/renderers/canvas/text.js +96 -0
- package/src/renderers/canvas/validation.js +23 -0
- package/src/renderers/png.js +29 -0
- package/src/selectors/coordinates.js +13 -0
- package/src/selectors/datasets.js +13 -0
- package/src/selectors/index.js +4 -0
- package/src/selectors/layers.js +32 -0
- package/src/selectors/scales.js +21 -0
- package/src/theme/defaults.js +13 -0
- package/types/extension.d.ts +14 -0
- package/types/index.d.ts +81 -0
- package/types/png.d.ts +14 -0
- package/types/program.d.ts +1096 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `ggaction` are recorded in this file.
|
|
4
|
+
|
|
5
|
+
## [0.0.1] - 2026-07-17
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Immutable, traceable `ChartProgram` authoring with user-facing chart actions.
|
|
10
|
+
- Point, line, area, bar, rule, error-bar, error-band, regression, density, and box-plot workflows.
|
|
11
|
+
- Position, appearance, scale, axis, grid, legend, title, selection, and highlighting actions.
|
|
12
|
+
- Browser Canvas rendering through `ggaction` and Node PNG output through `ggaction/png`.
|
|
13
|
+
- Public extension authoring through `ggaction/extension`.
|
|
14
|
+
- TypeScript declarations for every public package entry.
|
|
15
|
+
- Runnable documentation, chart examples, generated images, and packed-package consumer qualification.
|
|
16
|
+
|
|
17
|
+
### Known limitations
|
|
18
|
+
|
|
19
|
+
- This is an experimental pre-1.0 release; public APIs may change in later minor or patch releases.
|
|
20
|
+
- Rendering targets Browser Canvas and Node PNG. SVG rendering, animation, facets, and program composition are not yet
|
|
21
|
+
supported.
|
|
22
|
+
- A semantic specification is never compiled automatically. Domain actions must materialize the concrete graphics they
|
|
23
|
+
change before rendering.
|
|
24
|
+
- Cartesian charts are the complete current path. Polar semantic tokens exist only where explicitly documented and do
|
|
25
|
+
not imply complete polar rendering.
|
|
26
|
+
|
|
27
|
+
[0.0.1]: https://github.com/hj-n/ggaction/releases/tag/v0.0.1
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hyeon Jeon
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# ggaction
|
|
2
|
+
|
|
3
|
+
`ggaction` is a JavaScript library for building charts through immutable,
|
|
4
|
+
traceable actions.
|
|
5
|
+
|
|
6
|
+
```javascript
|
|
7
|
+
import { chart, render } from "ggaction";
|
|
8
|
+
|
|
9
|
+
const cars = [
|
|
10
|
+
{ horsepower: 88, mpg: 27, origin: "USA" },
|
|
11
|
+
{ horsepower: 70, mpg: 36, origin: "Japan" },
|
|
12
|
+
{ horsepower: 110, mpg: 24, origin: "Europe" }
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const program = chart()
|
|
16
|
+
.createCanvas({
|
|
17
|
+
width: 640,
|
|
18
|
+
height: 400,
|
|
19
|
+
margin: { top: 30, right: 30, bottom: 60, left: 70 }
|
|
20
|
+
})
|
|
21
|
+
.createData({ values: cars })
|
|
22
|
+
.createPointMark()
|
|
23
|
+
.encodeX({ field: "horsepower" })
|
|
24
|
+
.encodeY({ field: "mpg" })
|
|
25
|
+
.encodeColor({ field: "origin" })
|
|
26
|
+
.encodeRadius({ value: 3 })
|
|
27
|
+
.createGuides({
|
|
28
|
+
axes: {
|
|
29
|
+
x: { title: { text: "Horsepower" } },
|
|
30
|
+
y: { title: { text: "Miles per Gallon" } }
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
render(program, document.querySelector("#chart").getContext("2d"));
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The renderer reads only fully materialized, backend-neutral graphics. Semantic
|
|
38
|
+
state is never automatically compiled during rendering.
|
|
39
|
+
|
|
40
|
+
> **Status:** `0.0.1` is the first experimental public release. APIs may change
|
|
41
|
+
> before `1.0.0`; changes are recorded in the [changelog](./CHANGELOG.md).
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm install ggaction
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The package is ESM-only and supports Node.js 20 or later. The default `ggaction`
|
|
50
|
+
entry works with modern browser build tools and Canvas. `ggaction/png` is
|
|
51
|
+
Node-only and uses a native Canvas dependency.
|
|
52
|
+
|
|
53
|
+
| Entry | Purpose |
|
|
54
|
+
| --- | --- |
|
|
55
|
+
| `ggaction` | Create chart programs and render them to Browser Canvas |
|
|
56
|
+
| `ggaction/extension` | Author wrapped actions and use public low-level primitives |
|
|
57
|
+
| `ggaction/png` | Render a completed program to a PNG file in Node.js |
|
|
58
|
+
|
|
59
|
+
All three entries include TypeScript declarations. See [Getting
|
|
60
|
+
Started](https://hyeonword.com/ggaction/getting-started/) for a complete browser
|
|
61
|
+
setup and [Rendering](https://hyeonword.com/ggaction/api/rendering/) for Node PNG
|
|
62
|
+
output.
|
|
63
|
+
|
|
64
|
+
## Documentation
|
|
65
|
+
|
|
66
|
+
- [npm package](https://www.npmjs.com/package/ggaction)
|
|
67
|
+
- [Getting started](https://hyeonword.com/ggaction/getting-started/)
|
|
68
|
+
- [Cars scatterplot tutorial](https://hyeonword.com/ggaction/tutorials/scatterplot/)
|
|
69
|
+
- [Cars line chart tutorial](https://hyeonword.com/ggaction/tutorials/line-chart/)
|
|
70
|
+
- [Cars histogram tutorial](https://hyeonword.com/ggaction/tutorials/histogram/)
|
|
71
|
+
- [Bar chart tutorial](https://hyeonword.com/ggaction/tutorials/grouped-bar/)
|
|
72
|
+
- [Regression scatterplot tutorial](https://hyeonword.com/ggaction/tutorials/regression-scatterplot/)
|
|
73
|
+
- [Density area chart tutorial](https://hyeonword.com/ggaction/tutorials/density-area/)
|
|
74
|
+
- [Error-bar chart tutorial](https://hyeonword.com/ggaction/tutorials/error-bar/)
|
|
75
|
+
- [Error-band chart tutorial](https://hyeonword.com/ggaction/tutorials/error-band/)
|
|
76
|
+
- [Mark selection and highlighting tutorial](https://hyeonword.com/ggaction/tutorials/mark-selection/)
|
|
77
|
+
- [Box-plot API](https://hyeonword.com/ggaction/api/box-plots/)
|
|
78
|
+
- [Chart concepts](https://hyeonword.com/ggaction/concepts/chart-program/)
|
|
79
|
+
- [Complete action reference](https://hyeonword.com/ggaction/reference/actions/)
|
|
80
|
+
- [Supported features](https://hyeonword.com/ggaction/supported-features/)
|
|
81
|
+
- [Documentation index](https://hyeonword.com/ggaction/)
|
|
82
|
+
|
|
83
|
+
The runnable [cars scatterplot](https://github.com/hj-n/ggaction/tree/main/examples/cars-scatterplot/),
|
|
84
|
+
[aggregate line chart](https://github.com/hj-n/ggaction/tree/main/examples/cars-line-chart/), and
|
|
85
|
+
[stacked histogram](https://github.com/hj-n/ggaction/tree/main/examples/cars-histogram/) use `data/cars.json`. The
|
|
86
|
+
[regression scatterplot](https://github.com/hj-n/ggaction/tree/main/examples/cars-regression-scatterplot/) also uses the
|
|
87
|
+
cars data and layers grouped fits with confidence bands. The [grouped bar
|
|
88
|
+
chart](https://github.com/hj-n/ggaction/tree/main/examples/jobs-grouped-bar/) uses `data/jobs.json`. The [density area
|
|
89
|
+
chart](https://github.com/hj-n/ggaction/tree/main/examples/cars-density-area/) derives grouped Acceleration
|
|
90
|
+
distributions from the cars data. The [error-bar chart](https://github.com/hj-n/ggaction/tree/main/examples/cars-error-bar/)
|
|
91
|
+
summarizes mean Acceleration with 95% confidence intervals. The
|
|
92
|
+
[error-band chart](https://github.com/hj-n/ggaction/tree/main/examples/gapminder-error-band/) summarizes grouped
|
|
93
|
+
confidence intervals as closed area paths over time.
|
|
94
|
+
The [box plot](https://github.com/hj-n/ggaction/tree/main/examples/cars-box-plot/) supports vertical or horizontal
|
|
95
|
+
Tukey/min–max ranges, configurable factor and component appearance, and
|
|
96
|
+
optional outlier rendering from categorical and quantitative field pairs.
|
|
97
|
+
The [mark-selection example](https://github.com/hj-n/ggaction/tree/main/examples/mark-selection/) compares grouped point,
|
|
98
|
+
complete stacked-bar, and line-series highlighting through one selector grammar.
|
|
99
|
+
Completed programs can also be exported through
|
|
100
|
+
[`ggaction/png`](https://hyeonword.com/ggaction/api/rendering/#png-output).
|
|
101
|
+
|
|
102
|
+
## Development
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npm install
|
|
106
|
+
npm test
|
|
107
|
+
npm run test:render
|
|
108
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ggaction",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Build charts through immutable, traceable graphical actions.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Hyeon Jeon",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/hj-n/ggaction.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://hyeonword.com/ggaction/",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/hj-n/ggaction/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"visualization",
|
|
17
|
+
"charts",
|
|
18
|
+
"graphics",
|
|
19
|
+
"canvas",
|
|
20
|
+
"data-visualization"
|
|
21
|
+
],
|
|
22
|
+
"type": "module",
|
|
23
|
+
"files": [
|
|
24
|
+
"src/",
|
|
25
|
+
"types/",
|
|
26
|
+
"README.md",
|
|
27
|
+
"CHANGELOG.md",
|
|
28
|
+
"LICENSE"
|
|
29
|
+
],
|
|
30
|
+
"types": "./types/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./types/index.d.ts",
|
|
34
|
+
"default": "./src/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./extension": {
|
|
37
|
+
"types": "./types/extension.d.ts",
|
|
38
|
+
"default": "./src/extension.js"
|
|
39
|
+
},
|
|
40
|
+
"./png": {
|
|
41
|
+
"types": "./types/png.d.ts",
|
|
42
|
+
"default": "./src/renderers/png.js"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"test": "node scripts/run-tests.js all",
|
|
47
|
+
"test:unit": "node scripts/run-tests.js unit",
|
|
48
|
+
"test:contracts": "node scripts/run-tests.js contracts",
|
|
49
|
+
"test:charts": "node scripts/run-tests.js charts",
|
|
50
|
+
"test:gates": "node scripts/run-tests.js gates",
|
|
51
|
+
"test:docs": "node scripts/run-tests.js docs",
|
|
52
|
+
"test:browser": "node scripts/run-tests.js browser",
|
|
53
|
+
"test:docs:built": "node scripts/check-built-docs.js _site",
|
|
54
|
+
"test:docs:browser": "node scripts/test-built-docs.js _site",
|
|
55
|
+
"test:coverage": "node scripts/run-tests.js coverage",
|
|
56
|
+
"test:render": "node test/support/artifacts.js && node scripts/run-tests.js render && node scripts/generate-roadmap-gallery.js && node scripts/test-roadmap-gallery.js",
|
|
57
|
+
"artifacts:gallery": "node scripts/generate-roadmap-gallery.js",
|
|
58
|
+
"artifacts:gallery:test": "node scripts/test-roadmap-gallery.js",
|
|
59
|
+
"docs:images": "node scripts/generate-doc-images.js",
|
|
60
|
+
"docs:llms": "node scripts/generate-llm-docs.js",
|
|
61
|
+
"contracts:catalog": "node scripts/generate-action-catalog.js",
|
|
62
|
+
"contracts:catalog:check": "node scripts/generate-action-catalog.js --check",
|
|
63
|
+
"package:check": "node scripts/package-artifact.js --check",
|
|
64
|
+
"package:pack": "node scripts/package-artifact.js --pack",
|
|
65
|
+
"test:package": "node scripts/package-consumer.js",
|
|
66
|
+
"release:notes": "node scripts/release-notes.js"
|
|
67
|
+
},
|
|
68
|
+
"engines": {
|
|
69
|
+
"node": ">=20"
|
|
70
|
+
},
|
|
71
|
+
"publishConfig": {
|
|
72
|
+
"access": "public",
|
|
73
|
+
"registry": "https://registry.npmjs.org/",
|
|
74
|
+
"tag": "latest"
|
|
75
|
+
},
|
|
76
|
+
"dependencies": {
|
|
77
|
+
"@napi-rs/canvas": "^1.0.2"
|
|
78
|
+
},
|
|
79
|
+
"devDependencies": {
|
|
80
|
+
"es-module-lexer": "^1.7.0",
|
|
81
|
+
"playwright": "^1.61.1",
|
|
82
|
+
"typescript": "^7.0.2"
|
|
83
|
+
}
|
|
84
|
+
}
|
package/src/AGENTS.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Source Instructions
|
|
2
|
+
|
|
3
|
+
Apply these instructions to library source, public package entry points, and source-facing declarations in addition to the repository root instructions.
|
|
4
|
+
|
|
5
|
+
## Source Ownership and Module Boundaries
|
|
6
|
+
|
|
7
|
+
- Organize directories by stable responsibility and reusable capability rather than by the chart example or implementation phase that first required the code.
|
|
8
|
+
- Give each cross-cutting contract one canonical owner. Shared defaults, closed vocabularies, resource lookup rules, validation rules, and dispatch tables must be defined once and consumed by actions rather than re-declared in multiple feature modules.
|
|
9
|
+
- Route ID-based lookup, existence checks, and required-resource errors for named semantic datasets, layers, scales, and coordinates through shared selectors. Keep capability queries local only when they select by semantic behavior rather than by resource identity.
|
|
10
|
+
- Use one shared concrete-graphic schema for graphical editing and rendering. Renderers may add completeness checks required to draw a node, but they must not redefine a conflicting property or value contract.
|
|
11
|
+
- Namespace every repeatable generated internal dataset, layer, scale, guide, and graphic ID from its owning user-defined resource ID and semantic role. Stable system IDs are reserved for structurally singular slots such as the canvas or one supported guide per channel; do not use global generated IDs where the same aggregate action can create more than one resource.
|
|
12
|
+
- Store each piece of program state in one canonical representation. Convenience aliases must be derived read-only accessors, and internal counters or bookkeeping must remain non-enumerable or private rather than becoming duplicate serialized state.
|
|
13
|
+
- Organize source code by reusable semantic capability and separate wrapped action orchestration from pure grammar, resolution, layout, and materialization calculations. Pure modules must not mutate a program or create trace nodes; meaningful authoring steps must remain wrapped actions.
|
|
14
|
+
- Keep semantic computation, concrete graphical materialization, and renderer execution as explicit boundaries. Do not let renderers infer chart semantics or let pure semantic modules author backend operations.
|
|
15
|
+
- Extract logic shared by multiple actions into the smallest responsible capability module instead of duplicating it or creating a chart-specific utility bucket.
|
|
16
|
+
- Express cross-cutting rematerialization as deterministic plans of wrapped action calls, deduplicate equivalent plan steps while preserving order, and register new consumers with the responsible materialization policy instead of scattering ad hoc rematerialization calls across unrelated actions.
|
|
17
|
+
- Keep every public JavaScript entry point synchronized with its TypeScript declaration, package export-map entry, and package-boundary tests. Keep Node-only adapters out of browser-safe entry points.
|
|
18
|
+
- Respect package boundaries in internal imports as well as public exports. Cross-package consumers must use the owning package's supported entry point rather than reaching through to private implementation files.
|
|
19
|
+
|
|
20
|
+
## User-Facing API Design
|
|
21
|
+
|
|
22
|
+
- Maintain three clear layers: the default Chart Authoring API, the public Action Authoring API, and private library internals.
|
|
23
|
+
- Assign every public action an explicit lifecycle in the action catalog: immutable create-only, mutable resource, assignment, aggregate create-only, stable resource with an edit gap, or primitive. Do not infer that every `create*` action mechanically requires an `edit*` counterpart.
|
|
24
|
+
- A stable independently addressable resource must either have a supported edit path or an explicit cataloged gap. Define editable properties, rematerialization ownership, and conflict behavior before adding its edit action; aggregate actions remain create-only and delegate updates to child actions.
|
|
25
|
+
- Treat resources whose type or attachment changes invalidate multiple semantic and graphical dependencies as structural create-only resources unless an approved resource-specific edit contract owns complete compatibility preflight and deterministic rematerialization. Scale is the explicit exception: `editScale` may atomically change its type only after validating the complete resulting definition and every connected consumer, then rematerializing all dependent marks and guides.
|
|
26
|
+
- Validate a scale edit against the complete proposed definition—type, domain, range, type-specific options, mapping policies, and every connected consumer—before applying any state change. Shared marks, axes, grids, and legends must either all accept the result and rematerialize deterministically or all remain unchanged.
|
|
27
|
+
- When a scale has no attached consumer, validate only the channel-independent definition. Defer channel-, field-, mark-, and item-grain-specific validation until attachment, and reject the attachment if the stored definition is incompatible.
|
|
28
|
+
- Do not treat a guide recipe change as ordinary rematerialization. When an edit would require replacing one structural guide family with another, require an explicit approved replacement action or reject the edit before changing semantic or graphical state.
|
|
29
|
+
- Keep sibling component actions behind the same public facade unless a distinct direct authoring use case justifies exposing one. Internal wrapped components remain visible in traces, but do not expose one sibling merely because implementation staging made it convenient.
|
|
30
|
+
- The default `ggaction` entry point serves chart authors through domain-specific actions and rendering.
|
|
31
|
+
- The `ggaction/extension` entry point serves action authors through `ChartProgram`, `action()`, primitive actions, and trace inspection.
|
|
32
|
+
- The Node-only `ggaction/png` entry point exports completed programs without adding Node dependencies to the browser entry point.
|
|
33
|
+
- Encourage extension authors to subclass `ChartProgram` instead of modifying the shared base prototype, so independently authored extensions do not collide.
|
|
34
|
+
- Do not expose private path-parsing, structural-copy, validation, or rendering-dispatch helpers through public entry points.
|
|
35
|
+
- Do not expose raw graphical IDs or raw graphical property paths when a meaningful domain action can represent the operation.
|
|
36
|
+
- Name chart-authoring actions after semantic concepts such as `createPointMark`; keep concrete realizations such as `circle` inside action implementations and `graphicSpec`.
|
|
37
|
+
- Public actions accept meaningful option objects, such as `editXAxisLine({ lineWidth: 3 })`.
|
|
38
|
+
- Every public action accepts one parameter object and returns a new `ChartProgram`.
|
|
39
|
+
- Require only inputs that the user must decide and that cannot be inferred safely. User-facing actions should infer or default the remaining options whenever the stored program state determines them unambiguously.
|
|
40
|
+
- Ordinary chart-authoring create actions may omit a user resource ID only when one documented deterministic role ID is unambiguous. Persist that resolved ID in semantic state, never invent numbered public-resource IDs, and require an explicit ID when the same role already exists. Advanced resource-assembly actions keep explicit IDs unless their own approved contract says otherwise.
|
|
41
|
+
- Resolve omitted options in this order: an explicit option, a unique inference from stored semantic state, a documented library default, then an error when no safe decision remains. Never choose arbitrarily among multiple candidates.
|
|
42
|
+
- For an action that derives a new layer from an existing encoded layer, infer omitted data, coordinate, position encodings, compatible scale IDs, and explicit semantic grouping from that source layer. Select the source by explicit target, then the current eligible layer, then a unique eligible layer; reject ambiguous candidates or ambiguous channel roles. Apply this rule by semantic capability rather than by the source mark type so layered composition remains consistent across marks.
|
|
43
|
+
- When an ordinary mark is added as a new layer, infer omitted data, coordinate, and compatible `x`/`y` encodings from the current eligible layer, then from one unique eligible layer. Persist the inferred encodings on the new layer, keep mark-specific policies such as aggregate, bin, and stack out of a different mark recipe unless that recipe explicitly supports them, and reject ambiguity instead of choosing a source arbitrarily. Explicit authoring always takes precedence.
|
|
44
|
+
- Make mark-dependent authoring order-independent whenever the final semantic state is unambiguous. Ordinary marks may remain graphically incomplete until their required encodings arrive; derived and composite mark actions must likewise support both compatible encodings before creation and compatible encodings after creation. Register the owning materialization intent when prerequisites are incomplete, rematerialize when later data or encoding actions complete them, and require an explicit target instead of choosing among ambiguous consumers.
|
|
45
|
+
- Treat documented defaults and inference behavior as part of the public API contract. Cover them in user documentation and tests, and persist inferred semantic decisions in `semanticSpec`.
|
|
46
|
+
- Use omission, an empty object, and `false` consistently in aggregate APIs: omission requests automatic applicability and inference, `{}` explicitly selects the component with inferred details, and `false` explicitly disables it.
|
|
47
|
+
- Define option-state semantics explicitly and consistently: omission preserves existing state, `"auto"` resets a supported inferred value, and an explicitly supported `undefined` removes an existing optional property. Do not conflate these states inside create, edit, or rematerialization code.
|
|
48
|
+
- Keep create and edit contracts consistent. A create action requires a missing resource, may treat an equivalent repeated definition as idempotent when explicitly intended, and must reject conflicting definitions. An edit action requires an existing resource and must reject an empty edit.
|
|
49
|
+
- Validate library-defined closed vocabularies such as channels and types.
|
|
50
|
+
- Treat user-defined IDs as names: validate their basic form, uniqueness when creating, and existence when referencing rather than checking them against a library vocabulary.
|
|
51
|
+
- Infer layout coordinates from Canvas or plot bounds when possible instead of requiring raw x/y values. Do not make non-overlap a global invariant; individual actions and explicit user options may intentionally produce overlapping graphics.
|
|
52
|
+
- Do not create placeholder resources for optional content. Create an optional dataset, semantic component, materialization config, graphic, or wrapped trace child only when the option is enabled and the component is semantically applicable with non-empty output; disabling or inapplicability must leave the entire optional branch absent.
|
|
53
|
+
- Validate every mark-specific appearance option against the resolved mark recipe before changing state. Reject inapplicable options with a clear error instead of silently ignoring them or partially applying a highlight.
|
|
54
|
+
|
|
55
|
+
## Actions and Trace
|
|
56
|
+
|
|
57
|
+
- Define every authoring action through the shared `action()` wrapper.
|
|
58
|
+
- Treat every `materialize*` and `rematerialize*` method as an internal wrapped action, not as a public direct-call API or an authoring primitive. The public domain action that owns the affected semantic resource must invoke it, and the resulting operation must remain visible in the trace.
|
|
59
|
+
- Provide an atomic domain action when multiple semantic operations are interdependent and separately authoring them would leave an incomplete or misleading chart state.
|
|
60
|
+
- When one public encoding choice requires a companion encoding, let the representative domain action create that companion through a wrapped child action. Keep the companion action available only as an advanced API when ordinary chart authors should not need to coordinate the pair manually.
|
|
61
|
+
- Implement an atomic action by orchestrating the existing wrapped child actions that own the relevant validation, inference, and materialization; do not duplicate their behavior inside the aggregate.
|
|
62
|
+
- Component actions invoked by higher-level actions must also be wrapped actions when they represent meaningful authoring steps.
|
|
63
|
+
- Aggregate actions must orchestrate wrapped child actions instead of duplicating their behavior or hiding meaningful authoring steps inside untraced helpers.
|
|
64
|
+
- Keep aggregate actions thin: they may decide child applicability and call order, but inference and validation owned by a child action must remain in that child rather than being reimplemented by the aggregate.
|
|
65
|
+
- Before an aggregate action invokes its first state-changing child, use the canonical child-owned validators and resolvers to preflight the complete option object, inferred ownership, applicability, and conflicts without duplicating their logic. A rejected aggregate call must not leave partial datasets, layers, graphics, configs, or trace branches.
|
|
66
|
+
- Do not hide meaningful action decomposition inside untraced helpers.
|
|
67
|
+
- Every trace has a virtual `program` root, and nested wrapped calls form its action hierarchy.
|
|
68
|
+
- Context updates are part of successful immutable state transitions; they are not separate actions or trace nodes.
|
|
69
|
+
- Treat context only as transient convenience for interpreting the next action. Completed semantic meaning and renderable output must be persisted in `semanticSpec` or `graphicSpec`, never only in context.
|
|
70
|
+
- Update context through a private immutable helper rather than a public or wrapped `setContext` action.
|
|
71
|
+
- When `editSemantic` can infer the current semantic resource from its validated path, high-level actions must rely on that transition instead of duplicating it with `_withContext`.
|
|
72
|
+
- Keep trace arguments lightweight. Do not copy large datasets or fully materialized value arrays into the trace.
|
|
73
|
+
- When a transform creates a derived dataset for a mark, rebind that mark through an explicit wrapped semantic action so the dependency is visible in the trace. Do not make consumers switch datasets implicitly through context or untraced mutation.
|
|
74
|
+
|
|
75
|
+
## Semantic and Graphical Boundary
|
|
76
|
+
|
|
77
|
+
- `semanticSpec` records what the chart means; `graphicSpec` records the concrete graphical result.
|
|
78
|
+
- The action that first introduces a semantic concept owns its inference, validation, and storage. Downstream actions must read the stored decision rather than silently creating, repairing, or re-inferring missing semantic state.
|
|
79
|
+
- Persist every inferred semantic decision, including resolved resource IDs and types, in `semanticSpec`; do not leave a resolved decision only in context or an implementation-local value.
|
|
80
|
+
- A semantic point mark may be realized by a graphical `circle` primitive.
|
|
81
|
+
- Compute aggregate scale domains at the final visual grouping grain. For example, grouped bars use one aggregate per x/category cell rather than an earlier aggregate that ignores the grouping field.
|
|
82
|
+
- A constant point shape is graphical appearance, while a field-driven shape is semantic encoding that must be explicitly materialized.
|
|
83
|
+
- User-specified scale domains and ranges are semantic. Resolved primitive values such as x, y, radius, and color are graphical.
|
|
84
|
+
- Give scale mapping one pure grammar owner. Point, line, area, bar, rule, guides, and future consumers must call the shared mapping policy rather than reimplementing transformations, clamping, reversal, discretization, or fallback behavior in mark-specific materializers.
|
|
85
|
+
- Decide missing and invalid value behavior at the final semantic item grain, not from the channel name alone. A fallback may preserve a row-owned item, but must be rejected when it could change path, series, aggregate, interval, group, or other compound topology unless that grain has an explicit topology-safe policy.
|
|
86
|
+
- Dataset values are immutable after creation; filtering, aggregation, and other data changes must create transforms or derived datasets rather than replace source values.
|
|
87
|
+
- Editing a stored transform parameter must create a new deterministic namespaced derived-dataset revision and explicitly rebind its consumers; never overwrite an existing dataset's values. An unreferenced old derived revision may be released from the new program through a visible internal wrapped action, while earlier programs retain it unchanged.
|
|
88
|
+
- Canvas properties, themes, fonts, strokes, and other appearance-only values are graphical.
|
|
89
|
+
- Keep appearance-only materialization settings such as a grouped-bar band fraction outside `semanticSpec`; store them in immutable graphical configuration and materialize their concrete results into `graphicSpec`.
|
|
90
|
+
- Keep mark-selector value sources explicit and non-overlapping: `field` reads data values, `channel` reads resolved pre-scale semantic encoding values, and `property` reads concrete `graphicSpec` values. Never expose a pixel dimension under a semantic channel name or overwrite a source field with an aggregate result.
|
|
91
|
+
- Represent bar measure geometry semantically with endpoint channels: `y`/`x` is the start endpoint and `y2`/`x2` is the end endpoint. Concrete rectangles continue to use top-left `x`/`y` plus `width`/`height`. A selector grain that groups multiple concrete children must retain every attachment ID and expose the union bounds only as graphical properties.
|
|
92
|
+
- Define selection over final materialized mark items rather than raw source rows. Reuse row identity only when one row corresponds deterministically to one final item; aggregated, grouped, stacked, path, area, and composite marks require identities at their actual visual grain.
|
|
93
|
+
- Give every selectable mark recipe an explicit semantic item model. Keep semantic endpoints and grouping roles distinct from derived concrete properties such as top-left position, width, height, radius, or union bounds, and never introduce ambiguous aliases between them.
|
|
94
|
+
- For marks with multiple meaningful grains, expose the grain explicitly and evaluate each grain independently. For example, one stacked-bar segment and the complete stack that contains it are different selectable items and must not be conflated by inference.
|
|
95
|
+
- Implement highlighting as an appearance operation over the canonical normalized selection result. `highlightMarks` must reuse `selectMarks` selection semantics rather than introducing a second selector grammar or resolving a different item set.
|
|
96
|
+
- Rematerialize highlights from the unhighlighted concrete baseline, then reevaluate stable item identities and apply the current selected/complement recipes. Never accumulate new highlight edits on previously highlighted graphics or treat stale child graphic IDs as authoritative.
|
|
97
|
+
- When a legend represents a selected or highlighted mark domain, synchronize the applicable legend symbol appearance with that domain state while preserving label readability. Do not dim or restyle legend text unless an explicit text option requests it.
|
|
98
|
+
- Output density such as PNG `pixelRatio` is a renderer option and must not rewrite logical values in `graphicSpec`.
|
|
99
|
+
- When a semantic change affects existing concrete output, the responsible domain action must explicitly rematerialize every affected graphical consumer.
|
|
100
|
+
- Positional encoding actions own coordinate inference and layer attachment. Guide actions read stored coordinates and must not create or repair them.
|
|
101
|
+
- Once scale consumers exist, canvas width, height, or margin edits must explicitly invoke wrapped rematerialization actions for every affected scale, mark, and guide; never leave stale concrete coordinates or rely on automatic compilation.
|
|
102
|
+
- Treat concrete rendering order as explicit graphical state rather than an accidental consequence of action call order. Use graphic placement to preserve relationships such as grids behind marks and axes or legends above them.
|
|
103
|
+
- Do not synthesize missing categorical combinations as zero values or placeholder graphics unless an explicit semantic completion policy requests them. Materialize only observed groups by default.
|
|
104
|
+
- Use one resolved ordinal domain order and its band geometry as the shared source of truth for marks, offsets, ticks, labels, and other positional consumers.
|
|
105
|
+
- When one composite component depends on another component's realized geometry, derive it from the concrete owner geometry instead of duplicating scale, band, width, or endpoint calculations. Rematerialize the owner before its dependents so Canvas and scale edits preserve alignment.
|
|
106
|
+
- Design shared guides around their semantic role, such as a categorical legend, and express mark-specific symbols through graphical recipes instead of forking the complete guide implementation by mark type.
|
|
107
|
+
- Give each guide type one chart-independent documented default, such as a right-side legend. Alternate placement must come from an explicit public option rather than a different hidden default for each chart type.
|
|
108
|
+
- Generic aggregate actions must select only semantic combinations that their child actions currently support. Determine applicability from persisted mark, encoding, scale, and coordinate state rather than from resource presence alone.
|
|
109
|
+
- Statistical transforms must record enough provenance to reproduce and interpret their results: the source dataset, transform type, input and output fields, grouping, method, and every resolved parameter or default that affects the derived values.
|
|
110
|
+
- When one statistical policy controls multiple coupled outputs, normalize it once and pass the same immutable resolved decision to every derived dataset and component. Do not independently recompute classifications such as summaries, bounds, and excluded rows in separate consumers.
|
|
111
|
+
- Derived-data output order must be deterministic and documented. Unless a transform defines another semantic order, preserve group order by first appearance in the source and use a stable, explicit order within each group.
|
|
112
|
+
- Keep statistical computation, semantic data authoring, and graphical materialization separate. Pure grammar modules compute derived values, data actions own transform provenance and derived datasets, and mark actions turn those values into concrete graphics.
|
|
113
|
+
- User-facing guide text must not expose generated internal field names when transform provenance can recover the original meaning. Infer titles from source fields and semantic roles such as `Density` rather than names such as `Acceleration_density`.
|
|
114
|
+
- Layers that jointly express one visual relationship, such as points, fitted lines, confidence bands, density curves, and their baselines, must use compatible coordinates and shared scales. Convert baselines and bounds through those scales rather than treating semantic values as raw graphical coordinates.
|
|
115
|
+
- Layout actions that share reserved space must compute and validate occupied bounds from the actual concrete realization, including rotation, stroke extents, and every collection child rather than a nominal component box. When the requested title, legend, or similar block does not fit, report a clear layout error instead of silently expanding the Canvas or changing the user's margins.
|
|
116
|
+
- Validate same-edge title, axis, legend, and similar reserved-block collisions symmetrically from the final graphical state. Create, edit, Canvas rematerialization, and reversed authoring order must accept or reject an equivalent final layout identically.
|
|
117
|
+
- Resolve text measurement, word or character wrapping, Unicode code-point boundaries, and concrete line positions deterministically during materialization. Store the resolved text children in `graphicSpec`; renderers must not measure, wrap, or infer line placement again.
|
|
118
|
+
- When an edit changes a stable graphic between a single node and a collection, changes collection cardinality, or otherwise changes its concrete representation, reconcile it under the same stable resource ID and rendering placement. Remove stale children or incompatible properties instead of retaining mixed old and new state.
|
|
119
|
+
- Layout-resource edits and Canvas edits must converge: equivalent final options must produce the same concrete `graphicSpec` regardless of whether the layout edit or Canvas edit happened first.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { registerActions } from "./actions/index.js";
|
|
2
|
+
import { ChartProgram as CoreChartProgram } from "./core/ChartProgram.js";
|
|
3
|
+
|
|
4
|
+
export class ChartProgram extends CoreChartProgram {}
|
|
5
|
+
|
|
6
|
+
registerActions(ChartProgram);
|
|
7
|
+
|
|
8
|
+
export function chart() {
|
|
9
|
+
return new ChartProgram();
|
|
10
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { action } from "../../core/action.js";
|
|
2
|
+
import { validateKeys } from "../../core/validation.js";
|
|
3
|
+
|
|
4
|
+
const MEDIAN_OPTIONS = Object.freeze([
|
|
5
|
+
"id", "owner", "data", "category", "categoryType", "measure",
|
|
6
|
+
"coordinate", "categoryScale", "measureScale", "orientation", "stroke",
|
|
7
|
+
"strokeWidth"
|
|
8
|
+
]);
|
|
9
|
+
const OUTLIER_OPTIONS = Object.freeze([
|
|
10
|
+
"id", "data", "category", "categoryType", "measure", "coordinate",
|
|
11
|
+
"categoryScale", "measureScale", "orientation", "shape", "radius", "opacity"
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
export const createBoxMedian = action(
|
|
15
|
+
{
|
|
16
|
+
op: "createBoxMedian",
|
|
17
|
+
description: "Create a median rule spanning one concrete box body."
|
|
18
|
+
},
|
|
19
|
+
function (args = {}) {
|
|
20
|
+
validateKeys(args, MEDIAN_OPTIONS, "createBoxMedian");
|
|
21
|
+
if (!["vertical", "horizontal"].includes(args.orientation)) {
|
|
22
|
+
throw new Error(`Unsupported box median orientation "${args.orientation}".`);
|
|
23
|
+
}
|
|
24
|
+
const categoryAction = args.orientation === "vertical" ? "encodeX" : "encodeY";
|
|
25
|
+
const measureAction = args.orientation === "vertical" ? "encodeY" : "encodeX";
|
|
26
|
+
let next = this.createRuleMark({ id: args.id, data: args.data });
|
|
27
|
+
next = next[categoryAction]({
|
|
28
|
+
target: args.id,
|
|
29
|
+
field: args.category,
|
|
30
|
+
fieldType: args.categoryType,
|
|
31
|
+
coordinate: args.coordinate,
|
|
32
|
+
scale: { id: args.categoryScale }
|
|
33
|
+
});
|
|
34
|
+
next = next[measureAction]({
|
|
35
|
+
target: args.id,
|
|
36
|
+
field: args.measure,
|
|
37
|
+
fieldType: "quantitative",
|
|
38
|
+
coordinate: args.coordinate,
|
|
39
|
+
scale: { id: args.measureScale }
|
|
40
|
+
});
|
|
41
|
+
next = next
|
|
42
|
+
.encodeStroke({ target: args.id, value: args.stroke })
|
|
43
|
+
.encodeStrokeWidth({ target: args.id, value: args.strokeWidth });
|
|
44
|
+
next = next._withMarkConfig(args.id, {
|
|
45
|
+
...next.markConfigs[args.id],
|
|
46
|
+
boxSpanOwner: args.owner
|
|
47
|
+
});
|
|
48
|
+
return next.rematerializeRuleMark({ id: args.id });
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
export const createBoxOutliers = action(
|
|
53
|
+
{
|
|
54
|
+
op: "createBoxOutliers",
|
|
55
|
+
description: "Create concrete point symbols for box-plot outlier rows."
|
|
56
|
+
},
|
|
57
|
+
function (args = {}) {
|
|
58
|
+
validateKeys(args, OUTLIER_OPTIONS, "createBoxOutliers");
|
|
59
|
+
if (!["vertical", "horizontal"].includes(args.orientation)) {
|
|
60
|
+
throw new Error(`Unsupported box outlier orientation "${args.orientation}".`);
|
|
61
|
+
}
|
|
62
|
+
const categoryAction = args.orientation === "vertical" ? "encodeX" : "encodeY";
|
|
63
|
+
const measureAction = args.orientation === "vertical" ? "encodeY" : "encodeX";
|
|
64
|
+
let next = this.createPointMark({
|
|
65
|
+
id: args.id,
|
|
66
|
+
data: args.data,
|
|
67
|
+
shape: args.shape
|
|
68
|
+
});
|
|
69
|
+
next = next[categoryAction]({
|
|
70
|
+
target: args.id,
|
|
71
|
+
field: args.category,
|
|
72
|
+
fieldType: args.categoryType,
|
|
73
|
+
coordinate: args.coordinate,
|
|
74
|
+
scale: { id: args.categoryScale }
|
|
75
|
+
});
|
|
76
|
+
next = next[measureAction]({
|
|
77
|
+
target: args.id,
|
|
78
|
+
field: args.measure,
|
|
79
|
+
fieldType: "quantitative",
|
|
80
|
+
coordinate: args.coordinate,
|
|
81
|
+
scale: { id: args.measureScale }
|
|
82
|
+
});
|
|
83
|
+
return next
|
|
84
|
+
.encodeRadius({ target: args.id, value: args.radius })
|
|
85
|
+
.encodeOpacity({ target: args.id, value: args.opacity })
|
|
86
|
+
.editGraphics({ target: args.id, property: "fill", value: "#111111" });
|
|
87
|
+
}
|
|
88
|
+
);
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { action } from "../../core/action.js";
|
|
2
|
+
import { validateKeys } from "../../core/validation.js";
|
|
3
|
+
import { findDataset } from "../../selectors/datasets.js";
|
|
4
|
+
import { findSemanticScale } from "../../selectors/scales.js";
|
|
5
|
+
import {
|
|
6
|
+
BOX_PLOT_OPTIONS,
|
|
7
|
+
boxEncodingArgs,
|
|
8
|
+
resolveBoxAppearance,
|
|
9
|
+
resolveBoxMedianAppearance,
|
|
10
|
+
resolveBoxOutlierAppearance,
|
|
11
|
+
resolveBoxPosition,
|
|
12
|
+
resolveBoxWhisker,
|
|
13
|
+
resolveBoxWidth
|
|
14
|
+
} from "./options.js";
|
|
15
|
+
import {
|
|
16
|
+
resolveBoxOrientation,
|
|
17
|
+
resolveBoxPlotId,
|
|
18
|
+
resolveBoxSourceLayer
|
|
19
|
+
} from "./resolve.js";
|
|
20
|
+
|
|
21
|
+
export const createBoxPlot = action(
|
|
22
|
+
{
|
|
23
|
+
op: "createBoxPlot",
|
|
24
|
+
description: "Create a Tukey box plot from categorical and quantitative positions."
|
|
25
|
+
},
|
|
26
|
+
function (args = {}) {
|
|
27
|
+
validateKeys(args, BOX_PLOT_OPTIONS, "createBoxPlot");
|
|
28
|
+
const id = resolveBoxPlotId(this, args.id);
|
|
29
|
+
const source = resolveBoxSourceLayer(this, args.target);
|
|
30
|
+
const data = args.data ?? source?.data ?? this.context.currentData;
|
|
31
|
+
if (findDataset(this, data) === undefined) {
|
|
32
|
+
throw new Error("createBoxPlot requires data or one inferable dataset.");
|
|
33
|
+
}
|
|
34
|
+
const x = resolveBoxPosition(args.x, "x") ?? source?.encoding?.x;
|
|
35
|
+
const y = resolveBoxPosition(args.y, "y") ?? source?.encoding?.y;
|
|
36
|
+
const whisker = resolveBoxWhisker(args.whisker);
|
|
37
|
+
const width = resolveBoxWidth(args.width);
|
|
38
|
+
if (args.outliers !== undefined && typeof args.outliers !== "boolean") {
|
|
39
|
+
throw new TypeError("createBoxPlot outliers must be a boolean.");
|
|
40
|
+
}
|
|
41
|
+
const box = resolveBoxAppearance(args.box);
|
|
42
|
+
const median = resolveBoxMedianAppearance(args.median);
|
|
43
|
+
const outlier = resolveBoxOutlierAppearance(args.outlier);
|
|
44
|
+
if (x !== undefined && y !== undefined && resolveBoxOrientation(x, y) === undefined) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
"createBoxPlot requires one categorical axis and one quantitative axis."
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
const orientation = resolveBoxOrientation(x, y);
|
|
50
|
+
const category = orientation === "vertical" ? x : y;
|
|
51
|
+
const categoryScaleId = typeof category?.scale === "string"
|
|
52
|
+
? category.scale
|
|
53
|
+
: category?.scale?.id;
|
|
54
|
+
const categoryScale = findSemanticScale(this, categoryScaleId);
|
|
55
|
+
let next = categoryScale?.type === "point"
|
|
56
|
+
? this.editScale({ id: categoryScaleId, type: "band" })
|
|
57
|
+
: this;
|
|
58
|
+
next = next.createBarMark({ id, data })._withMarkConfig(id, {
|
|
59
|
+
boxPlot: {
|
|
60
|
+
whisker,
|
|
61
|
+
width,
|
|
62
|
+
outliers: args.outliers ?? true,
|
|
63
|
+
box,
|
|
64
|
+
median,
|
|
65
|
+
outlier
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
if (x !== undefined) {
|
|
69
|
+
next = next.encodeX({
|
|
70
|
+
...boxEncodingArgs(x),
|
|
71
|
+
target: id,
|
|
72
|
+
coordinate: args.coordinate ?? source?.coordinate
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
if (y !== undefined) {
|
|
76
|
+
next = next.encodeY({
|
|
77
|
+
...boxEncodingArgs(y),
|
|
78
|
+
target: id,
|
|
79
|
+
coordinate: args.coordinate ?? source?.coordinate
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return next.materializeBoxPlot({ id });
|
|
83
|
+
}
|
|
84
|
+
);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createBoxMedian, createBoxOutliers } from "./components.js";
|
|
2
|
+
import { createBoxPlot } from "./create.js";
|
|
3
|
+
import { materializeBoxPlot } from "./materialize.js";
|
|
4
|
+
|
|
5
|
+
export function registerBoxPlotActions(ProgramClass) {
|
|
6
|
+
ProgramClass.prototype.createBoxPlot = createBoxPlot;
|
|
7
|
+
ProgramClass.prototype.materializeBoxPlot = materializeBoxPlot;
|
|
8
|
+
ProgramClass.prototype.createBoxMedian = createBoxMedian;
|
|
9
|
+
ProgramClass.prototype.createBoxOutliers = createBoxOutliers;
|
|
10
|
+
}
|