ggaction 0.0.4 → 0.0.6

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 (169) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +35 -13
  3. package/package.json +18 -7
  4. package/src/actions/boxPlots/create.js +13 -7
  5. package/src/actions/boxPlots/edit.js +484 -36
  6. package/src/actions/boxPlots/materialize.js +3 -0
  7. package/src/actions/boxPlots/options.js +11 -4
  8. package/src/actions/boxPlots/resolve.js +10 -2
  9. package/src/actions/charts/bar.js +50 -0
  10. package/src/actions/charts/heatmap.js +205 -0
  11. package/src/actions/charts/histogram.js +58 -0
  12. package/src/actions/charts/index.js +15 -0
  13. package/src/actions/charts/line.js +69 -0
  14. package/src/actions/charts/parallel.js +76 -0
  15. package/src/actions/charts/scatter.js +56 -0
  16. package/src/actions/charts/shared.js +121 -0
  17. package/src/actions/composition/actions.js +13 -3
  18. package/src/actions/coordinates/parallel.js +50 -0
  19. package/src/actions/data/bin2d.js +260 -0
  20. package/src/actions/data/density.js +54 -13
  21. package/src/actions/data/derived.js +22 -0
  22. package/src/actions/data/filter.js +5 -0
  23. package/src/actions/data/gradientProfile.js +65 -0
  24. package/src/actions/data/horizon.js +71 -0
  25. package/src/actions/data/index.js +34 -2
  26. package/src/actions/data/intervalEdit.js +76 -0
  27. package/src/actions/data/window.js +50 -0
  28. package/src/actions/distributions/revision.js +326 -0
  29. package/src/actions/encodings/appearance.js +35 -1
  30. package/src/actions/encodings/color/index.js +17 -1
  31. package/src/actions/encodings/color/layout.js +8 -0
  32. package/src/actions/encodings/density/resolve.js +109 -0
  33. package/src/actions/encodings/density.js +330 -90
  34. package/src/actions/encodings/horizon/resolve.js +283 -0
  35. package/src/actions/encodings/horizon.js +364 -0
  36. package/src/actions/encodings/index.js +8 -0
  37. package/src/actions/encodings/parallel.js +115 -0
  38. package/src/actions/encodings/pathOrder.js +131 -0
  39. package/src/actions/encodings/position/apply.js +73 -4
  40. package/src/actions/encodings/position/index.js +15 -2
  41. package/src/actions/encodings/position/policies/arc.js +27 -4
  42. package/src/actions/encodings/position/policies/area.js +33 -2
  43. package/src/actions/encodings/position/policies/index.js +7 -2
  44. package/src/actions/encodings/position/policies/line.js +18 -2
  45. package/src/actions/encodings/position/resolve.js +24 -2
  46. package/src/actions/encodings/ranged.js +3 -1
  47. package/src/actions/encodings/remove.js +268 -0
  48. package/src/actions/encodings/ruleAppearance.js +88 -10
  49. package/src/actions/errorBands/edit.js +121 -13
  50. package/src/actions/errorBars/edit.js +45 -8
  51. package/src/actions/errorBars/options.js +7 -1
  52. package/src/actions/facets/actions.js +137 -3
  53. package/src/actions/facets/derive.js +33 -3
  54. package/src/actions/facets/replay.js +7 -36
  55. package/src/actions/gradientPlots/components.js +177 -0
  56. package/src/actions/gradientPlots/create.js +96 -0
  57. package/src/actions/gradientPlots/edit.js +501 -0
  58. package/src/actions/gradientPlots/index.js +24 -0
  59. package/src/actions/gradientPlots/materialize.js +287 -0
  60. package/src/actions/gradientPlots/options.js +120 -0
  61. package/src/actions/gradientPlots/paint.js +107 -0
  62. package/src/actions/gradientPlots/rebind.js +51 -0
  63. package/src/actions/gradientPlots/resolve.js +81 -0
  64. package/src/actions/guides/applicability.js +44 -17
  65. package/src/actions/guides/axes/axes.js +37 -3
  66. package/src/actions/guides/axes/edit.js +129 -47
  67. package/src/actions/guides/axes/index.js +2 -0
  68. package/src/actions/guides/axes/labels.js +11 -6
  69. package/src/actions/guides/axes/parallel/resolve.js +86 -0
  70. package/src/actions/guides/axes/parallel.js +228 -0
  71. package/src/actions/guides/axes/titles.js +11 -8
  72. package/src/actions/guides/guides.js +60 -5
  73. package/src/actions/guides/legends/categorical/actions.js +67 -19
  74. package/src/actions/guides/legends/continuous/common.js +3 -3
  75. package/src/actions/guides/legends/edit.js +63 -0
  76. package/src/actions/guides/legends/index.js +2 -0
  77. package/src/actions/guides/legends/remove.js +124 -30
  78. package/src/actions/guides/legends/strokeWidth.js +198 -0
  79. package/src/actions/index.js +6 -0
  80. package/src/actions/marks/arc/actions.js +46 -14
  81. package/src/actions/marks/area/actions.js +60 -136
  82. package/src/actions/marks/area/materialize.js +163 -0
  83. package/src/actions/marks/index.js +5 -1
  84. package/src/actions/marks/line/actions.js +99 -80
  85. package/src/actions/marks/line/materialize.js +146 -0
  86. package/src/actions/marks/point/create.js +76 -0
  87. package/src/actions/marks/point/edit.js +97 -0
  88. package/src/actions/marks/point/index.js +11 -1
  89. package/src/actions/marks/point/jitter.js +72 -0
  90. package/src/actions/marks/point/{actions.js → materialize.js} +183 -172
  91. package/src/actions/marks/rect/actions.js +3 -0
  92. package/src/actions/marks/remove.js +13 -2
  93. package/src/actions/marks/rule/actions.js +29 -4
  94. package/src/actions/marks/text/actions.js +19 -4
  95. package/src/actions/marks/text/index.js +7 -1
  96. package/src/actions/marks/text/layout.js +321 -0
  97. package/src/actions/primitives/semanticValidation/dataset.js +22 -0
  98. package/src/actions/primitives/semanticValidation/guide.js +43 -0
  99. package/src/actions/primitives/semanticValidation/index.js +23 -0
  100. package/src/actions/primitives/semanticValidation/layer.js +74 -0
  101. package/src/actions/primitives/semanticValidation/scale.js +74 -0
  102. package/src/actions/primitives/semanticValidation/shared.js +5 -0
  103. package/src/actions/primitives/semanticValue.js +1 -204
  104. package/src/actions/regression/edit.js +247 -88
  105. package/src/actions/scales/consumers/common.js +18 -0
  106. package/src/actions/scales/consumers/families.js +14 -3
  107. package/src/actions/scales/definitions.js +30 -0
  108. package/src/actions/scales/edit.js +6 -204
  109. package/src/actions/scales/editPolicy.js +214 -0
  110. package/src/actions/scales/materialize.js +1 -1
  111. package/src/actions/scales/patch.js +26 -0
  112. package/src/actions/selection/actions.js +150 -5
  113. package/src/actions/selection/index.js +6 -0
  114. package/src/actions/violinPlots/create.js +198 -0
  115. package/src/actions/violinPlots/index.js +5 -0
  116. package/src/core/textMetrics.js +47 -0
  117. package/src/core/vocabulary.js +14 -2
  118. package/src/grammar/arcs.js +49 -12
  119. package/src/grammar/areaSeries.js +41 -5
  120. package/src/grammar/bin2d.js +333 -0
  121. package/src/grammar/categoricalDensity.js +192 -0
  122. package/src/grammar/coordinates.js +1 -1
  123. package/src/grammar/curveCommands.js +3 -1
  124. package/src/grammar/density.js +252 -26
  125. package/src/grammar/facets/dependencies.js +3 -11
  126. package/src/grammar/facets/index.js +3 -1
  127. package/src/grammar/gradientProfile.js +213 -0
  128. package/src/grammar/horizon.js +455 -0
  129. package/src/grammar/jitter.js +197 -0
  130. package/src/grammar/lineSeries.js +69 -0
  131. package/src/grammar/paint.js +88 -0
  132. package/src/grammar/parallelCoordinates.js +213 -0
  133. package/src/grammar/pathOrder.js +35 -0
  134. package/src/grammar/pointShapes.js +24 -0
  135. package/src/grammar/rules.js +1 -0
  136. package/src/grammar/scales/appearance.js +21 -0
  137. package/src/grammar/schemas/concreteGraphic.js +11 -1
  138. package/src/grammar/schemas/graphicBounds.js +19 -37
  139. package/src/grammar/schemas/semanticPath.js +17 -2
  140. package/src/grammar/transforms.js +100 -11
  141. package/src/grammar/window.js +339 -0
  142. package/src/layout/labels.js +270 -0
  143. package/src/layout/text.js +4 -1
  144. package/src/layout/title.js +10 -20
  145. package/src/materialization/dataProvenance.js +56 -2
  146. package/src/materialization/dependencies.js +9 -14
  147. package/src/materialization/guides/resources.js +74 -24
  148. package/src/materialization/horizon.js +29 -0
  149. package/src/materialization/layout.js +15 -0
  150. package/src/materialization/marks/capabilities.js +21 -1
  151. package/src/materialization/marks/index.js +3 -0
  152. package/src/materialization/marks/pathOrder.js +71 -0
  153. package/src/materialization/marks/policies.js +6 -2
  154. package/src/materialization/scaleGuideDependencies.js +8 -0
  155. package/src/materialization/scales/resolve.js +2 -0
  156. package/src/materialization/selection/items/path.js +27 -1
  157. package/src/materialization/selection/items/point.js +1 -5
  158. package/src/materialization/selection/items/rect.js +21 -0
  159. package/src/materialization/selection/styles.js +20 -9
  160. package/src/materialization/text.js +12 -4
  161. package/src/renderers/canvas/fill.js +33 -0
  162. package/src/renderers/canvas/index.js +7 -0
  163. package/src/renderers/canvas/path.js +7 -4
  164. package/src/renderers/canvas/rect.js +9 -3
  165. package/src/selectors/datasets.js +4 -0
  166. package/src/theme/defaults.js +4 -0
  167. package/types/extension.d.ts +6 -0
  168. package/types/index.d.ts +77 -0
  169. package/types/program.d.ts +703 -17
@@ -0,0 +1,71 @@
1
+ import { isAggregate } from "../../grammar/aggregate.js";
2
+ import { findTransformPolicy } from "../../grammar/transforms.js";
3
+ import { findDataset } from "../../selectors/datasets.js";
4
+
5
+ function hasRowPreservingProvenance(program, dataset) {
6
+ const visited = new Set();
7
+ let current = dataset;
8
+ while (current?.source !== undefined) {
9
+ if (visited.has(current.id) || current.transform?.length !== 1) return false;
10
+ visited.add(current.id);
11
+ const policy = findTransformPolicy(current.transform[0].type);
12
+ if (
13
+ policy?.facetTopology !== "rowPreserving" &&
14
+ policy?.provenanceTransparent !== true
15
+ ) {
16
+ return false;
17
+ }
18
+ current = findDataset(program, current.source);
19
+ if (current === undefined) return false;
20
+ }
21
+ return current !== undefined;
22
+ }
23
+
24
+ function isPolar(layer) {
25
+ return layer.encoding?.theta !== undefined ||
26
+ layer.encoding?.radius !== undefined;
27
+ }
28
+
29
+ function isCompositePath(program, layer) {
30
+ const config = program.markConfigs[layer.id];
31
+ return config?.errorBand !== undefined ||
32
+ config?.errorBandBoundary !== undefined ||
33
+ config?.regression !== undefined;
34
+ }
35
+
36
+ export function pathOrderCompatibility(program, layer) {
37
+ if (!["line", "area"].includes(layer?.mark?.type)) {
38
+ return { compatible: false, reason: "requires a line or area mark" };
39
+ }
40
+ if (isPolar(layer)) {
41
+ return { compatible: false, reason: "does not support Polar paths" };
42
+ }
43
+ const dataset = findDataset(program, layer.data);
44
+ if (dataset === undefined) {
45
+ return { compatible: false, reason: "requires an existing dataset" };
46
+ }
47
+ if (!hasRowPreservingProvenance(program, dataset)) {
48
+ return {
49
+ compatible: false,
50
+ reason: "requires raw or row-preserving source data"
51
+ };
52
+ }
53
+ if (isCompositePath(program, layer)) {
54
+ return {
55
+ compatible: false,
56
+ reason: "does not support generated error or regression paths"
57
+ };
58
+ }
59
+ if (layer.mark.type === "line" && isAggregate(layer.encoding?.y?.aggregate)) {
60
+ return { compatible: false, reason: "does not support aggregate lines" };
61
+ }
62
+ return { compatible: true, dataset };
63
+ }
64
+
65
+ export function assertPathOrderCompatible(program, layer, operation) {
66
+ const result = pathOrderCompatibility(program, layer);
67
+ if (!result.compatible) {
68
+ throw new Error(`${operation} ${result.reason} on mark "${layer?.id}".`);
69
+ }
70
+ return result.dataset;
71
+ }
@@ -6,7 +6,8 @@ import {
6
6
  canMaterializePoint,
7
7
  canMaterializeRect,
8
8
  canMaterializeRule,
9
- canMaterializeText
9
+ canMaterializeText,
10
+ isIntentionallyEmptyArea
10
11
  } from "./capabilities.js";
11
12
 
12
13
  const MARK_MATERIALIZATION_POLICIES = Object.freeze({
@@ -33,7 +34,10 @@ const MARK_MATERIALIZATION_POLICIES = Object.freeze({
33
34
  canMaterialize: canMaterializeArea,
34
35
  op: "rematerializeAreaMark",
35
36
  positionEncoding: Object.freeze({ incomplete: "scale", scaleFirst: true }),
36
- encoding: Object.freeze({ sharedChannels: Object.freeze(["color"]) }),
37
+ encoding: Object.freeze({
38
+ sharedChannels: Object.freeze(["color"]),
39
+ skipRematerialization: isIntentionallyEmptyArea
40
+ }),
37
41
  scaleApplication: Object.freeze({ default: "defer" })
38
42
  }),
39
43
  arc: Object.freeze({
@@ -21,6 +21,8 @@ function usesPositionalScale(program, id) {
21
21
  return program.semanticSpec.layers.some(layer =>
22
22
  POSITION_ENCODING_CHANNELS.some(
23
23
  channel => layer.encoding?.[channel]?.scale === id
24
+ ) || (layer.encoding?.parallel?.dimensions ?? []).some(
25
+ dimension => dimension.scale === id
24
26
  )
25
27
  );
26
28
  }
@@ -62,6 +64,12 @@ export function planScaleGuideRematerialization(program, id) {
62
64
  const guides = [];
63
65
  const objects = program.graphicSpec?.objects ?? {};
64
66
  const guideConfigs = program.guideConfigs ?? {};
67
+ if (guideConfigs.axis?.parallel?.axes?.scales?.includes(id)) {
68
+ guides.push({
69
+ op: "rematerializeParallelAxes",
70
+ args: { target: guideConfigs.axis.parallel.axes.target }
71
+ });
72
+ }
65
73
  for (const axis of CARTESIAN_AXES) {
66
74
  if (
67
75
  objects[axis.lineObject] &&
@@ -18,6 +18,7 @@ import {
18
18
  resolveShapeRange,
19
19
  resolveSizeRange,
20
20
  resolveStrokeDashRange,
21
+ resolveStrokeWidthRange,
21
22
  resolveTransformedDomain,
22
23
  SCALE_ROLES,
23
24
  validateLinearScaleType,
@@ -92,6 +93,7 @@ function resolveRange({
92
93
  if (channel === "shape") return resolveShapeRange(scale.range);
93
94
  if (channel === "size") return resolveSizeRange(scale.range);
94
95
  if (channel === "opacity") return resolveOpacityRange(scale.range);
96
+ if (channel === "strokeWidth") return resolveStrokeWidthRange(scale.range);
95
97
  if (isOrdinalOffset) return undefined;
96
98
  return resolveArcAutoPositionRange({
97
99
  consumers,
@@ -4,7 +4,12 @@ import {
4
4
  } from "../../../grammar/areaSeries.js";
5
5
  import { deriveLineSeries } from "../../../grammar/lineSeries.js";
6
6
  import { findUpstreamTransform } from "../../dataProvenance.js";
7
- import { finalizeItems, uniqueFields } from "./common.js";
7
+ import {
8
+ channelMapFromRow,
9
+ finalizeItems,
10
+ ownFields,
11
+ uniqueFields
12
+ } from "./common.js";
8
13
 
9
14
  function rowsForSeries(rows, key) {
10
15
  const entries = Object.entries(key);
@@ -34,6 +39,27 @@ function seriesDefinitions(layer, rows, series) {
34
39
  }
35
40
 
36
41
  export function resolveLineItems(program, layer, dataset) {
42
+ if (layer.encoding?.parallel !== undefined) {
43
+ const parallel = layer.encoding.parallel;
44
+ const definitions = dataset.values.flatMap((row, index) => {
45
+ const incomplete = parallel.dimensions.some(dimension => {
46
+ const value = row[dimension.field];
47
+ return dimension.fieldType === "quantitative"
48
+ ? !Number.isFinite(value)
49
+ : typeof value !== "string" && !Number.isFinite(value);
50
+ });
51
+ if (incomplete && parallel.missing === "drop-row") return [];
52
+ return [{
53
+ key: parallel.key === undefined
54
+ ? `${layer.id}/row/source:${index}`
55
+ : `${layer.id}/row/${String(row[parallel.key])}`,
56
+ fields: ownFields(row),
57
+ channels: channelMapFromRow(row, layer),
58
+ members: [row]
59
+ }];
60
+ });
61
+ return finalizeItems(program, layer, "row", definitions, "path");
62
+ }
37
63
  const derived = deriveLineSeries(dataset.values, layer);
38
64
  return finalizeItems(
39
65
  program,
@@ -18,11 +18,7 @@ export function resolvePointItems(program, layer, dataset) {
18
18
  );
19
19
  if (
20
20
  !Array.isArray(graphic?.items) ||
21
- !completePosition ||
22
- (
23
- layer.encoding?.size?.scale === undefined &&
24
- !Number.isFinite(program.markConfigs[layer.id]?.radius)
25
- )
21
+ !completePosition
26
22
  ) {
27
23
  throw new Error(`Point mark "${layer.id}" is incomplete for selection.`);
28
24
  }
@@ -1,16 +1,37 @@
1
1
  import { resolveRectRows } from "../../rect.js";
2
2
  import {
3
+ channelMapFromRow,
3
4
  concreteProperties,
4
5
  finalizeItems,
5
6
  itemKey,
6
7
  ownFields
7
8
  } from "./common.js";
8
9
 
10
+ function resolveGradientPlotItems(program, layer, dataset) {
11
+ const graphic = program.graphicSpec.objects[layer.id];
12
+ return finalizeItems(
13
+ program,
14
+ layer,
15
+ "rect",
16
+ dataset.values.map((row, index) => ({
17
+ key: itemKey(layer, "rect", index),
18
+ fields: ownFields(row),
19
+ channels: channelMapFromRow(row, layer),
20
+ properties: concreteProperties(graphic.items[index]?.properties),
21
+ members: [row]
22
+ })),
23
+ "rect"
24
+ );
25
+ }
26
+
9
27
  export function resolveRectItems(program, layer, dataset) {
10
28
  const graphic = program.graphicSpec.objects[layer.id];
11
29
  if (graphic?.type !== "rect" || !Array.isArray(graphic.items)) {
12
30
  throw new Error(`Rect mark "${layer.id}" is incomplete for selection.`);
13
31
  }
32
+ if (program.markConfigs[layer.id]?.gradientPlot?.materialized === true) {
33
+ return resolveGradientPlotItems(program, layer, dataset);
34
+ }
14
35
  const rows = resolveRectRows(program, layer, dataset);
15
36
  const definitions = rows.map((item, graphicIndex) => ({
16
37
  key: itemKey(layer, "rect", item.sourceIndex),
@@ -80,19 +80,19 @@ export function normalizePointHighlightStyle(args) {
80
80
  };
81
81
  }
82
82
 
83
- function normalizeRectangularHighlightStyle(args, mark) {
84
- for (const option of ["shape", "size", "offset", "strokeDash"]) {
83
+ function normalizeRectangularHighlightStyle(args, mark, { offset = false } = {}) {
84
+ for (const option of ["shape", "size", "strokeDash"]) {
85
85
  if (args[option] !== undefined) {
86
86
  throw new Error(`${mark} highlight does not support ${option}.`);
87
87
  }
88
88
  }
89
+ if (!offset && args.offset !== undefined) {
90
+ throw new Error(`${mark} highlight does not support offset.`);
91
+ }
89
92
  if (args.color !== undefined && args.fill !== undefined) {
90
93
  throw new Error(`${mark} highlight accepts color or fill, not both.`);
91
94
  }
92
- const fill = validateNonEmptyString(
93
- args.fill ?? args.color ?? DEFAULT_COLORS.highlight,
94
- `${mark} highlight fill`
95
- );
95
+ const explicitFill = args.fill ?? args.color;
96
96
  const opacity = args.opacity === undefined
97
97
  ? undefined
98
98
  : validateUnitInterval(args.opacity, `${mark} highlight opacity`);
@@ -105,11 +105,22 @@ function normalizeRectangularHighlightStyle(args, mark) {
105
105
  if (strokeWidth !== undefined && stroke === undefined) {
106
106
  throw new Error(`${mark} highlight strokeWidth requires stroke.`);
107
107
  }
108
+ const hasAppearanceOverride = opacity !== undefined || stroke !== undefined ||
109
+ strokeWidth !== undefined || args.offset !== undefined;
110
+ const fill = explicitFill === undefined && hasAppearanceOverride
111
+ ? undefined
112
+ : validateNonEmptyString(
113
+ explicitFill ?? DEFAULT_COLORS.highlight,
114
+ `${mark} highlight fill`
115
+ );
108
116
  return {
109
- fill,
117
+ ...(fill === undefined ? {} : { fill }),
110
118
  ...(opacity === undefined ? {} : { opacity }),
111
119
  ...(stroke === undefined ? {} : { stroke }),
112
- ...(strokeWidth === undefined ? {} : { strokeWidth })
120
+ ...(strokeWidth === undefined ? {} : { strokeWidth }),
121
+ ...(offset && args.offset !== undefined
122
+ ? { offset: normalizeOffset(args.offset) }
123
+ : {})
113
124
  };
114
125
  }
115
126
 
@@ -118,7 +129,7 @@ export function normalizeBarHighlightStyle(args) {
118
129
  }
119
130
 
120
131
  export function normalizeRectHighlightStyle(args) {
121
- return normalizeRectangularHighlightStyle(args, "Rect");
132
+ return normalizeRectangularHighlightStyle(args, "Rect", { offset: true });
122
133
  }
123
134
 
124
135
  function rejectHighlightOptions(args, mark, options) {
@@ -120,13 +120,14 @@ function resolveSourceTextItems(program, layer, config) {
120
120
  }
121
121
  const items = resolveMarkItems(program, source.id);
122
122
  return items.flatMap(item => {
123
+ const anchor = sourceAnchor(program, source, item);
123
124
  const concrete = concreteItem(
124
125
  sourceTextConfig(config, source, item),
125
- sourceAnchor(program, source, item),
126
+ anchor,
126
127
  contentValue(layer.encoding.text, item, source),
127
128
  layer.encoding.text.format
128
129
  );
129
- return concrete === undefined ? [] : [concrete];
130
+ return concrete === undefined ? [] : [{ graphic: concrete, anchor }];
130
131
  });
131
132
  }
132
133
 
@@ -145,12 +146,19 @@ function resolveRowTextItems(program, layer, config) {
145
146
  contentValue(layer.encoding.text, row),
146
147
  layer.encoding.text.format
147
148
  );
148
- return concrete === undefined ? [] : [concrete];
149
+ return concrete === undefined
150
+ ? []
151
+ : [{ graphic: concrete, anchor: { x: x[index], y: y[index] } }];
149
152
  });
150
153
  }
151
154
 
152
- export function resolveTextGraphicItems(program, layer, config) {
155
+ export function resolveTextGraphicEntries(program, layer, config) {
153
156
  return layer.source === undefined
154
157
  ? resolveRowTextItems(program, layer, config)
155
158
  : resolveSourceTextItems(program, layer, config);
156
159
  }
160
+
161
+ export function resolveTextGraphicItems(program, layer, config) {
162
+ return resolveTextGraphicEntries(program, layer, config)
163
+ .map(entry => entry.graphic);
164
+ }
@@ -0,0 +1,33 @@
1
+ import {
2
+ isLinearGradientPaint,
3
+ resolveLinearGradientCoordinates,
4
+ validateFillPaint
5
+ } from "../../grammar/paint.js";
6
+
7
+ export function applyCanvasFill(context, fill, bounds, graphicId) {
8
+ validateFillPaint(fill, `${graphicId}.fill`);
9
+ if (!isLinearGradientPaint(fill)) {
10
+ context.fillStyle = fill;
11
+ return fill;
12
+ }
13
+ if (typeof context.createLinearGradient !== "function") {
14
+ throw new TypeError(
15
+ `Graphic "${graphicId}" requires Canvas context createLinearGradient().`
16
+ );
17
+ }
18
+ const coordinates = resolveLinearGradientCoordinates(fill, bounds);
19
+ const gradient = context.createLinearGradient(
20
+ coordinates.from.x,
21
+ coordinates.from.y,
22
+ coordinates.to.x,
23
+ coordinates.to.y
24
+ );
25
+ if (gradient === null || typeof gradient !== "object" || typeof gradient.addColorStop !== "function") {
26
+ throw new TypeError(
27
+ `Graphic "${graphicId}" requires a Canvas linear gradient with addColorStop().`
28
+ );
29
+ }
30
+ for (const stop of fill.stops) gradient.addColorStop(stop.offset, stop.color);
31
+ context.fillStyle = gradient;
32
+ return gradient;
33
+ }
@@ -120,6 +120,13 @@ export function render(program, context, { pixelRatio = 1 } = {}) {
120
120
 
121
121
  context.canvas.width = Math.round(width * pixelRatio);
122
122
  context.canvas.height = Math.round(height * pixelRatio);
123
+ if (
124
+ context.canvas.style !== null &&
125
+ typeof context.canvas.style === "object"
126
+ ) {
127
+ context.canvas.style.width = `${width}px`;
128
+ context.canvas.style.height = `${height}px`;
129
+ }
123
130
  context.save();
124
131
 
125
132
  try {
@@ -1,6 +1,8 @@
1
1
  import { requireFiniteProperty } from "./validation.js";
2
2
  import { validateConcreteGraphicProperties } from
3
3
  "../../grammar/schemas/concreteGraphic.js";
4
+ import { resolvePathCommandBounds } from "../../grammar/schemas/graphicBounds.js";
5
+ import { applyCanvasFill } from "./fill.js";
4
6
 
5
7
  function drawPath(context, child, collectionId) {
6
8
  const properties = child.properties ?? {};
@@ -19,9 +21,6 @@ function drawPath(context, child, collectionId) {
19
21
  if (!hasFill && !hasStroke) {
20
22
  throw new Error(`Graphic "${graphicId}" requires a fill or stroke property.`);
21
23
  }
22
- if (hasFill && typeof properties.fill !== "string") {
23
- throw new Error(`Graphic "${graphicId}" requires a string fill property.`);
24
- }
25
24
  if (hasFill && commands.at(-1).op !== "Z") {
26
25
  throw new Error(`Graphic "${graphicId}" requires a final Z command when filled.`);
27
26
  }
@@ -75,7 +74,11 @@ function drawPath(context, child, collectionId) {
75
74
  }
76
75
 
77
76
  if (hasFill) {
78
- context.fillStyle = properties.fill;
77
+ const bounds = resolvePathCommandBounds(commands);
78
+ if (bounds === undefined) {
79
+ throw new Error(`Graphic "${graphicId}" requires finite path fill bounds.`);
80
+ }
81
+ applyCanvasFill(context, properties.fill, bounds, graphicId);
79
82
  context.fill();
80
83
  }
81
84
  if (hasStroke) {
@@ -1,6 +1,7 @@
1
1
  import { requireFiniteProperty } from "./validation.js";
2
2
  import { validateConcreteGraphicProperties } from
3
3
  "../../grammar/schemas/concreteGraphic.js";
4
+ import { applyCanvasFill } from "./fill.js";
4
5
 
5
6
  function drawRect(context, child, collectionId) {
6
7
  const properties = child.properties ?? {};
@@ -21,8 +22,8 @@ function drawRect(context, child, collectionId) {
21
22
  `Graphic "${graphicId}" requires non-negative rect dimensions and strokeWidth.`
22
23
  );
23
24
  }
24
- if (typeof properties.fill !== "string") {
25
- throw new Error(`Graphic "${graphicId}" requires a string fill property.`);
25
+ if (properties.fill === undefined) {
26
+ throw new Error(`Graphic "${graphicId}" requires a fill property.`);
26
27
  }
27
28
  if (typeof properties.stroke !== "string") {
28
29
  throw new Error(`Graphic "${graphicId}" requires a string stroke property.`);
@@ -34,7 +35,12 @@ function drawRect(context, child, collectionId) {
34
35
  }
35
36
 
36
37
  context.globalAlpha = opacity;
37
- context.fillStyle = properties.fill;
38
+ applyCanvasFill(context, properties.fill, {
39
+ left: x,
40
+ right: x + width,
41
+ top: y,
42
+ bottom: y + height
43
+ }, graphicId);
38
44
  context.fillRect(x, y, width, height);
39
45
  context.strokeStyle = properties.stroke;
40
46
  context.lineWidth = strokeWidth;
@@ -6,6 +6,10 @@ export function hasDataset(program, id) {
6
6
  return findDataset(program, id) !== undefined;
7
7
  }
8
8
 
9
+ export function findDatasetConsumer(program, source) {
10
+ return program.semanticSpec.datasets.find(dataset => dataset.source === source);
11
+ }
12
+
9
13
  export function requireDataset(program, id, label = `Dataset "${id}"`) {
10
14
  const dataset = findDataset(program, id);
11
15
  if (dataset === undefined) throw new Error(`${label} does not exist.`);
@@ -1,10 +1,14 @@
1
1
  export const DEFAULT_FONT_FAMILY = "sans-serif";
2
2
 
3
+ export const DEFAULT_POINT_RADIUS = 3;
4
+
3
5
  export const DEFAULT_COLORS = Object.freeze({
4
6
  mark: "#4c78a8",
5
7
  text: "#334155",
6
8
  strongText: "#0f172a",
7
9
  mutedText: "#64748b",
10
+ axis: "#475569",
11
+ axisTitle: "#1e293b",
8
12
  grid: "#e2e8f0",
9
13
  border: "#cbd5e1",
10
14
  sizeSymbol: "#94a3b8",
@@ -2,6 +2,12 @@ import type { ActionOptions } from "./program.js";
2
2
  import { ChartProgram } from "./program.js";
3
3
 
4
4
  export { ChartProgram };
5
+ export type {
6
+ FillPaint,
7
+ LinearGradientPaint,
8
+ LinearGradientPoint,
9
+ LinearGradientStop
10
+ } from "./program.js";
5
11
 
6
12
  export interface ActionMetadata {
7
13
  op: string;
package/types/index.d.ts CHANGED
@@ -13,6 +13,14 @@ export type {
13
13
  AxisTitleOptions,
14
14
  AxisValue,
15
15
  BarWidthOptions,
16
+ BoxPlotOptions,
17
+ BinnedHeatmapColorOptions,
18
+ BinnedHeatmapOptions,
19
+ BinnedHeatmapPositionChannel,
20
+ Bin2DCounts,
21
+ Bin2DDataOptions,
22
+ Bin2DExtent,
23
+ Bin2DOutputFields,
16
24
  CanvasOptions,
17
25
  CategoricalEncodingOptions,
18
26
  ColorLayout,
@@ -32,23 +40,45 @@ export type {
32
40
  CreateCoordinateOptions,
33
41
  CreateGuidesOptions,
34
42
  CreateGridOptions,
43
+ CreateBarPlotOptions,
44
+ CreateHeatmapOptions,
45
+ CreateHistogramOptions,
35
46
  CreateRegressionBandOptions,
36
47
  CreateRegressionLineOptions,
48
+ CreateLinePlotOptions,
49
+ CreateParallelCoordinatesOptions,
50
+ CreateScatterPlotOptions,
37
51
  CreateScaleOptions,
38
52
  DashPattern,
39
53
  DashScaleOptions,
40
54
  DashStyle,
41
55
  DatasetDensityTransform,
56
+ DatasetHorizonTransform,
57
+ DensityPlacement,
58
+ BaselineDensityPlacement,
59
+ CategoryDensityPlacement,
60
+ DensityPlacementSide,
61
+ DensityPlacementSplit,
62
+ DensityPlacementWidth,
63
+ DensityWidthResolution,
64
+ DatasetBin2DOutputFields,
65
+ DatasetBin2DTransform,
42
66
  DatasetFilterTransform,
43
67
  DatasetIntervalOutputFields,
44
68
  DatasetIntervalTransform,
69
+ DatasetWindowOperation,
70
+ DatasetWindowSort,
71
+ DatasetWindowTransform,
45
72
  DatasetRegressionTransform,
46
73
  DatasetScalar,
47
74
  DatasetTransform,
48
75
  EditGraphicsOptions,
76
+ EditBin2DDataOptions,
77
+ EditHorizonOptions,
49
78
  EditCompositionLayoutOptions,
50
79
  EditFacetHeadersOptions,
51
80
  EditAxisOptions,
81
+ EditGradientPlotOptions,
52
82
  EditGridDirectionsOptions,
53
83
  EditGridOptions,
54
84
  EditErrorBandBoundaryOptions,
@@ -63,6 +93,7 @@ export type {
63
93
  EditScaleOptions,
64
94
  EditSemanticOptions,
65
95
  FieldType,
96
+ FillPaint,
66
97
  FacetCompositionSpec,
67
98
  FacetGuideOptions,
68
99
  FacetOptions,
@@ -71,9 +102,39 @@ export type {
71
102
  GraphicObject,
72
103
  GraphicSpec,
73
104
  GraphicType,
105
+ GradientPlotAppearanceOptions,
106
+ GradientPlotCenterOptions,
107
+ GradientPlotDensityOptions,
108
+ GradientPlotOptions,
109
+ GradientPlotPositionChannel,
110
+ ViolinPlotAreaOptions,
111
+ ViolinPlotColorOptions,
112
+ ViolinPlotDensityOptions,
113
+ ViolinPlotOptions,
114
+ ViolinPlotPositionChannel,
115
+ ViolinPlotSplitOptions,
116
+ LinearGradientPaint,
117
+ LinearGradientPoint,
118
+ LinearGradientStop,
74
119
  GridDirectionOptions,
120
+ HeatmapBaseOptions,
121
+ HeatmapBinOptions,
75
122
  HistogramEncodingOptions,
123
+ HorizonEncodingOptions,
124
+ HorizonMissingPolicy,
125
+ HorizonOutputFields,
126
+ HorizonOverflowPolicy,
127
+ HorizonPaletteOptions,
128
+ HorizonResolution,
129
+ HorizonXEncoding,
130
+ HorizonYEncoding,
76
131
  HighlightMarksOptions,
132
+ JitterMaxOffset,
133
+ JitterPointsOptions,
134
+ LabelLayoutAxis,
135
+ LabelLayoutBounds,
136
+ LabelLayoutOptions,
137
+ LabelLeaderOptions,
77
138
  LegendOptions,
78
139
  EditLegendOptions,
79
140
  ContinuousColorInterpolation,
@@ -81,7 +142,12 @@ export type {
81
142
  OpacityEncodingOptions,
82
143
  OpacityScaleOptions,
83
144
  OffsetScaleOptions,
145
+ PathOrderEncodingOptions,
146
+ ParallelCoordinatesEncodingOptions,
147
+ ParallelDimension,
148
+ ParallelMissingPolicy,
84
149
  PointShape,
150
+ PreGriddedHeatmapOptions,
85
151
  MarkSelector,
86
152
  MarkGraphicProperty,
87
153
  Palette,
@@ -100,8 +166,11 @@ export type {
100
166
  EditRectMarkOptions,
101
167
  RemoveAxisOptions,
102
168
  RemoveGridOptions,
169
+ RemoveJitterOptions,
170
+ RemoveLabelLayoutOptions,
103
171
  RemoveLegendOptions,
104
172
  RemoveMarkOptions,
173
+ RemovePathOrderOptions,
105
174
  ScaleRange,
106
175
  ScaleOptions,
107
176
  ScaleType,
@@ -113,6 +182,10 @@ export type {
113
182
  SemanticSpec,
114
183
  SelectMarksOptions,
115
184
  StrokeDashEncodingOptions,
185
+ StrokeWidthEncodingOptions,
186
+ StrokeWidthScaleOptions,
187
+ ThetaEncodingOptions,
188
+ ThetaScaleOptions,
116
189
  TraceNode,
117
190
  TitleOptions,
118
191
  EditTitleOptions,
@@ -123,6 +196,10 @@ export type {
123
196
  TextMarkOptions,
124
197
  XAxisPosition,
125
198
  YAxisPosition,
199
+ WindowDataOptions,
200
+ WindowOperation,
201
+ WindowSort,
202
+ WindowSortOrder,
126
203
  XOffsetEncodingOptions
127
204
  } from "./program.js";
128
205