ggaction 0.0.3 → 0.0.5
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 +58 -3
- package/README.md +117 -83
- package/package.json +27 -11
- package/src/actions/boxPlots/create.js +13 -7
- package/src/actions/boxPlots/edit.js +31 -35
- package/src/actions/boxPlots/materialize.js +3 -0
- package/src/actions/boxPlots/options.js +8 -1
- package/src/actions/boxPlots/resolve.js +10 -2
- package/src/actions/charts/bar.js +50 -0
- package/src/actions/charts/heatmap.js +205 -0
- package/src/actions/charts/histogram.js +58 -0
- package/src/actions/charts/index.js +15 -0
- package/src/actions/charts/line.js +69 -0
- package/src/actions/charts/parallel.js +76 -0
- package/src/actions/charts/scatter.js +56 -0
- package/src/actions/charts/shared.js +121 -0
- package/src/actions/coordinates/parallel.js +50 -0
- package/src/actions/data/bin2d.js +137 -0
- package/src/actions/data/density.js +54 -13
- package/src/actions/data/derived.js +22 -0
- package/src/actions/data/filter.js +5 -0
- package/src/actions/data/gradientProfile.js +65 -0
- package/src/actions/data/horizon.js +71 -0
- package/src/actions/data/index.js +29 -2
- package/src/actions/data/regression.js +1 -1
- package/src/actions/data/window.js +50 -0
- package/src/actions/encodings/appearance.js +1 -1
- package/src/actions/encodings/color/continuous.js +137 -0
- package/src/actions/encodings/color/index.js +167 -0
- package/src/actions/encodings/color/layout.js +99 -0
- package/src/actions/encodings/color/policy.js +88 -0
- package/src/actions/encodings/density/resolve.js +109 -0
- package/src/actions/encodings/density.js +193 -88
- package/src/actions/encodings/horizon/resolve.js +283 -0
- package/src/actions/encodings/horizon.js +364 -0
- package/src/actions/encodings/index.js +8 -2
- package/src/actions/encodings/offset.js +1 -1
- package/src/actions/encodings/parallel.js +115 -0
- package/src/actions/encodings/pathOrder.js +131 -0
- package/src/actions/encodings/position/apply.js +73 -4
- package/src/actions/encodings/{position.js → position/index.js} +21 -8
- package/src/actions/encodings/position/policies/arc.js +27 -4
- package/src/actions/encodings/position/policies/area.js +33 -2
- package/src/actions/encodings/position/policies/index.js +7 -2
- package/src/actions/encodings/position/policies/line.js +18 -2
- package/src/actions/encodings/position/resolve.js +25 -3
- package/src/actions/encodings/ranged.js +5 -3
- package/src/actions/encodings/ruleAppearance.js +88 -10
- package/src/actions/encodings/strokeDash.js +1 -1
- package/src/actions/encodings/text.js +1 -1
- package/src/actions/errorBands/options.js +1 -1
- package/src/actions/errorBars/options.js +1 -1
- package/src/actions/facets/actions.js +2 -3
- package/src/actions/facets/derive.js +35 -5
- package/src/actions/facets/guides.js +1 -1
- package/src/actions/facets/replay.js +7 -36
- package/src/actions/gradientPlots/components.js +177 -0
- package/src/actions/gradientPlots/create.js +96 -0
- package/src/actions/gradientPlots/edit.js +153 -0
- package/src/actions/gradientPlots/index.js +24 -0
- package/src/actions/gradientPlots/materialize.js +287 -0
- package/src/actions/gradientPlots/options.js +120 -0
- package/src/actions/gradientPlots/paint.js +107 -0
- package/src/actions/gradientPlots/rebind.js +51 -0
- package/src/actions/gradientPlots/resolve.js +81 -0
- package/src/actions/guides/applicability.js +44 -17
- package/src/actions/guides/axes/axes.js +37 -3
- package/src/actions/guides/axes/index.js +2 -0
- package/src/actions/guides/axes/labels.js +12 -7
- package/src/actions/guides/axes/parallel/resolve.js +86 -0
- package/src/actions/guides/axes/parallel.js +228 -0
- package/src/actions/guides/axes/ticks.js +1 -1
- package/src/actions/guides/axes/titles.js +12 -9
- package/src/actions/guides/grids/resolve.js +1 -1
- package/src/actions/guides/guides.js +60 -5
- package/src/actions/guides/legends/categorical/actions.js +48 -11
- package/src/actions/guides/legends/categorical/layout.js +4 -2
- package/src/actions/guides/legends/continuous/interval.js +1 -1
- package/src/actions/guides/legends/continuous/opacity.js +1 -1
- package/src/actions/guides/legends/edit.js +7 -20
- package/src/actions/guides/legends/index.js +2 -0
- package/src/actions/guides/legends/remove.js +6 -31
- package/src/actions/guides/legends/size.js +1 -1
- package/src/actions/guides/legends/strokeWidth.js +170 -0
- package/src/actions/guides/legends/target.js +25 -0
- package/src/actions/guides/polar/{axes.js → axes/index.js} +5 -5
- package/src/actions/guides/polar/index.js +1 -1
- package/src/actions/guides/polar/resolve.js +1 -1
- package/src/actions/guides/tickValues.js +1 -1
- package/src/actions/index.js +6 -0
- package/src/actions/intervals/resolve.js +1 -1
- package/src/actions/marks/arc/actions.js +2 -2
- package/src/actions/marks/area/actions.js +61 -137
- package/src/actions/marks/area/materialize.js +163 -0
- package/src/actions/marks/bar/edit.js +1 -1
- package/src/actions/marks/index.js +5 -1
- package/src/actions/marks/line/actions.js +100 -81
- package/src/actions/marks/line/materialize.js +146 -0
- package/src/actions/marks/point/create.js +76 -0
- package/src/actions/marks/point/edit.js +85 -0
- package/src/actions/marks/point/index.js +11 -1
- package/src/actions/marks/point/jitter.js +72 -0
- package/src/actions/marks/point/{actions.js → materialize.js} +175 -170
- package/src/actions/marks/rect/actions.js +4 -1
- package/src/actions/marks/remove.js +13 -2
- package/src/actions/marks/rule/actions.js +26 -4
- package/src/actions/marks/shared.js +1 -1
- package/src/actions/marks/text/actions.js +20 -5
- package/src/actions/marks/text/index.js +7 -1
- package/src/actions/marks/text/layout.js +321 -0
- package/src/actions/primitives/semanticValidation/dataset.js +22 -0
- package/src/actions/primitives/semanticValidation/guide.js +43 -0
- package/src/actions/primitives/semanticValidation/index.js +23 -0
- package/src/actions/primitives/semanticValidation/layer.js +74 -0
- package/src/actions/primitives/semanticValidation/scale.js +74 -0
- package/src/actions/primitives/semanticValidation/shared.js +5 -0
- package/src/actions/primitives/semanticValue.js +1 -204
- package/src/actions/regression/components.js +1 -1
- package/src/actions/regression/create.js +1 -1
- package/src/actions/regression/edit.js +20 -22
- package/src/actions/scales/consumers/common.js +109 -0
- package/src/actions/scales/consumers/families.js +58 -0
- package/src/actions/scales/consumers/index.js +41 -0
- package/src/actions/scales/{consumers.js → consumers/seriesLayout.js} +10 -132
- package/src/actions/scales/create.js +1 -1
- package/src/actions/scales/definitions.js +32 -2
- package/src/actions/scales/edit.js +8 -206
- package/src/actions/scales/editPolicy.js +214 -0
- package/src/actions/scales/materialize.js +3 -3
- package/src/actions/scales/patch.js +26 -0
- package/src/actions/selection/actions.js +28 -3
- package/src/actions/violinPlots/create.js +198 -0
- package/src/actions/violinPlots/index.js +5 -0
- package/src/core/ChartProgram.js +22 -236
- package/src/core/compositionState.js +169 -0
- package/src/core/materializationState.js +65 -0
- package/src/core/programState.js +28 -0
- package/src/core/textMetrics.js +47 -0
- package/src/core/vocabulary.js +7 -2
- package/src/grammar/arcs.js +50 -13
- package/src/grammar/areaSeries.js +42 -6
- package/src/grammar/bars/aggregate.js +1 -1
- package/src/grammar/bin2d.js +333 -0
- package/src/grammar/categoricalDensity.js +192 -0
- package/src/grammar/coordinates.js +1 -1
- package/src/grammar/curveCommands.js +3 -1
- package/src/grammar/density.js +253 -27
- package/src/grammar/facets/dependencies.js +3 -11
- package/src/grammar/{facets.js → facets/index.js} +8 -6
- package/src/grammar/gradientProfile.js +213 -0
- package/src/grammar/horizon.js +455 -0
- package/src/grammar/interval.js +2 -96
- package/src/grammar/jitter.js +197 -0
- package/src/grammar/lineSeries.js +70 -1
- package/src/grammar/paint.js +88 -0
- package/src/grammar/palettes.js +5 -7
- package/src/grammar/parallelCoordinates.js +213 -0
- package/src/grammar/pathOrder.js +35 -0
- package/src/grammar/pointShapes.js +24 -0
- package/src/grammar/polarLineCommands.js +1 -1
- package/src/grammar/regression/derive.js +97 -0
- package/src/grammar/regression/index.js +22 -0
- package/src/grammar/regression/models.js +282 -0
- package/src/grammar/regression/parameters.js +106 -0
- package/src/grammar/rules.js +2 -1
- package/src/grammar/scales/appearance.js +21 -0
- package/src/grammar/scales/color.js +4 -2
- package/src/grammar/schemas/concreteGraphic.js +11 -1
- package/src/grammar/schemas/graphicBounds.js +19 -37
- package/src/grammar/schemas/semanticPath.js +17 -2
- package/src/grammar/statistics/studentT.js +98 -0
- package/src/grammar/transforms.js +101 -12
- package/src/grammar/window.js +339 -0
- package/src/layout/labels.js +270 -0
- package/src/layout/text.js +4 -1
- package/src/layout/title.js +10 -20
- package/src/materialization/bars/aggregate.js +1 -1
- package/src/materialization/bars/grouped.js +1 -1
- package/src/materialization/bars/histogram.js +1 -1
- package/src/materialization/bars/ranged.js +1 -1
- package/src/materialization/dataProvenance.js +56 -2
- package/src/materialization/density.js +1 -1
- package/src/materialization/dependencies.js +15 -101
- package/src/materialization/encodings.js +2 -2
- package/src/materialization/facetGuides/index.js +2 -0
- package/src/materialization/facetGuides/legacyCategorical.js +125 -0
- package/src/materialization/facetGuides/placement.js +126 -0
- package/src/materialization/facetGuides/preparation.js +188 -0
- package/src/materialization/facets.js +1 -1
- package/src/materialization/guides/resources.js +74 -24
- package/src/materialization/horizon.js +29 -0
- package/src/materialization/layout.js +15 -0
- package/src/materialization/marks/capabilities.js +161 -0
- package/src/materialization/marks/index.js +108 -0
- package/src/materialization/marks/pathOrder.js +71 -0
- package/src/materialization/marks/policies.js +91 -0
- package/src/materialization/rect.js +1 -1
- package/src/materialization/rowEncoding.js +1 -1
- package/src/materialization/scaleGuideDependencies.js +102 -0
- package/src/materialization/scales/map.js +1 -1
- package/src/materialization/scales/policies/series.js +1 -1
- package/src/materialization/scales/resolve.js +3 -1
- package/src/materialization/selection/items/bar.js +1 -1
- package/src/materialization/selection/items/path.js +27 -1
- package/src/materialization/selection/items/point.js +1 -5
- package/src/materialization/selection/items/rect.js +21 -0
- package/src/materialization/selection/styles.js +21 -10
- package/src/materialization/text.js +12 -4
- package/src/renderers/canvas/fill.js +33 -0
- package/src/renderers/canvas/index.js +7 -0
- package/src/renderers/canvas/path.js +7 -4
- package/src/renderers/canvas/rect.js +9 -3
- package/src/renderers/canvas/text.js +14 -1
- package/src/selectors/datasets.js +4 -0
- package/src/theme/defaults.js +4 -0
- package/types/extension.d.ts +10 -1
- package/types/index.d.ts +76 -0
- package/types/program.d.ts +632 -10
- package/src/actions/encodings/color.js +0 -424
- package/src/grammar/regression.js +0 -578
- package/src/grammar/scales.js +0 -1
- package/src/materialization/facetGuides.js +0 -426
- package/src/materialization/marks.js +0 -316
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,59 @@ All notable changes to `ggaction` are recorded in this file.
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## [0.0.5] - 2026-07-21
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Added complete `createScatterPlot`, `createLinePlot`, `createBarPlot`, `createHistogram`, `createHeatmap`, and
|
|
12
|
+
`createParallelCoordinates` facades that reuse ordinary mark, encoding, scale, coordinate, and guide actions.
|
|
13
|
+
- Added deterministic point jitter, ordered line paths, collision-aware text labels, field-driven rule widths, and
|
|
14
|
+
weighted Polar sectors with immutable rematerialization across data, scale, Canvas, selection, and facet changes.
|
|
15
|
+
- Added window and rectangular 2D-bin data actions, binned heatmaps, categorical density and violin plots, Horizon
|
|
16
|
+
charts, Parallel Coordinates, and density-filled gradient plots with Browser Canvas and Node PNG parity.
|
|
17
|
+
- Added backend-neutral item-local gradient paint, expanded public declarations and action contracts, and runnable
|
|
18
|
+
chart examples for every new capability.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- Aligned Box Plot with the shared facade inference, ambiguity, public option-type, and opt-in guide contracts while
|
|
23
|
+
preserving its existing omitted-guide behavior.
|
|
24
|
+
- Reorganized source, test, documentation, and internal architecture ownership around explicit policies and capability
|
|
25
|
+
registries without changing the renderer's concrete `graphicSpec` boundary.
|
|
26
|
+
- Expanded public documentation with task-oriented API routing, generated split action references, improved mobile and
|
|
27
|
+
no-JavaScript navigation, complete facade discovery, and release-scoped deployment checks.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- Materialized the documented default point radius, made direct quantitative line x/y authoring order-independent, and
|
|
32
|
+
made layered datum rules resolve to the expected full-span geometry instead of an empty result.
|
|
33
|
+
- Kept sticky documentation deep links below the top bar by sharing one computed fragment offset between CSS and the
|
|
34
|
+
page table of contents.
|
|
35
|
+
|
|
36
|
+
## [0.0.4] - 2026-07-19
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- Transferred the canonical repository to the `ggaction` organization and moved public documentation to
|
|
41
|
+
`https://ggaction.github.io/ggaction/`.
|
|
42
|
+
- Made public documentation deployment release-scoped so ordinary `main` pushes continue to verify docs without
|
|
43
|
+
changing the published site.
|
|
44
|
+
- Refactored source ownership and materialization boundaries while preserving the public API, stored specifications,
|
|
45
|
+
trace hierarchy, and rendered output.
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
|
|
49
|
+
- Normalized numeric Canvas font weights before rendering so valid intermediate values such as `650` retain normal
|
|
50
|
+
text geometry in both Browser Canvas and Node PNG output.
|
|
51
|
+
- Applied right categorical legend offsets from the plot boundary consistently during creation and focused layout
|
|
52
|
+
edits, including labels, titles, and optional backgrounds.
|
|
53
|
+
- Accepted `count` on sequential palette descriptors as a concrete gradient-stop count, consistently across
|
|
54
|
+
top-level palette shorthands, nested ranges, encodings, direct scales, and scale edits.
|
|
55
|
+
- Preserved concrete `ChartProgram` subclasses in the TypeScript signature of wrapped extension actions and added
|
|
56
|
+
a strict NodeNext declaration-merging authoring pattern.
|
|
57
|
+
- Routed every selective `llms.txt` target to a deployed HTML page, stabilized action fragments, and made built-site
|
|
58
|
+
checks validate both HTTP targets and DOM IDs.
|
|
59
|
+
|
|
7
60
|
## [0.0.3] - 2026-07-19
|
|
8
61
|
|
|
9
62
|
### Added
|
|
@@ -58,6 +111,8 @@ All notable changes to `ggaction` are recorded in this file.
|
|
|
58
111
|
- Cartesian charts are the complete current path. Polar semantic tokens exist only where explicitly documented and do
|
|
59
112
|
not imply complete polar rendering.
|
|
60
113
|
|
|
61
|
-
[0.0.
|
|
62
|
-
[0.0.
|
|
63
|
-
[0.0.
|
|
114
|
+
[0.0.5]: https://github.com/ggaction/ggaction/releases/tag/v0.0.5
|
|
115
|
+
[0.0.4]: https://github.com/ggaction/ggaction/releases/tag/v0.0.4
|
|
116
|
+
[0.0.3]: https://github.com/ggaction/ggaction/releases/tag/v0.0.3
|
|
117
|
+
[0.0.2]: https://github.com/ggaction/ggaction/releases/tag/v0.0.2
|
|
118
|
+
[0.0.1]: https://github.com/ggaction/ggaction/releases/tag/v0.0.1
|
package/README.md
CHANGED
|
@@ -1,119 +1,153 @@
|
|
|
1
1
|
# ggaction
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/ggaction) [](./LICENSE) [](https://ggaction.github.io/ggaction/)
|
|
4
|
+
|
|
5
|
+
### A grammar for how charts are made.
|
|
6
|
+
|
|
7
|
+
Most visualization grammars describe a finished chart. **ggaction** represents chart authoring itself as an immutable, traceable sequence of graphical actions.
|
|
8
|
+
|
|
9
|
+
Build, inspect, select, and revise charts one meaningful action at a time.
|
|
10
|
+
|
|
11
|
+
<p align="left">
|
|
12
|
+
<img src="./docs/assets/images/readme-authoring-sequence.gif" width="960" height="540" loading="eager" alt="A ggaction program progressively creates a scatterplot, adds grouped regression fits and confidence bands, highlights the Japan group across chart layers, and finishes with an R-squared annotation.">
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
Every frame is rendered from an immutable `ChartProgram`. The final R² label is a custom traceable action composed from ggaction's public extension primitives.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install ggaction
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## A grammar of graphical action
|
|
22
|
+
|
|
23
|
+
Actions are verbs:
|
|
24
|
+
|
|
25
|
+
`create · transform · encode · edit · select · compose`
|
|
26
|
+
|
|
27
|
+
Chart resources are nouns:
|
|
28
|
+
|
|
29
|
+
`data · marks · scales · coordinates · guides`
|
|
30
|
+
|
|
31
|
+
A `ChartProgram` is the immutable sentence they produce.
|
|
32
|
+
|
|
33
|
+
The following fragment assumes `cars` is an array of row objects:
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
import { chart } from "ggaction";
|
|
37
|
+
|
|
38
|
+
const program = chart()
|
|
39
|
+
.createCanvas()
|
|
40
|
+
.createData({ values: cars })
|
|
41
|
+
.createScatterPlot({
|
|
42
|
+
id: "points",
|
|
43
|
+
x: "Displacement",
|
|
44
|
+
y: "Acceleration",
|
|
45
|
+
color: "Origin",
|
|
46
|
+
guides: false
|
|
47
|
+
})
|
|
48
|
+
.createRegression()
|
|
49
|
+
.createGuides();
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Why actions?
|
|
53
|
+
|
|
54
|
+
- **Progressive** — build and revise a chart one meaningful operation at a time.
|
|
55
|
+
- **Traceable** — retain high-level actions and the wrapped actions they invoke.
|
|
56
|
+
- **Materialized** — actions create concrete backend-neutral graphics; rendering does not perform hidden semantic compilation.
|
|
57
|
+
|
|
58
|
+
## Quick start
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm install ggaction
|
|
62
|
+
```
|
|
5
63
|
|
|
6
64
|
```javascript
|
|
7
65
|
import { chart, render } from "ggaction";
|
|
8
66
|
|
|
9
|
-
const
|
|
10
|
-
{
|
|
11
|
-
{
|
|
12
|
-
{
|
|
67
|
+
const observations = [
|
|
68
|
+
{ displacement: 97, acceleration: 14.5, origin: "Japan" },
|
|
69
|
+
{ displacement: 140, acceleration: 15.5, origin: "USA" },
|
|
70
|
+
{ displacement: 86, acceleration: 16.4, origin: "Japan" }
|
|
13
71
|
];
|
|
14
72
|
|
|
15
73
|
const program = chart()
|
|
16
74
|
.createCanvas({
|
|
17
75
|
width: 640,
|
|
18
76
|
height: 400,
|
|
19
|
-
margin: { top: 30, right:
|
|
77
|
+
margin: { top: 30, right: 130, bottom: 60, left: 70 }
|
|
20
78
|
})
|
|
21
|
-
.createData({ values:
|
|
22
|
-
.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
.encodeRadius({ value: 3 })
|
|
27
|
-
.createGuides({
|
|
28
|
-
axes: {
|
|
29
|
-
x: { title: { text: "Horsepower" } },
|
|
30
|
-
y: { title: { text: "Miles per Gallon" } }
|
|
31
|
-
}
|
|
79
|
+
.createData({ values: observations })
|
|
80
|
+
.createScatterPlot({
|
|
81
|
+
x: "displacement",
|
|
82
|
+
y: "acceleration",
|
|
83
|
+
color: "origin"
|
|
32
84
|
});
|
|
33
85
|
|
|
34
|
-
|
|
86
|
+
const context = document.querySelector("#chart").getContext("2d");
|
|
87
|
+
render(program, context);
|
|
35
88
|
```
|
|
36
89
|
|
|
37
|
-
|
|
38
|
-
|
|
90
|
+
Use `createLinePlot`, `createBarPlot`, `createHistogram`, and `createHeatmap` for
|
|
91
|
+
the other basic Cartesian charts. Each facade records the regular mark,
|
|
92
|
+
encoding, and guide actions as trace children, and the same resource-specific
|
|
93
|
+
actions remain available for later edits. See the
|
|
94
|
+
[Basic Charts API](https://ggaction.github.io/ggaction/api/basic-charts/).
|
|
39
95
|
|
|
40
|
-
|
|
41
|
-
|
|
96
|
+
Use `createParallelCoordinates({ dimensions })` to connect each source row
|
|
97
|
+
across ordered, dimension-local scales and axes. See the
|
|
98
|
+
[Parallel Coordinates API](https://ggaction.github.io/ggaction/api/parallel-coordinates/)
|
|
99
|
+
or the [runnable Cars example](./examples/cars-parallel-coordinates/).
|
|
42
100
|
|
|
43
|
-
|
|
101
|
+
For an advanced layered example, follow the [regression recipe](https://ggaction.github.io/ggaction/recipes/regression-scatterplot/)
|
|
102
|
+
or open the [runnable regression example](./examples/cars-regression-scatterplot/).
|
|
103
|
+
To compare category distributions with density-filled strips, read the
|
|
104
|
+
[gradient-plot guide](https://ggaction.github.io/ggaction/api/gradient-plots/)
|
|
105
|
+
or open the [runnable example](./examples/cars-gradient-plot/).
|
|
106
|
+
For symmetric or split density shapes centered on categories, use the
|
|
107
|
+
[violin-plot API](https://ggaction.github.io/ggaction/api/violin-plots/) or the
|
|
108
|
+
[runnable Cars example](./examples/cars-acceleration-violins/).
|
|
109
|
+
For compact signed time-series bands, use `encodeHorizon` on an area mark and
|
|
110
|
+
open the [runnable Gapminder example](./examples/gapminder-horizon/).
|
|
44
111
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
112
|
+
## What it supports
|
|
113
|
+
|
|
114
|
+
- Cartesian, Polar, and Parallel-coordinate charts
|
|
115
|
+
- Statistical layers and intervals
|
|
116
|
+
- Faceting and program composition
|
|
117
|
+
- Mark selection and coordinated highlighting
|
|
118
|
+
- Browser Canvas and Node PNG output
|
|
119
|
+
- TypeScript declarations and traceable extension actions
|
|
48
120
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
121
|
+
See the current [supported features](https://ggaction.github.io/ggaction/supported-features/), [tutorials and examples](https://ggaction.github.io/ggaction/tutorials/), and [action reference](https://ggaction.github.io/ggaction/reference/actions/) for exact coverage. Runnable programs are collected in [`examples/`](./examples/README.md).
|
|
122
|
+
|
|
123
|
+
## Package entries
|
|
124
|
+
|
|
125
|
+
The package is ESM-only and requires Node.js 20 or later.
|
|
52
126
|
|
|
53
127
|
| Entry | Purpose |
|
|
54
128
|
| --- | --- |
|
|
55
129
|
| `ggaction` | Create chart programs and render them to Browser Canvas |
|
|
56
|
-
| `ggaction/extension` | Author wrapped actions
|
|
130
|
+
| `ggaction/extension` | Author wrapped actions with public low-level primitives |
|
|
57
131
|
| `ggaction/png` | Render a completed program to a PNG file in Node.js |
|
|
58
132
|
|
|
59
|
-
All
|
|
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.
|
|
133
|
+
All entries include TypeScript declarations. The default entry is browser-safe; the PNG adapter is Node-only.
|
|
63
134
|
|
|
64
135
|
## Documentation
|
|
65
136
|
|
|
66
|
-
- [
|
|
67
|
-
- [
|
|
68
|
-
- [
|
|
69
|
-
- [
|
|
70
|
-
- [
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
The [program-composition example](https://github.com/hj-n/ggaction/tree/main/examples/program-composition/) combines
|
|
100
|
-
complete child programs, edits their inferred layout, and replaces one stable slot.
|
|
101
|
-
The [facet example](https://github.com/hj-n/ggaction/tree/main/examples/cars-origin-scatterplot-facet/)
|
|
102
|
-
repeats one chart with `.facet({ field: "Origin" })`, channel-level shared or
|
|
103
|
-
independent scales, per-cell or occupied-outer axes, and an optional compatible
|
|
104
|
-
parent-owned legend. Regression, density, interval, and box-derived data are
|
|
105
|
-
recomputed independently inside each cell.
|
|
106
|
-
Complete Cartesian and Polar programs can be nested with `hconcat` and
|
|
107
|
-
`vconcat`; the [cross-feature dashboard](https://github.com/hj-n/ggaction/tree/main/examples/cross-feature-dashboard/)
|
|
108
|
-
shows a Polar child replacement beside a Cartesian facet. Polar sources cannot
|
|
109
|
-
currently be faceted and fail before partial child state is created.
|
|
110
|
-
Completed programs can also be exported through
|
|
111
|
-
[`ggaction/png`](https://hyeonword.com/ggaction/api/rendering/#png-output).
|
|
112
|
-
|
|
113
|
-
## Development
|
|
137
|
+
- [Getting Started](https://ggaction.github.io/ggaction/getting-started/)
|
|
138
|
+
- [Tutorials and Examples](https://ggaction.github.io/ggaction/tutorials/)
|
|
139
|
+
- [Action Reference](https://ggaction.github.io/ggaction/reference/actions/)
|
|
140
|
+
- [Concepts](https://ggaction.github.io/ggaction/concepts/chart-program/)
|
|
141
|
+
- [Supported Features](https://ggaction.github.io/ggaction/supported-features/)
|
|
142
|
+
|
|
143
|
+
## Status and development
|
|
144
|
+
|
|
145
|
+
> **Status:** `0.0.5` is the current experimental public release. APIs may change before `1.0.0`; changes are recorded in the [changelog](./CHANGELOG.md).
|
|
114
146
|
|
|
115
147
|
```bash
|
|
116
148
|
npm install
|
|
149
|
+
npm run assets:readme
|
|
117
150
|
npm test
|
|
118
151
|
npm run test:render
|
|
152
|
+
npm run test:docs
|
|
119
153
|
```
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ggaction",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Build charts through immutable, traceable graphical actions.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Hyeon Jeon",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/
|
|
9
|
+
"url": "git+https://github.com/ggaction/ggaction.git"
|
|
10
10
|
},
|
|
11
|
-
"homepage": "https://
|
|
11
|
+
"homepage": "https://ggaction.github.io/ggaction/",
|
|
12
12
|
"bugs": {
|
|
13
|
-
"url": "https://github.com/
|
|
13
|
+
"url": "https://github.com/ggaction/ggaction/issues"
|
|
14
14
|
},
|
|
15
15
|
"keywords": [
|
|
16
16
|
"visualization",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"type": "module",
|
|
23
23
|
"files": [
|
|
24
24
|
"src/",
|
|
25
|
-
"!src
|
|
25
|
+
"!src/**/AGENTS.md",
|
|
26
26
|
"types/",
|
|
27
27
|
"README.md",
|
|
28
28
|
"CHANGELOG.md",
|
|
@@ -54,16 +54,29 @@
|
|
|
54
54
|
"test:docs:built": "node scripts/check-built-docs.js _site",
|
|
55
55
|
"test:docs:browser": "node scripts/test-built-docs.js _site",
|
|
56
56
|
"test:coverage": "node scripts/run-tests.js coverage",
|
|
57
|
-
"test:render": "node
|
|
58
|
-
"
|
|
59
|
-
"artifacts:
|
|
57
|
+
"test:render": "node scripts/run-render-tests.js",
|
|
58
|
+
"assets:readme": "node scripts/generate-readme-demo.js",
|
|
59
|
+
"artifacts:clean": "node test/support/artifacts.js",
|
|
60
|
+
"artifacts:gallery": "node scripts/generate-artifact-gallery.js",
|
|
61
|
+
"artifacts:gallery:test": "node scripts/test-artifact-gallery.js",
|
|
60
62
|
"docs:images": "node scripts/generate-doc-images.js",
|
|
61
63
|
"docs:llms": "node scripts/generate-llm-docs.js",
|
|
64
|
+
"docs:capabilities": "node scripts/generate-doc-capabilities.js",
|
|
65
|
+
"docs:capabilities:check": "node scripts/generate-doc-capabilities.js --check",
|
|
66
|
+
"docs:actions": "node scripts/generate-doc-action-metadata.js",
|
|
67
|
+
"docs:actions:check": "node scripts/generate-doc-action-metadata.js --check",
|
|
68
|
+
"docs:search": "node scripts/generate-doc-search-index.js",
|
|
69
|
+
"docs:search:check": "node scripts/generate-doc-search-index.js --check",
|
|
70
|
+
"docs:reference": "node scripts/generate-doc-action-reference.js",
|
|
71
|
+
"docs:reference:check": "node scripts/generate-doc-action-reference.js --check",
|
|
72
|
+
"docs:metadata": "node scripts/generate-doc-page-metadata.js",
|
|
73
|
+
"docs:metadata:check": "node scripts/generate-doc-page-metadata.js --check",
|
|
74
|
+
"docs:preflight": "node scripts/check-docs-environment.js",
|
|
62
75
|
"docs:signatures": "node scripts/generate-doc-signatures.js",
|
|
63
76
|
"docs:signatures:check": "node scripts/generate-doc-signatures.js --check",
|
|
64
|
-
"docs:generate": "npm run docs:signatures && npm run docs:images && npm run docs:llms",
|
|
77
|
+
"docs:generate": "npm run docs:signatures && npm run docs:capabilities && npm run docs:actions && npm run docs:reference && npm run docs:metadata && npm run docs:search && npm run docs:images && npm run docs:llms",
|
|
65
78
|
"docs:build": "bundle exec jekyll build --source docs --destination _site --baseurl /ggaction",
|
|
66
|
-
"docs:verify": "npm run docs:generate && npm run test:docs && npm run docs:build && npm run test:docs:built && npm run test:docs:browser",
|
|
79
|
+
"docs:verify": "npm run docs:preflight && npm run docs:generate && npm run test:docs && npm run docs:build && npm run test:docs:built && npm run test:docs:browser",
|
|
67
80
|
"contracts:catalog": "node scripts/generate-action-catalog.js",
|
|
68
81
|
"contracts:catalog:check": "node scripts/generate-action-catalog.js --check",
|
|
69
82
|
"package:check": "node scripts/package-artifact.js --check",
|
|
@@ -83,8 +96,11 @@
|
|
|
83
96
|
"@napi-rs/canvas": "^1.0.2"
|
|
84
97
|
},
|
|
85
98
|
"devDependencies": {
|
|
99
|
+
"@axe-core/playwright": "^4.12.1",
|
|
86
100
|
"es-module-lexer": "^1.7.0",
|
|
101
|
+
"gifenc": "^1.0.3",
|
|
87
102
|
"playwright": "^1.61.1",
|
|
88
|
-
"typescript": "^7.0.2"
|
|
103
|
+
"typescript": "^7.0.2",
|
|
104
|
+
"vite": "^6.4.3"
|
|
89
105
|
}
|
|
90
106
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { action } from "../../core/action.js";
|
|
2
2
|
import { validateKeys } from "../../core/validation.js";
|
|
3
|
-
import { findDataset } from "../../selectors/datasets.js";
|
|
4
3
|
import { findSemanticScale } from "../../selectors/scales.js";
|
|
4
|
+
import { resolveFacadeData } from "../charts/shared.js";
|
|
5
5
|
import {
|
|
6
6
|
BOX_PLOT_OPTIONS,
|
|
7
7
|
boxEncodingArgs,
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
resolveBoxMedianAppearance,
|
|
10
10
|
resolveBoxOutlierAppearance,
|
|
11
11
|
resolveBoxPosition,
|
|
12
|
+
resolveBoxGuides,
|
|
12
13
|
resolveBoxWhisker,
|
|
13
14
|
resolveBoxWidth
|
|
14
15
|
} from "./options.js";
|
|
@@ -26,11 +27,14 @@ export const createBoxPlot = action(
|
|
|
26
27
|
function (args = {}) {
|
|
27
28
|
validateKeys(args, BOX_PLOT_OPTIONS, "createBoxPlot");
|
|
28
29
|
const id = resolveBoxPlotId(this, args.id);
|
|
29
|
-
const source = resolveBoxSourceLayer(this, args.target
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
const source = resolveBoxSourceLayer(this, args.target, {
|
|
31
|
+
requiresInference: args.x === undefined || args.y === undefined
|
|
32
|
+
});
|
|
33
|
+
const data = resolveFacadeData(
|
|
34
|
+
this,
|
|
35
|
+
args.data ?? source?.data,
|
|
36
|
+
"createBoxPlot"
|
|
37
|
+
);
|
|
34
38
|
const x = resolveBoxPosition(args.x, "x") ?? source?.encoding?.x;
|
|
35
39
|
const y = resolveBoxPosition(args.y, "y") ?? source?.encoding?.y;
|
|
36
40
|
const whisker = resolveBoxWhisker(args.whisker);
|
|
@@ -41,6 +45,7 @@ export const createBoxPlot = action(
|
|
|
41
45
|
const box = resolveBoxAppearance(args.box);
|
|
42
46
|
const median = resolveBoxMedianAppearance(args.median);
|
|
43
47
|
const outlier = resolveBoxOutlierAppearance(args.outlier);
|
|
48
|
+
const guides = resolveBoxGuides(args.guides);
|
|
44
49
|
if (x !== undefined && y !== undefined && resolveBoxOrientation(x, y) === undefined) {
|
|
45
50
|
throw new Error(
|
|
46
51
|
"createBoxPlot requires one categorical axis and one quantitative axis."
|
|
@@ -62,7 +67,8 @@ export const createBoxPlot = action(
|
|
|
62
67
|
outliers: args.outliers ?? true,
|
|
63
68
|
box,
|
|
64
69
|
median,
|
|
65
|
-
outlier
|
|
70
|
+
outlier,
|
|
71
|
+
guides
|
|
66
72
|
}
|
|
67
73
|
});
|
|
68
74
|
if (x !== undefined) {
|
|
@@ -4,7 +4,8 @@ import { validateUserId } from "../../core/identifiers.js";
|
|
|
4
4
|
import { validateKeys } from "../../core/validation.js";
|
|
5
5
|
import { BOX_FIELDS, deriveBoxData, normalizeBoxTransform } from
|
|
6
6
|
"../../grammar/boxPlot.js";
|
|
7
|
-
import {
|
|
7
|
+
import { planDerivedDataRevision } from
|
|
8
|
+
"../../materialization/dataProvenance.js";
|
|
8
9
|
import { findDataset } from "../../selectors/datasets.js";
|
|
9
10
|
import { findLayer } from "../../selectors/layers.js";
|
|
10
11
|
import {
|
|
@@ -61,14 +62,6 @@ function resolveEditedWhisker(current, value) {
|
|
|
61
62
|
return resolveBoxWhisker(candidate, "editBoxPlot");
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
function nextRevisionId(program, ownerId, kind) {
|
|
65
|
-
let revision = 1;
|
|
66
|
-
while (hasDataset(program, `${ownerId}${kind}Revision${revision}`)) {
|
|
67
|
-
revision += 1;
|
|
68
|
-
}
|
|
69
|
-
return `${ownerId}${kind}Revision${revision}`;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
65
|
function removeOwnedMark(program, id) {
|
|
73
66
|
return program
|
|
74
67
|
.editSemantic({ property: `layer[${id}]`, remove: true })
|
|
@@ -76,10 +69,6 @@ function removeOwnedMark(program, id) {
|
|
|
76
69
|
._withoutMaterializationConfig(["marks", id]);
|
|
77
70
|
}
|
|
78
71
|
|
|
79
|
-
function rebindRuleData(program, id, data) {
|
|
80
|
-
return program.editSemantic({ property: `layer[${id}].data`, value: data });
|
|
81
|
-
}
|
|
82
|
-
|
|
83
72
|
export const editBoxPlot = action(
|
|
84
73
|
{
|
|
85
74
|
op: "editBoxPlot",
|
|
@@ -156,7 +145,18 @@ export const editBoxPlot = action(
|
|
|
156
145
|
derived.outliers.length > 0;
|
|
157
146
|
|
|
158
147
|
if (revisesData) {
|
|
159
|
-
|
|
148
|
+
const whiskerConfig = next.markConfigs[current.whiskerId];
|
|
149
|
+
const capIds = [
|
|
150
|
+
whiskerConfig.errorBar.lowerCapId,
|
|
151
|
+
whiskerConfig.errorBar.upperCapId
|
|
152
|
+
].filter(id => id !== undefined);
|
|
153
|
+
const summaryRevision = planDerivedDataRevision(this, {
|
|
154
|
+
owner: owner.id,
|
|
155
|
+
role: "SummaryData",
|
|
156
|
+
previous: current.summaryId,
|
|
157
|
+
consumers: [owner.id, current.whiskerId, ...capIds, current.medianId]
|
|
158
|
+
});
|
|
159
|
+
summaryId = summaryRevision.id;
|
|
160
160
|
next = next.createBoxSummaryData({
|
|
161
161
|
id: summaryId,
|
|
162
162
|
source: current.source,
|
|
@@ -166,7 +166,16 @@ export const editBoxPlot = action(
|
|
|
166
166
|
...(whisker.factor === undefined ? {} : { factor: whisker.factor })
|
|
167
167
|
});
|
|
168
168
|
if (hasOutliers) {
|
|
169
|
-
|
|
169
|
+
const hadOutlierLayer = findLayer(next, current.outlierId) !== undefined;
|
|
170
|
+
const outlierRevision = planDerivedDataRevision(this, {
|
|
171
|
+
owner: owner.id,
|
|
172
|
+
role: "OutlierData",
|
|
173
|
+
...(current.outlierDataId === undefined
|
|
174
|
+
? {}
|
|
175
|
+
: { previous: current.outlierDataId }),
|
|
176
|
+
consumers: hadOutlierLayer ? [current.outlierId] : []
|
|
177
|
+
});
|
|
178
|
+
outlierDataId = outlierRevision.id;
|
|
170
179
|
next = next.createBoxOutlierData({
|
|
171
180
|
id: outlierDataId,
|
|
172
181
|
source: current.source,
|
|
@@ -175,37 +184,24 @@ export const editBoxPlot = action(
|
|
|
175
184
|
whisker: whisker.type,
|
|
176
185
|
factor: whisker.factor
|
|
177
186
|
});
|
|
187
|
+
for (const rebind of outlierRevision.rebinds) {
|
|
188
|
+
next = next.rebindLayerData(rebind);
|
|
189
|
+
}
|
|
178
190
|
} else {
|
|
179
191
|
outlierDataId = undefined;
|
|
180
192
|
}
|
|
181
193
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const whiskerConfig = next.markConfigs[current.whiskerId];
|
|
187
|
-
const capIds = [
|
|
188
|
-
whiskerConfig.errorBar.lowerCapId,
|
|
189
|
-
whiskerConfig.errorBar.upperCapId
|
|
190
|
-
].filter(id => id !== undefined);
|
|
191
|
-
next = rebindRuleData(next, current.whiskerId, summaryId)
|
|
192
|
-
._withMarkConfig(current.whiskerId, {
|
|
194
|
+
for (const rebind of summaryRevision.rebinds) {
|
|
195
|
+
next = next.rebindLayerData(rebind);
|
|
196
|
+
}
|
|
197
|
+
next = next._withMarkConfig(current.whiskerId, {
|
|
193
198
|
...whiskerConfig,
|
|
194
199
|
errorBar: { ...whiskerConfig.errorBar, data: summaryId }
|
|
195
200
|
});
|
|
196
|
-
for (const capId of capIds) {
|
|
197
|
-
next = rebindRuleData(next, capId, summaryId);
|
|
198
|
-
}
|
|
199
|
-
next = rebindRuleData(next, current.medianId, summaryId);
|
|
200
201
|
|
|
201
202
|
const hadOutlierLayer = findLayer(next, current.outlierId) !== undefined;
|
|
202
203
|
if (hadOutlierLayer && !hasOutliers) {
|
|
203
204
|
next = removeOwnedMark(next, current.outlierId);
|
|
204
|
-
} else if (hadOutlierLayer && hasOutliers) {
|
|
205
|
-
next = next.editSemantic({
|
|
206
|
-
property: `layer[${current.outlierId}].data`,
|
|
207
|
-
value: outlierDataId
|
|
208
|
-
});
|
|
209
205
|
} else if (!hadOutlierLayer && hasOutliers) {
|
|
210
206
|
next = next.createBoxOutliers({
|
|
211
207
|
id: current.outlierId,
|
|
@@ -189,6 +189,9 @@ export const materializeBoxPlot = action(
|
|
|
189
189
|
outlierDataId: hasOutliers ? outlierDataId : undefined
|
|
190
190
|
}
|
|
191
191
|
});
|
|
192
|
+
if (config.guides !== false) {
|
|
193
|
+
next = next.createGuides(config.guides);
|
|
194
|
+
}
|
|
192
195
|
return next._withContext({ currentMark: ownerId, currentData: source });
|
|
193
196
|
}
|
|
194
197
|
);
|
|
@@ -8,10 +8,11 @@ import {
|
|
|
8
8
|
} from "../../core/validation.js";
|
|
9
9
|
import { validatePointShape } from "../../grammar/pointShapes.js";
|
|
10
10
|
import { DEFAULT_COLORS } from "../../theme/defaults.js";
|
|
11
|
+
import { normalizeGuides } from "../charts/shared.js";
|
|
11
12
|
|
|
12
13
|
export const BOX_PLOT_OPTIONS = Object.freeze([
|
|
13
14
|
"id", "target", "data", "x", "y", "coordinate", "whisker",
|
|
14
|
-
"width", "outliers", "box", "median", "outlier"
|
|
15
|
+
"width", "outliers", "box", "median", "outlier", "guides"
|
|
15
16
|
]);
|
|
16
17
|
|
|
17
18
|
const DEFAULT_BOX = Object.freeze({
|
|
@@ -52,6 +53,12 @@ export function boxEncodingArgs(value) {
|
|
|
52
53
|
};
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
export function resolveBoxGuides(value) {
|
|
57
|
+
return value === undefined
|
|
58
|
+
? false
|
|
59
|
+
: normalizeGuides(value, "createBoxPlot");
|
|
60
|
+
}
|
|
61
|
+
|
|
55
62
|
export function resolveBoxWhisker(value, operation = "createBoxPlot") {
|
|
56
63
|
if (value === undefined) return Object.freeze({ type: "tukey", factor: 1.5 });
|
|
57
64
|
if (!isPlainObject(value)) {
|
|
@@ -11,7 +11,9 @@ export function resolveBoxOrientation(x, y) {
|
|
|
11
11
|
return undefined;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export function resolveBoxSourceLayer(program, target
|
|
14
|
+
export function resolveBoxSourceLayer(program, target, {
|
|
15
|
+
requiresInference = true
|
|
16
|
+
} = {}) {
|
|
15
17
|
if (target !== undefined) {
|
|
16
18
|
const layer = findLayer(program, validateUserId(target, "Box source layer id"));
|
|
17
19
|
if (layer === undefined) throw new Error(`Unknown box source layer "${target}".`);
|
|
@@ -24,7 +26,13 @@ export function resolveBoxSourceLayer(program, target) {
|
|
|
24
26
|
const eligible = program.semanticSpec.layers.filter(
|
|
25
27
|
layer => layer.encoding?.x !== undefined && layer.encoding?.y !== undefined
|
|
26
28
|
);
|
|
27
|
-
|
|
29
|
+
if (eligible.length === 1) return eligible[0];
|
|
30
|
+
if (eligible.length > 1 && requiresInference) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
"createBoxPlot target is ambiguous; provide target or explicit x and y."
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
28
36
|
}
|
|
29
37
|
|
|
30
38
|
export function resolveBoxPlotId(program, requested) {
|