ggaction 0.0.6 → 0.0.8

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.
Files changed (96) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +77 -14
  3. package/package.json +19 -2
  4. package/src/BasicChartProgram.js +10 -0
  5. package/src/actions/basic.js +21 -0
  6. package/src/actions/canvas/actions.js +42 -0
  7. package/src/actions/canvas/index.js +5 -1
  8. package/src/actions/categoryOrder/index.js +111 -0
  9. package/src/actions/charts/basic.js +13 -0
  10. package/src/actions/data/basic.js +11 -0
  11. package/src/actions/data/basicBin2d.js +43 -0
  12. package/src/actions/data/bin2d.js +1 -26
  13. package/src/actions/data/bin2dMaterialize.js +29 -0
  14. package/src/actions/data/index.js +6 -0
  15. package/src/actions/data/timeUnit.js +48 -0
  16. package/src/actions/encodings/angle.js +77 -0
  17. package/src/actions/encodings/appearance.js +6 -2
  18. package/src/actions/encodings/basic.js +21 -0
  19. package/src/actions/encodings/color/index.js +17 -13
  20. package/src/actions/encodings/color/layout.js +2 -0
  21. package/src/actions/encodings/color/policy.js +3 -0
  22. package/src/actions/encodings/index.js +2 -0
  23. package/src/actions/encodings/position/index.js +5 -1
  24. package/src/actions/encodings/position/policies/area.js +40 -2
  25. package/src/actions/encodings/position/policies/bar.js +6 -0
  26. package/src/actions/encodings/position/policies/index.js +2 -0
  27. package/src/actions/encodings/position/policies/tick.js +19 -0
  28. package/src/actions/encodings/ranged.js +6 -2
  29. package/src/actions/encodings/remove.js +1 -1
  30. package/src/actions/guides/axes/index.js +10 -2
  31. package/src/actions/guides/basic.js +19 -0
  32. package/src/actions/guides/grids/grid.js +7 -3
  33. package/src/actions/guides/grids/index.js +1 -1
  34. package/src/actions/guides/legends/categorical/actions.js +23 -6
  35. package/src/actions/guides/legends/categorical/index.js +14 -2
  36. package/src/actions/guides/legends/continuous/common.js +17 -5
  37. package/src/actions/guides/legends/continuous/gradient.js +12 -8
  38. package/src/actions/guides/legends/continuous/index.js +6 -2
  39. package/src/actions/guides/legends/continuous/opacity.js +70 -17
  40. package/src/actions/guides/legends/edit.js +13 -8
  41. package/src/actions/guides/legends/lane.js +468 -0
  42. package/src/actions/guides/legends/remove.js +3 -7
  43. package/src/actions/index.js +2 -0
  44. package/src/actions/marks/area/actions.js +3 -1
  45. package/src/actions/marks/area/materialize.js +12 -3
  46. package/src/actions/marks/bar/index.js +5 -1
  47. package/src/actions/marks/basic.js +11 -0
  48. package/src/actions/marks/index.js +2 -0
  49. package/src/actions/marks/line/actions.js +5 -1
  50. package/src/actions/marks/line/index.js +4 -1
  51. package/src/actions/marks/point/index.js +5 -1
  52. package/src/actions/marks/point/materialize.js +17 -5
  53. package/src/actions/marks/rect/actions.js +5 -1
  54. package/src/actions/marks/rect/index.js +4 -1
  55. package/src/actions/marks/tick/actions.js +225 -0
  56. package/src/actions/marks/tick/index.js +1 -0
  57. package/src/actions/primitives/semanticValidation/layer.js +2 -0
  58. package/src/actions/scales/consumers/index.js +8 -0
  59. package/src/actions/scales/consumers/seriesLayout.js +22 -2
  60. package/src/actions/scales/index.js +5 -1
  61. package/src/actions/scales/materialize.js +2 -0
  62. package/src/actions/selection/actions.js +5 -2
  63. package/src/basic.js +2 -0
  64. package/src/core/vocabulary.js +7 -3
  65. package/src/grammar/areaSeries.js +112 -2
  66. package/src/grammar/categoryOrder.js +138 -0
  67. package/src/grammar/direction.js +46 -0
  68. package/src/grammar/facets/index.js +1 -1
  69. package/src/grammar/pointShapes.js +34 -6
  70. package/src/grammar/positionCompatibility.js +4 -0
  71. package/src/grammar/schemas/semanticPath.js +2 -1
  72. package/src/grammar/seriesLayout.js +10 -2
  73. package/src/grammar/timeUnit.js +108 -0
  74. package/src/grammar/transforms.js +6 -0
  75. package/src/grammar/window.js +73 -7
  76. package/src/layout/legendLane.js +338 -0
  77. package/src/materialization/marks/capabilities.js +9 -0
  78. package/src/materialization/marks/index.js +2 -1
  79. package/src/materialization/marks/policies.js +13 -0
  80. package/src/materialization/scales/policies/series.js +9 -0
  81. package/src/materialization/scales/resolve.js +25 -3
  82. package/src/materialization/selection/items/index.js +1 -0
  83. package/src/materialization/selection/items/path.js +4 -1
  84. package/src/materialization/selection/items/tick.js +42 -0
  85. package/src/materialization/selection/policies/index.js +2 -0
  86. package/src/materialization/selection/policies/tick.js +10 -0
  87. package/src/renderers/canvas/index.js +96 -60
  88. package/src/renderers/canvas/text.js +3 -14
  89. package/src/renderers/pdf.js +112 -0
  90. package/src/renderers/svg.js +559 -0
  91. package/src/renderers/text.js +11 -0
  92. package/types/basic.d.ts +107 -0
  93. package/types/index.d.ts +10 -0
  94. package/types/pdf.d.ts +26 -0
  95. package/types/program.d.ts +96 -3
  96. package/types/svg.d.ts +11 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,47 @@ All notable changes to `ggaction` are recorded in this file.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## [0.0.8] - 2026-08-04
8
+
9
+ ### Added
10
+
11
+ - Added immutable UTC time-unit derivation, semantic category ordering and reset, and partitioned moving mean/sum
12
+ windows with matching public actions, strict declarations, examples, and Current contracts.
13
+ - Added the Tick mark lifecycle, point/Tick angle encoding, and non-negative centered area stacking with stable
14
+ Canvas, SVG, PNG, and PDF evidence.
15
+
16
+ ### Changed
17
+
18
+ - Expanded the public example and documentation corpus with actual-data temporal, ordering, directional Tick,
19
+ centered-area, and multi-legend charts, plus installed-package coverage for their supported workflows.
20
+
21
+ ### Fixed
22
+
23
+ - Replaced independent same-edge legend placement with deterministic shared right/left lanes and left-packed
24
+ top/bottom rows, aligning titles, symbols, and labels while preserving lifecycle convergence and compact margins.
25
+
26
+ ## [0.0.7] - 2026-07-23
27
+
28
+ ### Added
29
+
30
+ - Added the browser-safe `ggaction/basic` entry for creation-focused scatter, line, bar, histogram, and heatmap
31
+ programs, with matching TypeScript declarations and an enforced 120,000-byte gzip bundle budget.
32
+ - Added browser-safe `ggaction/svg` output and Node-only `ggaction/pdf` single-page vector files, with matching
33
+ declarations, metadata/accessibility options, installed-consumer coverage, and Canvas/SVG/PNG/PDF visual evidence.
34
+
35
+ ### Changed
36
+
37
+ - Applied the documented numeric font-weight normalization consistently across Canvas, SVG, PNG, and PDF rendering.
38
+ - Generated the repository example index from the canonical chart catalog and reduced the LLM index to concise,
39
+ non-duplicated routes into exact runtime and support documentation.
40
+ - Simplified Getting Started, grouped the support limitations by topic, and clarified the complete documentation
41
+ verification workflow for contributors.
42
+
43
+ ### Fixed
44
+
45
+ - Corrected the Canvas target in troubleshooting guidance and documented complete, executable Canvas, SVG, PNG, and
46
+ PDF rendering flows with signatures generated from the public TypeScript declarations.
47
+
7
48
  ## [0.0.6] - 2026-07-23
8
49
 
9
50
  ### Added
@@ -135,6 +176,8 @@ All notable changes to `ggaction` are recorded in this file.
135
176
  - Cartesian charts are the complete current path. Polar semantic tokens exist only where explicitly documented and do
136
177
  not imply complete polar rendering.
137
178
 
179
+ [0.0.8]: https://github.com/ggaction/ggaction/releases/tag/v0.0.8
180
+ [0.0.7]: https://github.com/ggaction/ggaction/releases/tag/v0.0.7
138
181
  [0.0.6]: https://github.com/ggaction/ggaction/releases/tag/v0.0.6
139
182
  [0.0.5]: https://github.com/ggaction/ggaction/releases/tag/v0.0.5
140
183
  [0.0.4]: https://github.com/ggaction/ggaction/releases/tag/v0.0.4
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ggaction
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/ggaction.svg)](https://www.npmjs.com/package/ggaction) [![license](https://img.shields.io/npm/l/ggaction.svg)](./LICENSE) [![documentation](https://img.shields.io/badge/docs-ggaction.github.io-2563eb)](https://ggaction.github.io/ggaction/)
3
+ [![npm version](https://img.shields.io/npm/v/ggaction.svg)](https://www.npmjs.com/package/ggaction) [![CI](https://github.com/ggaction/ggaction/actions/workflows/ci.yml/badge.svg)](https://github.com/ggaction/ggaction/actions/workflows/ci.yml) [![license](https://img.shields.io/npm/l/ggaction.svg)](./LICENSE) [![documentation](https://img.shields.io/badge/docs-ggaction.github.io-2563eb)](https://ggaction.github.io/ggaction/)
4
4
 
5
5
  ### A grammar for how charts are made.
6
6
 
@@ -12,11 +12,7 @@ Build, inspect, select, and revise charts one meaningful action at a time.
12
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
13
  </p>
14
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
- ```
15
+ Every frame is rendered from an immutable `ChartProgram`. The final R² label is a traceable action built from public extension primitives.
20
16
 
21
17
  ## A grammar of graphical action
22
18
 
@@ -57,12 +53,24 @@ const program = chart()
57
53
 
58
54
  ## Quick start
59
55
 
56
+ Install the current public release:
57
+
60
58
  ```bash
61
59
  npm install ggaction
62
60
  ```
63
61
 
62
+ Add the Canvas element that the rendering code targets. Its accessible name and
63
+ fallback text summarize the chart for contexts where Canvas pixels are not
64
+ available:
65
+
66
+ ```html
67
+ <canvas id="chart" aria-label="Scatterplot of displacement versus acceleration by origin">
68
+ Scatterplot of displacement versus acceleration by origin.
69
+ </canvas>
70
+ ```
71
+
64
72
  ```javascript
65
- import { chart, render } from "ggaction";
73
+ import { chart, render } from "ggaction/basic";
66
74
 
67
75
  const observations = [
68
76
  { displacement: 97, acceleration: 14.5, origin: "Japan" },
@@ -80,17 +88,58 @@ const program = chart()
80
88
  .createScatterPlot({
81
89
  x: "displacement",
82
90
  y: "acceleration",
83
- color: "origin"
91
+ color: "origin",
92
+ shape: "origin"
84
93
  });
85
94
 
86
95
  const context = document.querySelector("#chart").getContext("2d");
87
96
  render(program, context);
88
97
  ```
89
98
 
99
+ Matching color and shape encodings give each origin a redundant visual cue and
100
+ create a labeled categorical legend automatically.
101
+
102
+ The Quick Start uses the smaller `ggaction/basic` entry for the common creation
103
+ path. Import from `ggaction` when you need editing, selection, composition,
104
+ alternative coordinates, or statistical layers.
105
+
106
+ ### Branch revisions without mutation
107
+
108
+ Start a separate program without a shape encoding, then derive alternatives
109
+ without changing the checkpoint:
110
+
111
+ ```javascript
112
+ import { chart as editableChart } from "ggaction";
113
+
114
+ const checkpoint = editableChart()
115
+ .createCanvas({ width: 640, height: 400 })
116
+ .createData({ values: observations })
117
+ .createScatterPlot({
118
+ x: "displacement",
119
+ y: "acceleration",
120
+ color: "origin"
121
+ });
122
+
123
+ const outlined = checkpoint.editPointMark({
124
+ stroke: "#0f172a",
125
+ strokeWidth: 2
126
+ });
127
+ const muted = checkpoint.editPointMark({ opacity: 0.35 });
128
+
129
+ console.log(checkpoint === outlined); // false
130
+ console.log(checkpoint.trace.children.at(-1).op); // "createScatterPlot"
131
+ console.log(outlined.trace.children.at(-1).op); // "editPointMark"
132
+ ```
133
+
134
+ All three remain renderable and inspectable. They are immutable values, not an
135
+ automatic undo stack; the application chooses which to retain or render.
136
+
90
137
  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
138
+ the other basic Cartesian charts. The `ggaction/basic` entry keeps this common
139
+ creation path below a 120,000-byte gzip bundle budget while each facade still
140
+ records its mark, encoding, and guide actions as trace children. Import from
141
+ `ggaction` when you need editing, selection, composition, alternative
142
+ coordinates, or statistical layers. See the
94
143
  [Basic Charts API](https://ggaction.github.io/ggaction/api/basic-charts/).
95
144
 
96
145
  Use `createParallelCoordinates({ dimensions })` to connect each source row
@@ -115,7 +164,7 @@ open the [runnable Gapminder example](./examples/gapminder-horizon/).
115
164
  - Statistical layers and intervals
116
165
  - Faceting and program composition
117
166
  - Mark selection and coordinated highlighting
118
- - Browser Canvas and Node PNG output
167
+ - Browser Canvas, SVG, Node PNG, and vector PDF output
119
168
  - TypeScript declarations and traceable extension actions
120
169
 
121
170
  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).
@@ -127,10 +176,14 @@ The package is ESM-only and requires Node.js 20 or later.
127
176
  | Entry | Purpose |
128
177
  | --- | --- |
129
178
  | `ggaction` | Create chart programs and render them to Browser Canvas |
179
+ | `ggaction/basic` | Create and render scatter, line, bar, histogram, and heatmap charts with a smaller browser bundle |
130
180
  | `ggaction/extension` | Author wrapped actions with public low-level primitives |
131
181
  | `ggaction/png` | Render a completed program to a PNG file in Node.js |
182
+ | `ggaction/pdf` | Render a completed program to a single-page vector PDF file in Node.js |
183
+ | `ggaction/svg` | Serialize a completed program to browser-safe SVG |
132
184
 
133
- All entries include TypeScript declarations. The default entry is browser-safe; the PNG adapter is Node-only.
185
+ All entries include TypeScript declarations. The default, basic, extension, and
186
+ SVG entries are browser-safe; the PNG and PDF adapters are Node-only.
134
187
 
135
188
  ## Documentation
136
189
 
@@ -140,9 +193,15 @@ All entries include TypeScript declarations. The default entry is browser-safe;
140
193
  - [Concepts](https://ggaction.github.io/ggaction/concepts/chart-program/)
141
194
  - [Supported Features](https://ggaction.github.io/ggaction/supported-features/)
142
195
 
196
+ ## Contributing
197
+
198
+ Bug reports, documentation improvements, examples, and focused code changes are
199
+ welcome. Read [CONTRIBUTING.md](./CONTRIBUTING.md) for setup, scope, tests, and
200
+ the extra discussion required before public API or architecture changes.
201
+
143
202
  ## Status and development
144
203
 
145
- > **Status:** `0.0.6` is the current experimental public release. APIs may change before `1.0.0`; changes are recorded in the [changelog](./CHANGELOG.md).
204
+ > **Status:** `0.0.8` is the current experimental public release. APIs may change before `1.0.0`; changes are recorded in the [changelog](./CHANGELOG.md).
146
205
 
147
206
  ```bash
148
207
  npm install
@@ -151,3 +210,7 @@ npm test
151
210
  npm run test:render
152
211
  npm run test:docs
153
212
  ```
213
+
214
+ ## Related Links
215
+
216
+ - Action-trace demo: [Link](https://ggactionbot.github.io/action-trace-demo/)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ggaction",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "Build charts through immutable, traceable graphical actions.",
5
5
  "license": "MIT",
6
6
  "author": "Hyeon Jeon",
@@ -20,6 +20,7 @@
20
20
  "data-visualization"
21
21
  ],
22
22
  "type": "module",
23
+ "sideEffects": false,
23
24
  "files": [
24
25
  "src/",
25
26
  "!src/**/AGENTS.md",
@@ -34,6 +35,10 @@
34
35
  "types": "./types/index.d.ts",
35
36
  "default": "./src/index.js"
36
37
  },
38
+ "./basic": {
39
+ "types": "./types/basic.d.ts",
40
+ "default": "./src/basic.js"
41
+ },
37
42
  "./extension": {
38
43
  "types": "./types/extension.d.ts",
39
44
  "default": "./src/extension.js"
@@ -41,6 +46,14 @@
41
46
  "./png": {
42
47
  "types": "./types/png.d.ts",
43
48
  "default": "./src/renderers/png.js"
49
+ },
50
+ "./pdf": {
51
+ "types": "./types/pdf.d.ts",
52
+ "default": "./src/renderers/pdf.js"
53
+ },
54
+ "./svg": {
55
+ "types": "./types/svg.d.ts",
56
+ "default": "./src/renderers/svg.js"
44
57
  }
45
58
  },
46
59
  "scripts": {
@@ -56,6 +69,7 @@
56
69
  "test:coverage": "node scripts/run-tests.js coverage",
57
70
  "test:render": "node scripts/run-render-tests.js",
58
71
  "assets:readme": "node scripts/generate-readme-demo.js",
72
+ "artifacts:renderers": "node scripts/generate-renderer-comparison.js",
59
73
  "artifacts:clean": "node test/support/artifacts.js",
60
74
  "artifacts:gallery": "node scripts/generate-artifact-gallery.js",
61
75
  "artifacts:gallery:test": "node scripts/test-artifact-gallery.js",
@@ -74,13 +88,16 @@
74
88
  "docs:preflight": "node scripts/check-docs-environment.js",
75
89
  "docs:signatures": "node scripts/generate-doc-signatures.js",
76
90
  "docs:signatures:check": "node scripts/generate-doc-signatures.js --check",
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",
91
+ "examples:index": "node scripts/generate-examples-readme.js",
92
+ "examples:index:check": "node scripts/generate-examples-readme.js --check",
93
+ "docs:generate": "npm run examples:index && npm run docs:actions && npm run docs:reference && npm run docs:signatures && npm run docs:capabilities && npm run docs:metadata && npm run docs:search && npm run docs:images && npm run docs:llms",
78
94
  "docs:build": "bundle exec jekyll build --source docs --destination _site --baseurl /ggaction",
79
95
  "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",
80
96
  "contracts:catalog": "node scripts/generate-action-catalog.js",
81
97
  "contracts:catalog:check": "node scripts/generate-action-catalog.js --check",
82
98
  "package:check": "node scripts/package-artifact.js --check",
83
99
  "package:pack": "node scripts/package-artifact.js --pack",
100
+ "package:bundle": "node scripts/measure-browser-bundle.js",
84
101
  "test:package": "node scripts/package-consumer.js",
85
102
  "release:notes": "node scripts/release-notes.js"
86
103
  },
@@ -0,0 +1,10 @@
1
+ import { registerBasicActions } from "./actions/basic.js";
2
+ import { ChartProgram as CoreChartProgram } from "./core/ChartProgram.js";
3
+
4
+ class BasicChartProgram extends CoreChartProgram {}
5
+
6
+ registerBasicActions(BasicChartProgram);
7
+
8
+ export function chart() {
9
+ return new BasicChartProgram();
10
+ }
@@ -0,0 +1,21 @@
1
+ import { registerBasicCanvasActions } from "./canvas/index.js";
2
+ import { registerBasicChartActions } from "./charts/basic.js";
3
+ import { registerCoordinateActions } from "./coordinates/index.js";
4
+ import { registerBasicDataActions } from "./data/basic.js";
5
+ import { registerBasicEncodingActions } from "./encodings/basic.js";
6
+ import { registerBasicGuideActions } from "./guides/basic.js";
7
+ import { registerBasicMarkActions } from "./marks/basic.js";
8
+ import { registerPrimitiveActions } from "./primitives/index.js";
9
+ import { registerBasicScaleActions } from "./scales/index.js";
10
+
11
+ export function registerBasicActions(ProgramClass) {
12
+ registerPrimitiveActions(ProgramClass);
13
+ registerBasicCanvasActions(ProgramClass);
14
+ registerBasicDataActions(ProgramClass);
15
+ registerBasicMarkActions(ProgramClass);
16
+ registerBasicScaleActions(ProgramClass);
17
+ registerBasicEncodingActions(ProgramClass);
18
+ registerCoordinateActions(ProgramClass);
19
+ registerBasicGuideActions(ProgramClass);
20
+ registerBasicChartActions(ProgramClass);
21
+ }
@@ -146,3 +146,45 @@ export const createCanvas = action(
146
146
  });
147
147
  }
148
148
  );
149
+
150
+ export const createBasicCanvas = action(
151
+ {
152
+ op: "createCanvas",
153
+ description: "Create and configure the chart canvas."
154
+ },
155
+ function (args = {}) {
156
+ validateOptions(args, "createCanvas", { allowEmpty: true });
157
+ assertCanvasHierarchyAvailable(this);
158
+ const state = {
159
+ width: Object.hasOwn(args, "width") ? args.width : DEFAULT_CANVAS.width,
160
+ height: Object.hasOwn(args, "height") ? args.height : DEFAULT_CANVAS.height,
161
+ background: Object.hasOwn(args, "background")
162
+ ? args.background
163
+ : DEFAULT_CANVAS.background,
164
+ margin: normalizeMargin(args.margin, DEFAULT_MARGIN),
165
+ size: {
166
+ width: Object.hasOwn(args, "width") ? "explicit" : "auto",
167
+ height: Object.hasOwn(args, "height") ? "explicit" : "auto"
168
+ }
169
+ };
170
+ validateCanvasState(state);
171
+ let next = this
172
+ .createGraphics({
173
+ id: CANVAS_GRAPHIC_ID,
174
+ type: "canvas"
175
+ })
176
+ .createGraphics({
177
+ id: PLOT_GRAPHIC_ID,
178
+ type: "collection",
179
+ parent: CANVAS_GRAPHIC_ID
180
+ });
181
+ for (const property of ["width", "height", "background"]) {
182
+ next = next.editGraphics({
183
+ target: CANVAS_GRAPHIC_ID,
184
+ property,
185
+ value: state[property]
186
+ });
187
+ }
188
+ return next._withCanvasConfig({ margin: state.margin, size: state.size });
189
+ }
190
+ );
@@ -1,6 +1,10 @@
1
- import { createCanvas, editCanvas } from "./actions.js";
1
+ import { createBasicCanvas, createCanvas, editCanvas } from "./actions.js";
2
2
 
3
3
  export function registerCanvasActions(ProgramClass) {
4
4
  ProgramClass.prototype.editCanvas = editCanvas;
5
5
  ProgramClass.prototype.createCanvas = createCanvas;
6
6
  }
7
+
8
+ export function registerBasicCanvasActions(ProgramClass) {
9
+ ProgramClass.prototype.createCanvas = createBasicCanvas;
10
+ }
@@ -0,0 +1,111 @@
1
+ import { action } from "../../core/action.js";
2
+ import { validateUserId } from "../../core/identifiers.js";
3
+ import { validateKeys } from "../../core/validation.js";
4
+ import { findDataset } from "../../selectors/datasets.js";
5
+ import { resolveEligibleLayer } from "../../selectors/layers.js";
6
+ import { findScaleConsumers } from "../scales/consumers/index.js";
7
+ import {
8
+ applyMaterializationPlan,
9
+ planScaleGuideRematerialization
10
+ } from "../../materialization/dependencies.js";
11
+ import { getMarkMaterializationStep } from "../../materialization/marks/index.js";
12
+ import { buildMaterializationPlan } from "../../materialization/planner.js";
13
+ import {
14
+ normalizeCategoryOrder,
15
+ resolveCategoryOrder
16
+ } from "../../grammar/categoryOrder.js";
17
+
18
+ const ORDER_OPTIONS = Object.freeze(["target", "channel", "values", "by", "direction"]);
19
+ const REMOVE_OPTIONS = Object.freeze(["target", "channel"]);
20
+
21
+ function resolveAssignment(program, args, operation, { activeOnly = false } = {}) {
22
+ if (!["x", "y"].includes(args.channel)) {
23
+ throw new Error(`${operation} channel must be x or y.`);
24
+ }
25
+ const requested = args.target === undefined
26
+ ? undefined
27
+ : validateUserId(args.target, "Mark id");
28
+ const layer = resolveEligibleLayer(program, {
29
+ target: requested,
30
+ label: "categorical position mark",
31
+ predicate: candidate => {
32
+ const encoding = candidate.encoding?.[args.channel];
33
+ return ["nominal", "ordinal"].includes(encoding?.fieldType) &&
34
+ typeof encoding.field === "string" && encoding.scale !== undefined &&
35
+ (!activeOnly || encoding.categoryOrder !== undefined);
36
+ }
37
+ });
38
+ const encoding = layer.encoding[args.channel];
39
+ const dataset = findDataset(program, layer.data);
40
+ if (dataset === undefined) {
41
+ throw new Error(`Mark "${layer.id}" requires an existing dataset.`);
42
+ }
43
+ const consumers = findScaleConsumers(program, encoding.scale);
44
+ const incompatible = consumers.find(consumer =>
45
+ consumer.layer.id !== layer.id &&
46
+ (consumer.layer.data !== layer.data || consumer.encoding.field !== encoding.field)
47
+ );
48
+ if (incompatible !== undefined) {
49
+ throw new Error(
50
+ `Category order scale "${encoding.scale}" has an incompatible shared consumer.`
51
+ );
52
+ }
53
+ return { layer, encoding, dataset };
54
+ }
55
+
56
+ function rematerializeAssignment(program, scaleId) {
57
+ const consumers = findScaleConsumers(program, scaleId);
58
+ return applyMaterializationPlan(program, buildMaterializationPlan({
59
+ scales: [{
60
+ op: "rematerializeScale",
61
+ args: { id: scaleId, guides: false, marks: false }
62
+ }],
63
+ marks: consumers.map(consumer =>
64
+ getMarkMaterializationStep(program, consumer.layer)
65
+ ),
66
+ guides: planScaleGuideRematerialization(program, scaleId)
67
+ }));
68
+ }
69
+
70
+ export const orderCategories = action(
71
+ { op: "orderCategories", description: "Assign semantic order to a categorical position." },
72
+ function (args = {}) {
73
+ validateKeys(args, ORDER_OPTIONS, "orderCategories");
74
+ const { layer, encoding, dataset } = resolveAssignment(this, args, "orderCategories");
75
+ const order = normalizeCategoryOrder(args);
76
+ resolveCategoryOrder(dataset.values, encoding.field, order);
77
+ const edited = this.editSemantic({
78
+ property: `layer[${layer.id}].encoding.${args.channel}.categoryOrder`,
79
+ value: order
80
+ });
81
+ return rematerializeAssignment(edited, encoding.scale);
82
+ }
83
+ );
84
+
85
+ export const removeCategoryOrder = action(
86
+ { op: "removeCategoryOrder", description: "Restore automatic category order." },
87
+ function (args = {}) {
88
+ validateKeys(args, REMOVE_OPTIONS, "removeCategoryOrder");
89
+ const { layer, encoding } = resolveAssignment(
90
+ this,
91
+ args,
92
+ "removeCategoryOrder",
93
+ { activeOnly: args.target === undefined }
94
+ );
95
+ if (encoding.categoryOrder === undefined) {
96
+ throw new Error(
97
+ `Mark "${layer.id}" ${args.channel} encoding has no category order assignment.`
98
+ );
99
+ }
100
+ const edited = this.editSemantic({
101
+ property: `layer[${layer.id}].encoding.${args.channel}.categoryOrder`,
102
+ remove: true
103
+ });
104
+ return rematerializeAssignment(edited, encoding.scale);
105
+ }
106
+ );
107
+
108
+ export function registerCategoryOrderActions(ProgramClass) {
109
+ ProgramClass.prototype.orderCategories = orderCategories;
110
+ ProgramClass.prototype.removeCategoryOrder = removeCategoryOrder;
111
+ }
@@ -0,0 +1,13 @@
1
+ import { createBarPlot } from "./bar.js";
2
+ import { createHeatmap } from "./heatmap.js";
3
+ import { createHistogram } from "./histogram.js";
4
+ import { createLinePlot } from "./line.js";
5
+ import { createScatterPlot } from "./scatter.js";
6
+
7
+ export function registerBasicChartActions(ProgramClass) {
8
+ ProgramClass.prototype.createBarPlot = createBarPlot;
9
+ ProgramClass.prototype.createHeatmap = createHeatmap;
10
+ ProgramClass.prototype.createHistogram = createHistogram;
11
+ ProgramClass.prototype.createScatterPlot = createScatterPlot;
12
+ ProgramClass.prototype.createLinePlot = createLinePlot;
13
+ }
@@ -0,0 +1,11 @@
1
+ import { createData } from "./create.js";
2
+ import { createDerivedData } from "./derived.js";
3
+ import { createBasicBin2DData } from "./basicBin2d.js";
4
+ import { materializeBin2DData } from "./bin2dMaterialize.js";
5
+
6
+ export function registerBasicDataActions(ProgramClass) {
7
+ ProgramClass.prototype.createData = createData;
8
+ ProgramClass.prototype.createDerivedData = createDerivedData;
9
+ ProgramClass.prototype.createBin2DData = createBasicBin2DData;
10
+ ProgramClass.prototype.materializeBin2DData = materializeBin2DData;
11
+ }
@@ -0,0 +1,43 @@
1
+ import { action } from "../../core/action.js";
2
+ import { validateUserId } from "../../core/identifiers.js";
3
+ import { validateKeys } from "../../core/validation.js";
4
+ import {
5
+ deriveBin2DRows,
6
+ normalizeBin2DTransform
7
+ } from "../../grammar/bin2d.js";
8
+ import { findDataset } from "../../selectors/datasets.js";
9
+
10
+ const OPTIONS = Object.freeze([
11
+ "id", "source", "x", "y", "bins", "extent", "includeEmpty", "members", "as"
12
+ ]);
13
+
14
+ export const createBasicBin2DData = action(
15
+ {
16
+ op: "createBin2DData",
17
+ description: "Create immutable rectangular 2D-bin values."
18
+ },
19
+ function (args = {}) {
20
+ validateKeys(args, OPTIONS, "createBin2DData");
21
+ const owner = validateUserId(args.id, "2D bin dataset id");
22
+ if (this.materializationConfigs.data?.bin2d?.[owner] !== undefined) {
23
+ throw new Error(`2D bin owner "${owner}" already exists.`);
24
+ }
25
+ const source = validateUserId(
26
+ args.source ?? this.context.currentData,
27
+ "Source dataset id"
28
+ );
29
+ const dataset = findDataset(this, source);
30
+ if (dataset?.values === undefined) {
31
+ throw new Error(`Source dataset "${source}" has no values.`);
32
+ }
33
+ const transform = normalizeBin2DTransform({ ...args, id: owner });
34
+ deriveBin2DRows(dataset.values, transform);
35
+ return this
36
+ .createDerivedData({ id: owner, source, transform: [transform] })
37
+ .materializeBin2DData({ id: owner })
38
+ ._withMaterializationConfig(
39
+ ["data", "bin2d", owner],
40
+ { current: owner }
41
+ );
42
+ }
43
+ );
@@ -14,7 +14,7 @@ import {
14
14
  findDataset,
15
15
  findDatasetConsumer
16
16
  } from "../../selectors/datasets.js";
17
- import { MATERIALIZE_OPTIONS, requireDerivedDataset } from "./shared.js";
17
+ export { materializeBin2DData } from "./bin2dMaterialize.js";
18
18
 
19
19
  const OPTIONS = Object.freeze([
20
20
  "id", "source", "x", "y", "bins", "extent", "includeEmpty", "members", "as"
@@ -160,31 +160,6 @@ function applyBin2DRevision(program, { owner, previous, source, transform }) {
160
160
  );
161
161
  }
162
162
 
163
- export const materializeBin2DData = action(
164
- {
165
- op: "materializeBin2DData",
166
- description: "Materialize one immutable rectangular 2D-bin dataset."
167
- },
168
- function (args = {}) {
169
- validateKeys(args, MATERIALIZE_OPTIONS, "materializeBin2DData");
170
- const { id, source, transform } = requireDerivedDataset(
171
- this,
172
- args.id,
173
- "bin2d"
174
- );
175
- const result = deriveBin2DRows(source.values, transform);
176
- return this
177
- .editSemantic({
178
- property: `dataset[${id}].transform`,
179
- value: [{ ...transform, resolved: result.resolved }]
180
- })
181
- .editSemantic({
182
- property: `dataset[${id}].values`,
183
- value: result.values
184
- });
185
- }
186
- );
187
-
188
163
  export const createBin2DData = action(
189
164
  {
190
165
  op: "createBin2DData",
@@ -0,0 +1,29 @@
1
+ import { action } from "../../core/action.js";
2
+ import { validateKeys } from "../../core/validation.js";
3
+ import { deriveBin2DRows } from "../../grammar/bin2d.js";
4
+ import { MATERIALIZE_OPTIONS, requireDerivedDataset } from "./shared.js";
5
+
6
+ export const materializeBin2DData = action(
7
+ {
8
+ op: "materializeBin2DData",
9
+ description: "Materialize one immutable rectangular 2D-bin dataset."
10
+ },
11
+ function (args = {}) {
12
+ validateKeys(args, MATERIALIZE_OPTIONS, "materializeBin2DData");
13
+ const { id, source, transform } = requireDerivedDataset(
14
+ this,
15
+ args.id,
16
+ "bin2d"
17
+ );
18
+ const result = deriveBin2DRows(source.values, transform);
19
+ return this
20
+ .editSemantic({
21
+ property: `dataset[${id}].transform`,
22
+ value: [{ ...transform, resolved: result.resolved }]
23
+ })
24
+ .editSemantic({
25
+ property: `dataset[${id}].values`,
26
+ value: result.values
27
+ });
28
+ }
29
+ );
@@ -23,6 +23,10 @@ import {
23
23
  import { createIntervalData, materializeIntervalData } from "./interval.js";
24
24
  import { createHorizonData, materializeHorizonData } from "./horizon.js";
25
25
  import { createWindowData, materializeWindowData } from "./window.js";
26
+ import {
27
+ createTimeUnitData,
28
+ materializeTimeUnitData
29
+ } from "./timeUnit.js";
26
30
  import {
27
31
  createBin2DData,
28
32
  editBin2DData,
@@ -54,6 +58,8 @@ export function registerDataActions(ProgramClass) {
54
58
  ProgramClass.prototype.materializeHorizonData = materializeHorizonData;
55
59
  ProgramClass.prototype.createWindowData = createWindowData;
56
60
  ProgramClass.prototype.materializeWindowData = materializeWindowData;
61
+ ProgramClass.prototype.createTimeUnitData = createTimeUnitData;
62
+ ProgramClass.prototype.materializeTimeUnitData = materializeTimeUnitData;
57
63
  ProgramClass.prototype.createBin2DData = createBin2DData;
58
64
  ProgramClass.prototype.editBin2DData = editBin2DData;
59
65
  ProgramClass.prototype.materializeBin2DData = materializeBin2DData;