ggaction 0.0.2 → 0.0.4
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 +55 -2
- package/README.md +95 -72
- package/package.json +15 -5
- package/src/actions/boxPlots/components.js +3 -3
- package/src/actions/boxPlots/edit.js +277 -0
- package/src/actions/boxPlots/index.js +2 -0
- package/src/actions/boxPlots/materialize.js +12 -1
- package/src/actions/boxPlots/options.js +25 -14
- package/src/actions/canvas/actions.js +21 -17
- package/src/actions/composition/actions.js +247 -0
- package/src/actions/composition/index.js +1 -0
- package/src/actions/coordinates/actions.js +2 -5
- package/src/actions/data/density.js +7 -1
- package/src/actions/data/filter.js +3 -1
- package/src/actions/data/regression.js +1 -1
- package/src/actions/encodings/appearance.js +12 -1
- package/src/actions/encodings/barWidth.js +5 -3
- package/src/actions/encodings/color/continuous.js +137 -0
- package/src/actions/encodings/color/index.js +151 -0
- package/src/actions/encodings/color/layout.js +91 -0
- package/src/actions/encodings/color/policy.js +88 -0
- package/src/actions/encodings/index.js +4 -2
- package/src/actions/encodings/offset.js +91 -73
- package/src/actions/encodings/position/apply.js +66 -0
- package/src/actions/encodings/position/index.js +130 -0
- package/src/actions/encodings/position/policies/arc.js +26 -0
- package/src/actions/encodings/position/policies/bar.js +9 -5
- package/src/actions/encodings/position/policies/index.js +5 -1
- package/src/actions/encodings/position/policies/line.js +20 -3
- package/src/actions/encodings/position/resolve.js +58 -9
- package/src/actions/encodings/ranged.js +30 -8
- package/src/actions/encodings/shared.js +17 -21
- package/src/actions/encodings/strokeDash.js +1 -1
- package/src/actions/encodings/text.js +73 -0
- package/src/actions/errorBands/create.js +37 -29
- package/src/actions/errorBands/edit.js +206 -0
- package/src/actions/errorBands/index.js +9 -0
- package/src/actions/errorBands/options.js +39 -0
- package/src/actions/errorBars/create.js +50 -43
- package/src/actions/errorBars/edit.js +130 -0
- package/src/actions/errorBars/index.js +3 -0
- package/src/actions/errorBars/options.js +52 -0
- package/src/actions/facets/actions.js +162 -0
- package/src/actions/facets/derive.js +200 -0
- package/src/actions/facets/guides.js +16 -0
- package/src/actions/facets/index.js +1 -0
- package/src/actions/facets/replay.js +69 -0
- package/src/actions/guides/applicability.js +113 -0
- package/src/actions/guides/axes/axes.js +70 -16
- package/src/actions/guides/axes/edit.js +126 -0
- package/src/actions/guides/axes/index.js +12 -0
- package/src/actions/guides/axes/labels.js +22 -19
- package/src/actions/guides/axes/remove.js +60 -0
- package/src/actions/guides/axes/tickGroups.js +7 -5
- package/src/actions/guides/axes/ticks.js +10 -5
- package/src/actions/guides/axes/titles.js +8 -6
- package/src/actions/guides/grids/grid.js +144 -15
- package/src/actions/guides/grids/resolve.js +6 -14
- package/src/actions/guides/guides.js +17 -75
- package/src/actions/guides/index.js +6 -0
- package/src/actions/guides/legends/categorical/actions.js +48 -9
- package/src/actions/guides/legends/categorical/components.js +16 -16
- package/src/actions/guides/legends/categorical/index.js +14 -14
- package/src/actions/guides/legends/categorical/layout.js +7 -3
- package/src/actions/guides/legends/categorical/options.js +2 -5
- package/src/actions/guides/legends/categorical/resolve.js +2 -2
- package/src/actions/guides/legends/categorical/symbols.js +14 -14
- package/src/actions/guides/legends/continuous/common.js +7 -7
- package/src/actions/guides/legends/continuous/interval.js +6 -6
- package/src/actions/guides/legends/continuous/opacity.js +1 -1
- package/src/actions/guides/legends/edit.js +4 -26
- package/src/actions/guides/legends/focused.js +63 -0
- package/src/actions/guides/legends/index.js +4 -0
- package/src/actions/guides/legends/remove.js +46 -0
- package/src/actions/guides/legends/size.js +13 -15
- package/src/actions/guides/legends/target.js +25 -0
- package/src/actions/guides/polar/axes/facade.js +219 -0
- package/src/actions/guides/polar/axes/index.js +55 -0
- package/src/actions/guides/polar/axes/labels.js +188 -0
- package/src/actions/guides/polar/axes/lines.js +113 -0
- package/src/actions/guides/polar/axes/shared.js +112 -0
- package/src/actions/guides/polar/axes/ticks.js +164 -0
- package/src/actions/guides/polar/axes/titles.js +165 -0
- package/src/actions/guides/polar/grids.js +252 -0
- package/src/actions/guides/polar/index.js +2 -0
- package/src/actions/guides/polar/resolve.js +269 -0
- package/src/actions/guides/tickValues.js +1 -1
- package/src/actions/index.js +4 -0
- package/src/actions/intervals/resolve.js +1 -1
- package/src/actions/marks/arc/actions.js +239 -0
- package/src/actions/marks/arc/index.js +1 -0
- package/src/actions/marks/{area.js → area/actions.js} +51 -38
- package/src/actions/marks/area/index.js +4 -0
- package/src/actions/marks/bar/create.js +26 -5
- package/src/actions/marks/bar/edit.js +1 -1
- package/src/actions/marks/bar/materialize.js +25 -31
- package/src/actions/marks/index.js +12 -4
- package/src/actions/marks/lifecycle.js +27 -0
- package/src/actions/marks/line/actions.js +373 -0
- package/src/actions/marks/line/index.js +1 -0
- package/src/actions/marks/{point.js → point/actions.js} +67 -108
- package/src/actions/marks/point/index.js +1 -0
- package/src/actions/marks/rect/actions.js +165 -0
- package/src/actions/marks/rect/index.js +1 -0
- package/src/actions/marks/remove.js +214 -0
- package/src/actions/marks/{rule.js → rule/actions.js} +34 -28
- package/src/actions/marks/rule/index.js +1 -0
- package/src/actions/marks/shared.js +156 -25
- package/src/actions/marks/text/actions.js +192 -0
- package/src/actions/marks/text/index.js +1 -0
- package/src/actions/primitives/createGraphics.js +13 -6
- package/src/actions/primitives/editGraphics.js +10 -3
- package/src/actions/primitives/semantic.js +26 -3
- package/src/actions/primitives/semanticValue.js +26 -4
- package/src/actions/regression/components.js +2 -2
- package/src/actions/regression/create.js +19 -2
- package/src/actions/regression/edit.js +247 -0
- package/src/actions/regression/index.js +3 -0
- package/src/actions/regression/resolve.js +3 -1
- package/src/actions/scales/consumers/common.js +91 -0
- package/src/actions/scales/consumers/families.js +47 -0
- package/src/actions/scales/consumers/index.js +41 -0
- package/src/actions/scales/{consumers.js → consumers/seriesLayout.js} +14 -127
- package/src/actions/scales/create.js +1 -1
- package/src/actions/scales/definitions.js +15 -6
- package/src/actions/scales/edit.js +45 -22
- package/src/actions/scales/materialize.js +58 -409
- package/src/actions/selection/actions.js +38 -24
- package/src/actions/selection/index.js +2 -0
- package/src/actions/titles/actions.js +63 -2
- package/src/actions/titles/index.js +2 -0
- package/src/actions/titles/resolve.js +12 -17
- package/src/composition.js +10 -0
- package/src/core/ChartProgram.js +49 -65
- package/src/core/action.js +10 -0
- package/src/core/compositionState.js +169 -0
- package/src/core/materializationState.js +65 -0
- package/src/core/programState.js +28 -0
- package/src/core/validation.js +16 -0
- package/src/core/vocabulary.js +154 -4
- package/src/grammar/arcs.js +154 -0
- package/src/grammar/areaSeries.js +1 -1
- package/src/grammar/bars/aggregate.js +3 -3
- package/src/grammar/bars/geometry.js +3 -3
- package/src/grammar/bars/policy.js +7 -1
- package/src/grammar/coordinates.js +3 -7
- package/src/grammar/density.js +21 -2
- package/src/grammar/facets/dependencies.js +185 -0
- package/src/grammar/facets/guides.js +257 -0
- package/src/grammar/facets/index.js +122 -0
- package/src/grammar/facets/scales.js +254 -0
- package/src/grammar/interval.js +2 -96
- package/src/grammar/lineSeries.js +112 -4
- package/src/grammar/palettes.js +5 -7
- package/src/grammar/polar.js +102 -0
- package/src/grammar/polarGuides.js +243 -0
- package/src/grammar/polarLineCommands.js +43 -0
- package/src/grammar/polarPaths.js +161 -0
- package/src/grammar/positionCompatibility.js +18 -2
- package/src/grammar/rects.js +26 -0
- 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 +1 -1
- package/src/grammar/scales/color.js +4 -2
- package/src/grammar/scales/continuous.js +6 -1
- package/src/grammar/scales/fields.js +7 -0
- package/src/grammar/scales/ordinal.js +5 -2
- package/src/grammar/scales/policies.js +1 -1
- package/src/grammar/schemas/graphic.js +1 -1
- package/src/grammar/schemas/graphicBounds.js +64 -9
- package/src/grammar/schemas/graphicTree.js +44 -10
- package/src/grammar/schemas/semanticPath.js +31 -9
- package/src/grammar/seriesLayout.js +13 -2
- package/src/grammar/statistics/studentT.js +98 -0
- package/src/grammar/text.js +71 -0
- package/src/grammar/ticks.js +50 -13
- package/src/grammar/transforms.js +1 -1
- package/src/index.js +1 -0
- package/src/layout/composition.js +310 -0
- package/src/layout/facets.js +131 -0
- package/src/layout/title.js +6 -0
- package/src/materialization/bars/aggregate.js +6 -3
- package/src/materialization/bars/grouped.js +75 -37
- package/src/materialization/bars/histogram.js +1 -1
- package/src/materialization/bars/ranged.js +1 -1
- package/src/materialization/composition.js +152 -0
- package/src/materialization/compositionSnapshot.js +114 -0
- package/src/materialization/density.js +1 -1
- package/src/materialization/dependencies.js +47 -73
- package/src/materialization/encodings.js +14 -28
- 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 +214 -0
- package/src/materialization/graphicHierarchy.js +15 -5
- package/src/materialization/guides/resources.js +46 -0
- package/src/materialization/marks/capabilities.js +141 -0
- package/src/materialization/marks/index.js +105 -0
- package/src/materialization/marks/policies.js +87 -0
- package/src/materialization/planner.js +105 -12
- package/src/materialization/rect.js +138 -0
- package/src/materialization/rectConfig.js +38 -0
- package/src/materialization/rowEncoding.js +33 -0
- package/src/materialization/scaleGuideDependencies.js +94 -0
- package/src/materialization/scales/map.js +40 -0
- package/src/materialization/scales/policies/arc.js +37 -0
- package/src/materialization/scales/policies/bar.js +132 -0
- package/src/materialization/scales/policies/series.js +76 -0
- package/src/materialization/scales/resolve.js +326 -0
- package/src/materialization/selection/items/arc.js +29 -0
- package/src/materialization/selection/items/bar.js +255 -0
- package/src/materialization/selection/items/common.js +131 -0
- package/src/materialization/selection/items/index.js +6 -0
- package/src/materialization/selection/items/path.js +59 -0
- package/src/materialization/selection/items/point.js +55 -0
- package/src/materialization/selection/items/rect.js +23 -0
- package/src/materialization/selection/items/rule.js +25 -0
- package/src/materialization/selection/policies/arc.js +10 -0
- package/src/materialization/selection/policies/area.js +1 -1
- package/src/materialization/selection/policies/bar.js +1 -1
- package/src/materialization/selection/policies/index.js +5 -1
- package/src/materialization/selection/policies/line.js +1 -1
- package/src/materialization/selection/policies/point.js +1 -1
- package/src/materialization/selection/policies/rect.js +10 -0
- package/src/materialization/selection/policies/rule.js +1 -1
- package/src/materialization/selection/styles.js +25 -17
- package/src/materialization/text.js +156 -0
- package/src/renderers/canvas/index.js +68 -22
- package/src/renderers/canvas/text.js +14 -1
- package/types/extension.d.ts +5 -1
- package/types/index.d.ts +56 -1
- package/types/program.d.ts +664 -27
- package/src/actions/encodings/color.js +0 -329
- package/src/actions/encodings/position.js +0 -176
- package/src/actions/marks/line.js +0 -248
- package/src/grammar/regression.js +0 -578
- package/src/grammar/scales.js +0 -1
- package/src/materialization/marks.js +0 -110
- package/src/materialization/selection/items.js +0 -503
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,57 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `ggaction` are recorded in this file.
|
|
4
4
|
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
## [0.0.4] - 2026-07-19
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Transferred the canonical repository to the `ggaction` organization and moved public documentation to
|
|
12
|
+
`https://ggaction.github.io/ggaction/`.
|
|
13
|
+
- Made public documentation deployment release-scoped so ordinary `main` pushes continue to verify docs without
|
|
14
|
+
changing the published site.
|
|
15
|
+
- Refactored source ownership and materialization boundaries while preserving the public API, stored specifications,
|
|
16
|
+
trace hierarchy, and rendered output.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Normalized numeric Canvas font weights before rendering so valid intermediate values such as `650` retain normal
|
|
21
|
+
text geometry in both Browser Canvas and Node PNG output.
|
|
22
|
+
- Applied right categorical legend offsets from the plot boundary consistently during creation and focused layout
|
|
23
|
+
edits, including labels, titles, and optional backgrounds.
|
|
24
|
+
- Accepted `count` on sequential palette descriptors as a concrete gradient-stop count, consistently across
|
|
25
|
+
top-level palette shorthands, nested ranges, encodings, direct scales, and scale edits.
|
|
26
|
+
- Preserved concrete `ChartProgram` subclasses in the TypeScript signature of wrapped extension actions and added
|
|
27
|
+
a strict NodeNext declaration-merging authoring pattern.
|
|
28
|
+
- Routed every selective `llms.txt` target to a deployed HTML page, stabilized action fragments, and made built-site
|
|
29
|
+
checks validate both HTTP targets and DOM IDs.
|
|
30
|
+
|
|
31
|
+
## [0.0.3] - 2026-07-19
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- Added complete Polar point, line/radar, arc/donut/rose/radial-bar authoring with theta/radius guides, selection,
|
|
36
|
+
highlighting, and Canvas/PNG rendering.
|
|
37
|
+
- Added immutable horizontal and vertical program composition, nested child snapshots, layout editing, stable child
|
|
38
|
+
replacement, and Cartesian facets with derived-data replay, scale resolution, outer axes, and shared legends.
|
|
39
|
+
- Added text and rect marks, directional offsets, horizontal grouped bars, and compatible shared temporal bar/line
|
|
40
|
+
position inference.
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- Nested compositions now preserve their intrinsic layout and honor outer cross-axis alignment instead of stretching
|
|
45
|
+
internal cells or leaving unequal snapshots pinned to the start edge.
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
|
|
49
|
+
- Corrected zero-baseline and signed geometry for aggregate bars, and materialized complete ranged or aggregate bars
|
|
50
|
+
with their documented default width.
|
|
51
|
+
- Made horizontal error bands compose with color and explicit boundaries in the same supported cases as vertical bands.
|
|
52
|
+
- Added standalone point-size legends and interval-aware temporal axis labels without duplicate automatic tick text.
|
|
53
|
+
- Published the exact `createDerivedData` transform-array contract and stable `ChartProgram` state-inspection paths in
|
|
54
|
+
TypeScript and user documentation.
|
|
55
|
+
|
|
5
56
|
## [0.0.2] - 2026-07-17
|
|
6
57
|
|
|
7
58
|
### Fixed
|
|
@@ -31,5 +82,7 @@ All notable changes to `ggaction` are recorded in this file.
|
|
|
31
82
|
- Cartesian charts are the complete current path. Polar semantic tokens exist only where explicitly documented and do
|
|
32
83
|
not imply complete polar rendering.
|
|
33
84
|
|
|
34
|
-
[0.0.
|
|
35
|
-
[0.0.
|
|
85
|
+
[0.0.4]: https://github.com/ggaction/ggaction/releases/tag/v0.0.4
|
|
86
|
+
[0.0.3]: https://github.com/ggaction/ggaction/releases/tag/v0.0.3
|
|
87
|
+
[0.0.2]: https://github.com/ggaction/ggaction/releases/tag/v0.0.2
|
|
88
|
+
[0.0.1]: https://github.com/ggaction/ggaction/releases/tag/v0.0.1
|
package/README.md
CHANGED
|
@@ -1,108 +1,131 @@
|
|
|
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
|
+
.createPointMark({ id: "points" })
|
|
42
|
+
.encodeX({ field: "Displacement" })
|
|
43
|
+
.encodeY({ field: "Acceleration" })
|
|
44
|
+
.encodeColor({ field: "Origin" })
|
|
45
|
+
.createRegression()
|
|
46
|
+
.createGuides();
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Why actions?
|
|
50
|
+
|
|
51
|
+
- **Progressive** — build and revise a chart one meaningful operation at a time.
|
|
52
|
+
- **Traceable** — retain high-level actions and the wrapped actions they invoke.
|
|
53
|
+
- **Materialized** — actions create concrete backend-neutral graphics; rendering does not perform hidden semantic compilation.
|
|
54
|
+
|
|
55
|
+
## Quick start
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npm install ggaction
|
|
59
|
+
```
|
|
5
60
|
|
|
6
61
|
```javascript
|
|
7
62
|
import { chart, render } from "ggaction";
|
|
8
63
|
|
|
9
|
-
const
|
|
10
|
-
{
|
|
11
|
-
{
|
|
12
|
-
{
|
|
64
|
+
const observations = [
|
|
65
|
+
{ displacement: 97, acceleration: 14.5, origin: "Japan" },
|
|
66
|
+
{ displacement: 140, acceleration: 15.5, origin: "USA" },
|
|
67
|
+
{ displacement: 86, acceleration: 16.4, origin: "Japan" }
|
|
13
68
|
];
|
|
14
69
|
|
|
15
70
|
const program = chart()
|
|
16
71
|
.createCanvas({
|
|
17
72
|
width: 640,
|
|
18
73
|
height: 400,
|
|
19
|
-
margin: { top: 30, right:
|
|
74
|
+
margin: { top: 30, right: 130, bottom: 60, left: 70 }
|
|
20
75
|
})
|
|
21
|
-
.createData({ values:
|
|
76
|
+
.createData({ values: observations })
|
|
22
77
|
.createPointMark()
|
|
23
|
-
.encodeX({ field: "
|
|
24
|
-
.encodeY({ field: "
|
|
78
|
+
.encodeX({ field: "displacement" })
|
|
79
|
+
.encodeY({ field: "acceleration" })
|
|
25
80
|
.encodeColor({ field: "origin" })
|
|
26
|
-
.encodeRadius({ value:
|
|
27
|
-
.createGuides(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
render(program, document.querySelector("#chart").getContext("2d"));
|
|
81
|
+
.encodeRadius({ value: 4 })
|
|
82
|
+
.createGuides();
|
|
83
|
+
|
|
84
|
+
const context = document.querySelector("#chart").getContext("2d");
|
|
85
|
+
render(program, context);
|
|
35
86
|
```
|
|
36
87
|
|
|
37
|
-
|
|
38
|
-
state is never automatically compiled during rendering.
|
|
88
|
+
For the advanced version, follow the [regression recipe](https://ggaction.github.io/ggaction/recipes/regression-scatterplot/) or open the [runnable example](./examples/cars-regression-scatterplot/).
|
|
39
89
|
|
|
40
|
-
|
|
41
|
-
> before `1.0.0`; changes are recorded in the [changelog](./CHANGELOG.md).
|
|
90
|
+
## What it supports
|
|
42
91
|
|
|
43
|
-
|
|
92
|
+
- Cartesian and polar charts
|
|
93
|
+
- Statistical layers and intervals
|
|
94
|
+
- Faceting and program composition
|
|
95
|
+
- Mark selection and coordinated highlighting
|
|
96
|
+
- Browser Canvas and Node PNG output
|
|
97
|
+
- TypeScript declarations and traceable extension actions
|
|
44
98
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
99
|
+
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).
|
|
100
|
+
|
|
101
|
+
## Package entries
|
|
48
102
|
|
|
49
|
-
The package is ESM-only and
|
|
50
|
-
entry works with modern browser build tools and Canvas. `ggaction/png` is
|
|
51
|
-
Node-only and uses a native Canvas dependency.
|
|
103
|
+
The package is ESM-only and requires Node.js 20 or later.
|
|
52
104
|
|
|
53
105
|
| Entry | Purpose |
|
|
54
106
|
| --- | --- |
|
|
55
107
|
| `ggaction` | Create chart programs and render them to Browser Canvas |
|
|
56
|
-
| `ggaction/extension` | Author wrapped actions
|
|
108
|
+
| `ggaction/extension` | Author wrapped actions with public low-level primitives |
|
|
57
109
|
| `ggaction/png` | Render a completed program to a PNG file in Node.js |
|
|
58
110
|
|
|
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.
|
|
111
|
+
All entries include TypeScript declarations. The default entry is browser-safe; the PNG adapter is Node-only.
|
|
63
112
|
|
|
64
113
|
## Documentation
|
|
65
114
|
|
|
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
|
-
Completed programs can also be exported through
|
|
100
|
-
[`ggaction/png`](https://hyeonword.com/ggaction/api/rendering/#png-output).
|
|
101
|
-
|
|
102
|
-
## Development
|
|
115
|
+
- [Getting Started](https://ggaction.github.io/ggaction/getting-started/)
|
|
116
|
+
- [Tutorials and Examples](https://ggaction.github.io/ggaction/tutorials/)
|
|
117
|
+
- [Action Reference](https://ggaction.github.io/ggaction/reference/actions/)
|
|
118
|
+
- [Concepts](https://ggaction.github.io/ggaction/concepts/chart-program/)
|
|
119
|
+
- [Supported Features](https://ggaction.github.io/ggaction/supported-features/)
|
|
120
|
+
|
|
121
|
+
## Status and development
|
|
122
|
+
|
|
123
|
+
> **Status:** `0.0.4` is the current experimental public release. APIs may change before `1.0.0`; changes are recorded in the [changelog](./CHANGELOG.md).
|
|
103
124
|
|
|
104
125
|
```bash
|
|
105
126
|
npm install
|
|
127
|
+
npm run assets:readme
|
|
106
128
|
npm test
|
|
107
129
|
npm run test:render
|
|
130
|
+
npm run test:docs
|
|
108
131
|
```
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ggaction",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
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",
|
|
@@ -55,10 +55,18 @@
|
|
|
55
55
|
"test:docs:browser": "node scripts/test-built-docs.js _site",
|
|
56
56
|
"test:coverage": "node scripts/run-tests.js coverage",
|
|
57
57
|
"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",
|
|
58
|
+
"assets:readme": "node scripts/generate-readme-demo.js",
|
|
58
59
|
"artifacts:gallery": "node scripts/generate-roadmap-gallery.js",
|
|
59
60
|
"artifacts:gallery:test": "node scripts/test-roadmap-gallery.js",
|
|
60
61
|
"docs:images": "node scripts/generate-doc-images.js",
|
|
61
62
|
"docs:llms": "node scripts/generate-llm-docs.js",
|
|
63
|
+
"docs:capabilities": "node scripts/generate-doc-capabilities.js",
|
|
64
|
+
"docs:capabilities:check": "node scripts/generate-doc-capabilities.js --check",
|
|
65
|
+
"docs:signatures": "node scripts/generate-doc-signatures.js",
|
|
66
|
+
"docs:signatures:check": "node scripts/generate-doc-signatures.js --check",
|
|
67
|
+
"docs:generate": "npm run docs:signatures && npm run docs:capabilities && npm run docs:images && npm run docs:llms",
|
|
68
|
+
"docs:build": "bundle exec jekyll build --source docs --destination _site --baseurl /ggaction",
|
|
69
|
+
"docs:verify": "npm run docs:generate && npm run test:docs && npm run docs:build && npm run test:docs:built && npm run test:docs:browser",
|
|
62
70
|
"contracts:catalog": "node scripts/generate-action-catalog.js",
|
|
63
71
|
"contracts:catalog:check": "node scripts/generate-action-catalog.js --check",
|
|
64
72
|
"package:check": "node scripts/package-artifact.js --check",
|
|
@@ -79,7 +87,9 @@
|
|
|
79
87
|
},
|
|
80
88
|
"devDependencies": {
|
|
81
89
|
"es-module-lexer": "^1.7.0",
|
|
90
|
+
"gifenc": "^1.0.3",
|
|
82
91
|
"playwright": "^1.61.1",
|
|
83
|
-
"typescript": "^7.0.2"
|
|
92
|
+
"typescript": "^7.0.2",
|
|
93
|
+
"vite": "^6.4.3"
|
|
84
94
|
}
|
|
85
95
|
}
|
|
@@ -64,7 +64,8 @@ export const createBoxOutliers = action(
|
|
|
64
64
|
let next = this.createPointMark({
|
|
65
65
|
id: args.id,
|
|
66
66
|
data: args.data,
|
|
67
|
-
shape: args.shape
|
|
67
|
+
shape: args.shape,
|
|
68
|
+
fill: "#111111"
|
|
68
69
|
});
|
|
69
70
|
next = next[categoryAction]({
|
|
70
71
|
target: args.id,
|
|
@@ -82,7 +83,6 @@ export const createBoxOutliers = action(
|
|
|
82
83
|
});
|
|
83
84
|
return next
|
|
84
85
|
.encodeRadius({ target: args.id, value: args.radius })
|
|
85
|
-
.encodeOpacity({ target: args.id, value: args.opacity })
|
|
86
|
-
.editGraphics({ target: args.id, property: "fill", value: "#111111" });
|
|
86
|
+
.encodeOpacity({ target: args.id, value: args.opacity });
|
|
87
87
|
}
|
|
88
88
|
);
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { action } from "../../core/action.js";
|
|
2
|
+
import { isPlainObject } from "../../core/immutable.js";
|
|
3
|
+
import { validateUserId } from "../../core/identifiers.js";
|
|
4
|
+
import { validateKeys } from "../../core/validation.js";
|
|
5
|
+
import { BOX_FIELDS, deriveBoxData, normalizeBoxTransform } from
|
|
6
|
+
"../../grammar/boxPlot.js";
|
|
7
|
+
import { hasDataset } from "../../selectors/index.js";
|
|
8
|
+
import { findDataset } from "../../selectors/datasets.js";
|
|
9
|
+
import { findLayer } from "../../selectors/layers.js";
|
|
10
|
+
import {
|
|
11
|
+
resolveBoxAppearance,
|
|
12
|
+
resolveBoxMedianAppearance,
|
|
13
|
+
resolveBoxOutlierAppearance,
|
|
14
|
+
resolveBoxWhisker,
|
|
15
|
+
resolveBoxWidth
|
|
16
|
+
} from "./options.js";
|
|
17
|
+
|
|
18
|
+
const OPTIONS = Object.freeze([
|
|
19
|
+
"target", "whisker", "width", "outliers", "box", "median", "outlier"
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
function resolveBoxOwner(program, requested) {
|
|
23
|
+
const eligible = program.semanticSpec.layers.filter(
|
|
24
|
+
layer => program.markConfigs[layer.id]?.boxPlot?.materialized === true
|
|
25
|
+
);
|
|
26
|
+
if (requested !== undefined) {
|
|
27
|
+
const id = validateUserId(requested, "Box-plot owner id");
|
|
28
|
+
const layer = findLayer(program, id);
|
|
29
|
+
if (layer === undefined || !eligible.includes(layer)) {
|
|
30
|
+
throw new Error(`Unknown box-plot owner "${id}".`);
|
|
31
|
+
}
|
|
32
|
+
return layer;
|
|
33
|
+
}
|
|
34
|
+
const current = findLayer(program, program.context.currentMark);
|
|
35
|
+
if (current !== undefined && eligible.includes(current)) return current;
|
|
36
|
+
if (eligible.length === 1) return eligible[0];
|
|
37
|
+
if (eligible.length === 0) throw new Error("No box-plot owner is available.");
|
|
38
|
+
throw new Error("Box-plot owner is ambiguous; provide target.");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function requirePatch(value, label) {
|
|
42
|
+
if (!isPlainObject(value)) {
|
|
43
|
+
throw new TypeError(`editBoxPlot ${label} must be a plain object.`);
|
|
44
|
+
}
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function resolveEditedWhisker(current, value) {
|
|
49
|
+
if (value === undefined) return current;
|
|
50
|
+
const patch = requirePatch(value, "whisker");
|
|
51
|
+
validateKeys(patch, ["type", "factor"], "editBoxPlot whisker");
|
|
52
|
+
const type = patch.type ?? current.type;
|
|
53
|
+
const candidate = type === "minmax"
|
|
54
|
+
? { type, ...(Object.hasOwn(patch, "factor") ? { factor: patch.factor } : {}) }
|
|
55
|
+
: {
|
|
56
|
+
type,
|
|
57
|
+
factor: Object.hasOwn(patch, "factor")
|
|
58
|
+
? patch.factor
|
|
59
|
+
: current.type === "tukey" ? current.factor : 1.5
|
|
60
|
+
};
|
|
61
|
+
return resolveBoxWhisker(candidate, "editBoxPlot");
|
|
62
|
+
}
|
|
63
|
+
|
|
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
|
+
function removeOwnedMark(program, id) {
|
|
73
|
+
return program
|
|
74
|
+
.editSemantic({ property: `layer[${id}]`, remove: true })
|
|
75
|
+
.editGraphics({ target: id, remove: true })
|
|
76
|
+
._withoutMaterializationConfig(["marks", id]);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function rebindRuleData(program, id, data) {
|
|
80
|
+
return program.editSemantic({ property: `layer[${id}].data`, value: data });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const editBoxPlot = action(
|
|
84
|
+
{
|
|
85
|
+
op: "editBoxPlot",
|
|
86
|
+
description: "Revise one box plot and its owned components."
|
|
87
|
+
},
|
|
88
|
+
function (args = {}) {
|
|
89
|
+
validateKeys(args, OPTIONS, "editBoxPlot");
|
|
90
|
+
if (!OPTIONS.slice(1).some(key => Object.hasOwn(args, key))) {
|
|
91
|
+
throw new Error("editBoxPlot requires at least one box-plot option.");
|
|
92
|
+
}
|
|
93
|
+
const owner = resolveBoxOwner(this, args.target);
|
|
94
|
+
const current = this.markConfigs[owner.id].boxPlot;
|
|
95
|
+
const whisker = resolveEditedWhisker(current.whisker, args.whisker);
|
|
96
|
+
const width = Object.hasOwn(args, "width")
|
|
97
|
+
? resolveBoxWidth(args.width, "editBoxPlot")
|
|
98
|
+
: current.width;
|
|
99
|
+
if (Object.hasOwn(args, "outliers") && typeof args.outliers !== "boolean") {
|
|
100
|
+
throw new TypeError("editBoxPlot outliers must be a boolean.");
|
|
101
|
+
}
|
|
102
|
+
const outliers = args.outliers ?? current.outliers;
|
|
103
|
+
const boxPatch = Object.hasOwn(args, "box")
|
|
104
|
+
? requirePatch(args.box, "box")
|
|
105
|
+
: {};
|
|
106
|
+
const medianPatch = Object.hasOwn(args, "median")
|
|
107
|
+
? requirePatch(args.median, "median")
|
|
108
|
+
: {};
|
|
109
|
+
const outlierPatch = Object.hasOwn(args, "outlier")
|
|
110
|
+
? requirePatch(args.outlier, "outlier")
|
|
111
|
+
: {};
|
|
112
|
+
const box = resolveBoxAppearance(
|
|
113
|
+
{ ...current.box, ...boxPatch },
|
|
114
|
+
"editBoxPlot"
|
|
115
|
+
);
|
|
116
|
+
const median = resolveBoxMedianAppearance(
|
|
117
|
+
{ ...current.median, ...medianPatch },
|
|
118
|
+
"editBoxPlot"
|
|
119
|
+
);
|
|
120
|
+
const outlier = resolveBoxOutlierAppearance(
|
|
121
|
+
{ ...current.outlier, ...outlierPatch },
|
|
122
|
+
"editBoxPlot"
|
|
123
|
+
);
|
|
124
|
+
if (Object.hasOwn(boxPatch, "fill") && owner.encoding?.color !== undefined) {
|
|
125
|
+
throw new Error(
|
|
126
|
+
"editBoxPlot box.fill cannot be combined with a color encoding."
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const revisesData = JSON.stringify(whisker) !== JSON.stringify(current.whisker) ||
|
|
131
|
+
outliers !== current.outliers;
|
|
132
|
+
const changesBox = Object.hasOwn(args, "box") ||
|
|
133
|
+
Object.hasOwn(args, "width") || revisesData;
|
|
134
|
+
const changesMedian = Object.hasOwn(args, "median") ||
|
|
135
|
+
Object.hasOwn(args, "width") || revisesData;
|
|
136
|
+
const changesOutlier = Object.hasOwn(args, "outlier") || revisesData;
|
|
137
|
+
const categoryEncoding = current.orientation === "vertical"
|
|
138
|
+
? owner.encoding.x
|
|
139
|
+
: owner.encoding.y;
|
|
140
|
+
const measureEncoding = current.orientation === "vertical"
|
|
141
|
+
? owner.encoding.y
|
|
142
|
+
: owner.encoding.x;
|
|
143
|
+
let summaryId = current.summaryId;
|
|
144
|
+
let outlierDataId = current.outlierDataId;
|
|
145
|
+
let next = this;
|
|
146
|
+
|
|
147
|
+
const sourceRows = findDataset(this, current.source).values;
|
|
148
|
+
const derived = deriveBoxData(sourceRows, normalizeBoxTransform({
|
|
149
|
+
type: "boxSummary",
|
|
150
|
+
category: current.category,
|
|
151
|
+
field: current.measure,
|
|
152
|
+
whisker: whisker.type,
|
|
153
|
+
...(whisker.factor === undefined ? {} : { factor: whisker.factor })
|
|
154
|
+
}));
|
|
155
|
+
const hasOutliers = outliers && whisker.type === "tukey" &&
|
|
156
|
+
derived.outliers.length > 0;
|
|
157
|
+
|
|
158
|
+
if (revisesData) {
|
|
159
|
+
summaryId = nextRevisionId(this, owner.id, "SummaryData");
|
|
160
|
+
next = next.createBoxSummaryData({
|
|
161
|
+
id: summaryId,
|
|
162
|
+
source: current.source,
|
|
163
|
+
category: current.category,
|
|
164
|
+
field: current.measure,
|
|
165
|
+
whisker: whisker.type,
|
|
166
|
+
...(whisker.factor === undefined ? {} : { factor: whisker.factor })
|
|
167
|
+
});
|
|
168
|
+
if (hasOutliers) {
|
|
169
|
+
outlierDataId = nextRevisionId(this, owner.id, "OutlierData");
|
|
170
|
+
next = next.createBoxOutlierData({
|
|
171
|
+
id: outlierDataId,
|
|
172
|
+
source: current.source,
|
|
173
|
+
category: current.category,
|
|
174
|
+
field: current.measure,
|
|
175
|
+
whisker: whisker.type,
|
|
176
|
+
factor: whisker.factor
|
|
177
|
+
});
|
|
178
|
+
} else {
|
|
179
|
+
outlierDataId = undefined;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
next = next.editSemantic({
|
|
183
|
+
property: `layer[${owner.id}].data`,
|
|
184
|
+
value: summaryId
|
|
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, {
|
|
193
|
+
...whiskerConfig,
|
|
194
|
+
errorBar: { ...whiskerConfig.errorBar, data: summaryId }
|
|
195
|
+
});
|
|
196
|
+
for (const capId of capIds) {
|
|
197
|
+
next = rebindRuleData(next, capId, summaryId);
|
|
198
|
+
}
|
|
199
|
+
next = rebindRuleData(next, current.medianId, summaryId);
|
|
200
|
+
|
|
201
|
+
const hadOutlierLayer = findLayer(next, current.outlierId) !== undefined;
|
|
202
|
+
if (hadOutlierLayer && !hasOutliers) {
|
|
203
|
+
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
|
+
} else if (!hadOutlierLayer && hasOutliers) {
|
|
210
|
+
next = next.createBoxOutliers({
|
|
211
|
+
id: current.outlierId,
|
|
212
|
+
data: outlierDataId,
|
|
213
|
+
category: current.category,
|
|
214
|
+
categoryType: categoryEncoding.fieldType,
|
|
215
|
+
measure: current.measure,
|
|
216
|
+
orientation: current.orientation,
|
|
217
|
+
coordinate: owner.coordinate,
|
|
218
|
+
categoryScale: categoryEncoding.scale,
|
|
219
|
+
measureScale: measureEncoding.scale,
|
|
220
|
+
shape: outlier.shape,
|
|
221
|
+
radius: outlier.radius,
|
|
222
|
+
opacity: outlier.opacity
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
next = next._withMarkConfig(owner.id, {
|
|
228
|
+
...next.markConfigs[owner.id],
|
|
229
|
+
boxPlot: {
|
|
230
|
+
...current,
|
|
231
|
+
whisker,
|
|
232
|
+
width,
|
|
233
|
+
outliers,
|
|
234
|
+
box,
|
|
235
|
+
median,
|
|
236
|
+
outlier,
|
|
237
|
+
summaryId,
|
|
238
|
+
outlierDataId
|
|
239
|
+
},
|
|
240
|
+
barWidth: { band: width },
|
|
241
|
+
fill: box.fill,
|
|
242
|
+
opacity: box.opacity,
|
|
243
|
+
stroke: box.stroke,
|
|
244
|
+
strokeWidth: box.strokeWidth
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
if (changesBox) next = next.rematerializeBarMark({ id: owner.id });
|
|
248
|
+
if (revisesData) {
|
|
249
|
+
next = next.rematerializeErrorBar({ id: current.whiskerId });
|
|
250
|
+
}
|
|
251
|
+
if (changesMedian) {
|
|
252
|
+
next = next
|
|
253
|
+
.encodeStroke({ target: current.medianId, value: median.stroke })
|
|
254
|
+
.encodeStrokeWidth({
|
|
255
|
+
target: current.medianId,
|
|
256
|
+
value: median.strokeWidth
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
if (findLayer(next, current.outlierId) !== undefined && changesOutlier) {
|
|
260
|
+
next = next
|
|
261
|
+
.editPointMark({
|
|
262
|
+
target: current.outlierId,
|
|
263
|
+
shape: outlier.shape,
|
|
264
|
+
opacity: outlier.opacity
|
|
265
|
+
})
|
|
266
|
+
.encodeRadius({ target: current.outlierId, value: outlier.radius });
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (revisesData) {
|
|
270
|
+
next = next.releaseDerivedData({ id: current.summaryId });
|
|
271
|
+
if (current.outlierDataId !== undefined) {
|
|
272
|
+
next = next.releaseDerivedData({ id: current.outlierDataId });
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return next._withContext({ currentMark: owner.id, currentData: current.source });
|
|
276
|
+
}
|
|
277
|
+
);
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { createBoxMedian, createBoxOutliers } from "./components.js";
|
|
2
2
|
import { createBoxPlot } from "./create.js";
|
|
3
|
+
import { editBoxPlot } from "./edit.js";
|
|
3
4
|
import { materializeBoxPlot } from "./materialize.js";
|
|
4
5
|
|
|
5
6
|
export function registerBoxPlotActions(ProgramClass) {
|
|
6
7
|
ProgramClass.prototype.createBoxPlot = createBoxPlot;
|
|
8
|
+
ProgramClass.prototype.editBoxPlot = editBoxPlot;
|
|
7
9
|
ProgramClass.prototype.materializeBoxPlot = materializeBoxPlot;
|
|
8
10
|
ProgramClass.prototype.createBoxMedian = createBoxMedian;
|
|
9
11
|
ProgramClass.prototype.createBoxOutliers = createBoxOutliers;
|
|
@@ -50,7 +50,11 @@ export const materializeBoxPlot = action(
|
|
|
50
50
|
source,
|
|
51
51
|
orientation,
|
|
52
52
|
category: category.field,
|
|
53
|
-
measure: measure.field
|
|
53
|
+
measure: measure.field,
|
|
54
|
+
summaryId,
|
|
55
|
+
whiskerId,
|
|
56
|
+
medianId,
|
|
57
|
+
outlierId
|
|
54
58
|
},
|
|
55
59
|
barWidth: { band: config.width },
|
|
56
60
|
fill: config.box.fill,
|
|
@@ -178,6 +182,13 @@ export const materializeBoxPlot = action(
|
|
|
178
182
|
opacity: config.outlier.opacity
|
|
179
183
|
});
|
|
180
184
|
}
|
|
185
|
+
next = next._withMarkConfig(ownerId, {
|
|
186
|
+
...next.markConfigs[ownerId],
|
|
187
|
+
boxPlot: {
|
|
188
|
+
...next.markConfigs[ownerId].boxPlot,
|
|
189
|
+
outlierDataId: hasOutliers ? outlierDataId : undefined
|
|
190
|
+
}
|
|
191
|
+
});
|
|
181
192
|
return next._withContext({ currentMark: ownerId, currentData: source });
|
|
182
193
|
}
|
|
183
194
|
);
|