ggaction 0.0.2 → 0.0.3
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 +28 -0
- package/README.md +12 -1
- package/package.json +6 -1
- 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/encodings/appearance.js +11 -0
- package/src/actions/encodings/barWidth.js +5 -3
- package/src/actions/encodings/color.js +120 -25
- package/src/actions/encodings/index.js +2 -0
- package/src/actions/encodings/offset.js +90 -72
- package/src/actions/encodings/position/apply.js +66 -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 +57 -8
- package/src/actions/encodings/position.js +44 -90
- package/src/actions/encodings/ranged.js +28 -6
- package/src/actions/encodings/shared.js +17 -21
- 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 +21 -18
- 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 +9 -4
- package/src/actions/guides/axes/titles.js +7 -5
- package/src/actions/guides/grids/grid.js +144 -15
- package/src/actions/guides/grids/resolve.js +5 -13
- 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 +3 -1
- 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 +5 -5
- package/src/actions/guides/legends/edit.js +2 -6
- 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 +64 -0
- package/src/actions/guides/legends/size.js +12 -14
- package/src/actions/guides/polar/axes/facade.js +219 -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/axes.js +55 -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/index.js +4 -0
- 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/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 +25 -3
- package/src/actions/regression/components.js +1 -1
- package/src/actions/regression/create.js +18 -1
- 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.js +15 -6
- package/src/actions/scales/definitions.js +13 -4
- package/src/actions/scales/edit.js +42 -19
- package/src/actions/scales/materialize.js +57 -408
- 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 +200 -2
- package/src/core/action.js +10 -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/bars/aggregate.js +2 -2
- 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 +20 -1
- package/src/grammar/facets/dependencies.js +185 -0
- package/src/grammar/facets/guides.js +257 -0
- package/src/grammar/facets/scales.js +254 -0
- package/src/grammar/facets.js +122 -0
- package/src/grammar/lineSeries.js +111 -3
- 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/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/text.js +71 -0
- package/src/grammar/ticks.js +50 -13
- 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 +5 -2
- package/src/materialization/bars/grouped.js +75 -37
- package/src/materialization/composition.js +152 -0
- package/src/materialization/compositionSnapshot.js +114 -0
- package/src/materialization/dependencies.js +71 -16
- package/src/materialization/encodings.js +14 -28
- package/src/materialization/facetGuides.js +426 -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.js +220 -14
- 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/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 +24 -16
- package/src/materialization/text.js +156 -0
- package/src/renderers/canvas/index.js +68 -22
- package/types/extension.d.ts +1 -0
- package/types/index.d.ts +56 -1
- package/types/program.d.ts +664 -27
- package/src/actions/marks/line.js +0 -248
- package/src/materialization/selection/items.js +0 -503
|
@@ -11,6 +11,15 @@ import {
|
|
|
11
11
|
resolveCanvasGraphicPlacement
|
|
12
12
|
} from "../../materialization/graphicHierarchy.js";
|
|
13
13
|
|
|
14
|
+
function assertTitleScope(program, operation) {
|
|
15
|
+
if (
|
|
16
|
+
program.compositionSpec !== undefined &&
|
|
17
|
+
program.compositionSpec.type !== "facet"
|
|
18
|
+
) {
|
|
19
|
+
throw new Error(`${operation} is not available on this composition ChartProgram.`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
14
23
|
function hasRotation(graphic) {
|
|
15
24
|
const properties = graphic.items?.map(child => child.properties) ??
|
|
16
25
|
[graphic.properties];
|
|
@@ -179,8 +188,13 @@ export const rematerializeTitle = action(
|
|
|
179
188
|
);
|
|
180
189
|
|
|
181
190
|
export const createTitle = action(
|
|
182
|
-
{
|
|
191
|
+
{
|
|
192
|
+
op: "createTitle",
|
|
193
|
+
description: "Create a chart title and optional subtitle.",
|
|
194
|
+
scope: "any"
|
|
195
|
+
},
|
|
183
196
|
function (args = {}) {
|
|
197
|
+
assertTitleScope(this, "createTitle");
|
|
184
198
|
const options = normalizeTitleOptions(args);
|
|
185
199
|
if (Object.keys(this.semanticSpec.title).length > 0) {
|
|
186
200
|
throw new Error("createTitle requires missing semantic title state.");
|
|
@@ -197,6 +211,9 @@ export const createTitle = action(
|
|
|
197
211
|
next = next.editSemantic({ property: "title.subtitle", value: subtitle });
|
|
198
212
|
}
|
|
199
213
|
next = next._withTitleConfig(config);
|
|
214
|
+
if (next.compositionSpec?.type === "facet") {
|
|
215
|
+
return next.materializeComposition();
|
|
216
|
+
}
|
|
200
217
|
resolveTitleLayout(next, config);
|
|
201
218
|
next = next.createTitleText();
|
|
202
219
|
if (subtitle !== undefined) next = next.createSubtitleText();
|
|
@@ -205,8 +222,13 @@ export const createTitle = action(
|
|
|
205
222
|
);
|
|
206
223
|
|
|
207
224
|
export const editTitle = action(
|
|
208
|
-
{
|
|
225
|
+
{
|
|
226
|
+
op: "editTitle",
|
|
227
|
+
description: "Edit one stable chart title resource.",
|
|
228
|
+
scope: "any"
|
|
229
|
+
},
|
|
209
230
|
function (args = {}) {
|
|
231
|
+
assertTitleScope(this, "editTitle");
|
|
210
232
|
if (this.semanticSpec.title.text === undefined) {
|
|
211
233
|
throw new Error("editTitle requires an existing chart title.");
|
|
212
234
|
}
|
|
@@ -229,7 +251,46 @@ export const editTitle = action(
|
|
|
229
251
|
});
|
|
230
252
|
}
|
|
231
253
|
next = next._withTitleConfig(normalized.config);
|
|
254
|
+
if (next.compositionSpec?.type === "facet") {
|
|
255
|
+
return next.materializeComposition();
|
|
256
|
+
}
|
|
232
257
|
resolveTitleLayout(next, normalized.config);
|
|
233
258
|
return next.rematerializeTitle();
|
|
234
259
|
}
|
|
235
260
|
);
|
|
261
|
+
|
|
262
|
+
export const removeTitle = action(
|
|
263
|
+
{
|
|
264
|
+
op: "removeTitle",
|
|
265
|
+
description: "Remove the complete chart title resource.",
|
|
266
|
+
scope: "any"
|
|
267
|
+
},
|
|
268
|
+
function (args = {}) {
|
|
269
|
+
assertTitleScope(this, "removeTitle");
|
|
270
|
+
noOptions(args, "removeTitle");
|
|
271
|
+
if (
|
|
272
|
+
this.semanticSpec.title.text === undefined &&
|
|
273
|
+
this.titleConfig === undefined &&
|
|
274
|
+
this.graphicSpec.objects.chartTitle === undefined &&
|
|
275
|
+
this.graphicSpec.objects.chartSubtitle === undefined
|
|
276
|
+
) {
|
|
277
|
+
throw new Error("removeTitle requires an existing chart title.");
|
|
278
|
+
}
|
|
279
|
+
let next = this;
|
|
280
|
+
if (next.semanticSpec.title.subtitle !== undefined) {
|
|
281
|
+
next = next.editSemantic({ property: "title.subtitle", remove: true });
|
|
282
|
+
}
|
|
283
|
+
if (next.semanticSpec.title.text !== undefined) {
|
|
284
|
+
next = next.editSemantic({ property: "title.text", remove: true });
|
|
285
|
+
}
|
|
286
|
+
for (const id of ["chartTitle", "chartSubtitle"]) {
|
|
287
|
+
if (next.graphicSpec.objects[id] !== undefined) {
|
|
288
|
+
next = next.editGraphics({ target: id, remove: true });
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
next = next._withoutMaterializationConfig(["title"]);
|
|
292
|
+
return next.compositionSpec?.type === "facet"
|
|
293
|
+
? next.materializeComposition()
|
|
294
|
+
: next;
|
|
295
|
+
}
|
|
296
|
+
);
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
createTitle,
|
|
4
4
|
createTitleText,
|
|
5
5
|
editTitle,
|
|
6
|
+
removeTitle,
|
|
6
7
|
editSubtitleText,
|
|
7
8
|
editTitleText,
|
|
8
9
|
rematerializeTitle
|
|
@@ -11,6 +12,7 @@ import {
|
|
|
11
12
|
export function registerTitleActions(ProgramClass) {
|
|
12
13
|
ProgramClass.prototype.createTitle = createTitle;
|
|
13
14
|
ProgramClass.prototype.editTitle = editTitle;
|
|
15
|
+
ProgramClass.prototype.removeTitle = removeTitle;
|
|
14
16
|
ProgramClass.prototype.createTitleText = createTitleText;
|
|
15
17
|
ProgramClass.prototype.editTitleText = editTitleText;
|
|
16
18
|
ProgramClass.prototype.createSubtitleText = createSubtitleText;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isPlainObject } from "../../core/immutable.js";
|
|
2
2
|
import {
|
|
3
3
|
validateKeys,
|
|
4
|
+
validateOptionObject,
|
|
4
5
|
validateNonEmptyString,
|
|
5
6
|
validateNonNegativeFinite,
|
|
6
7
|
validatePositiveFinite
|
|
@@ -8,6 +9,7 @@ import {
|
|
|
8
9
|
import { resolveGraphicBounds } from "../../layout/canvas.js";
|
|
9
10
|
import { resolveConcreteGraphicBounds } from "../../grammar/schemas/graphicBounds.js";
|
|
10
11
|
import {
|
|
12
|
+
alignedTextAnchor,
|
|
11
13
|
alignedTitleAnchor,
|
|
12
14
|
buildTitleReadingBlock,
|
|
13
15
|
layoutBoundsIntersect,
|
|
@@ -15,6 +17,8 @@ import {
|
|
|
15
17
|
unionTitleBounds
|
|
16
18
|
} from "../../layout/title.js";
|
|
17
19
|
import { DEFAULT_COLORS, DEFAULT_FONT_FAMILY } from "../../theme/defaults.js";
|
|
20
|
+
import { findCanvasGraphic } from
|
|
21
|
+
"../../materialization/graphicHierarchy.js";
|
|
18
22
|
|
|
19
23
|
const OPTIONS = Object.freeze([
|
|
20
24
|
"text", "subtitle", "position", "align", "offset", "gap", "titleStyle",
|
|
@@ -129,10 +133,7 @@ function normalizeConfig(args, defaults, { hasSubtitle }) {
|
|
|
129
133
|
}
|
|
130
134
|
|
|
131
135
|
export function normalizeTitleOptions(args) {
|
|
132
|
-
|
|
133
|
-
throw new TypeError("createTitle options must be a plain object.");
|
|
134
|
-
}
|
|
135
|
-
validateKeys(args, OPTIONS, "createTitle");
|
|
136
|
+
validateOptionObject(args, OPTIONS, "createTitle");
|
|
136
137
|
const text = validateTitleString(args.text, "Chart title text");
|
|
137
138
|
const subtitle = args.subtitle === undefined
|
|
138
139
|
? undefined
|
|
@@ -145,13 +146,11 @@ export function normalizeTitleOptions(args) {
|
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
export function normalizeTitleEditOptions(args, previous, semanticTitle) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
throw new Error("editTitle requires at least one change.");
|
|
154
|
-
}
|
|
149
|
+
validateOptionObject(args, OPTIONS, "editTitle", {
|
|
150
|
+
allowEmpty: false,
|
|
151
|
+
emptyMessage: "editTitle requires at least one change.",
|
|
152
|
+
emptyError: Error
|
|
153
|
+
});
|
|
155
154
|
const text = args.text === undefined
|
|
156
155
|
? semanticTitle.text
|
|
157
156
|
: validateTitleString(args.text, "Chart title text");
|
|
@@ -233,7 +232,7 @@ function validateLayout(program, position, titleBounds, plot, canvas) {
|
|
|
233
232
|
|
|
234
233
|
export function resolveTitleLayout(program, config) {
|
|
235
234
|
const plot = resolveGraphicBounds(program);
|
|
236
|
-
const canvas = program
|
|
235
|
+
const canvas = findCanvasGraphic(program);
|
|
237
236
|
if (
|
|
238
237
|
plot === undefined ||
|
|
239
238
|
![plot.x, plot.y, plot.width, plot.height].every(Number.isFinite) ||
|
|
@@ -256,11 +255,7 @@ export function resolveTitleLayout(program, config) {
|
|
|
256
255
|
let title;
|
|
257
256
|
let subtitle;
|
|
258
257
|
if (horizontal) {
|
|
259
|
-
const x = config.align
|
|
260
|
-
? plot.x
|
|
261
|
-
: config.align === "center"
|
|
262
|
-
? plot.x + plot.width / 2
|
|
263
|
-
: plot.x + plot.width;
|
|
258
|
+
const x = alignedTextAnchor(plot.x, plot.width, config.align);
|
|
264
259
|
const blockTop = config.position === "top"
|
|
265
260
|
? 16 + config.offset
|
|
266
261
|
: plot.y + plot.height + config.offset;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { chart } from "./ChartProgram.js";
|
|
2
|
+
import { hconcatAction, vconcatAction } from "./actions/composition/actions.js";
|
|
3
|
+
|
|
4
|
+
export function hconcat(options) {
|
|
5
|
+
return hconcatAction.call(chart(), options);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function vconcat(options) {
|
|
9
|
+
return vconcatAction.call(chart(), options);
|
|
10
|
+
}
|
package/src/core/ChartProgram.js
CHANGED
|
@@ -15,12 +15,186 @@ import {
|
|
|
15
15
|
createEmptySemanticSpec,
|
|
16
16
|
createTraceRoot
|
|
17
17
|
} from "./specs.js";
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
FACET_SCALE_CHANNELS,
|
|
20
|
+
FACET_SCALE_RESOLUTIONS,
|
|
21
|
+
LEGEND_CONFIG_KINDS
|
|
22
|
+
} from "./vocabulary.js";
|
|
19
23
|
|
|
20
24
|
function ownState(value) {
|
|
21
25
|
return isOwned(value) ? value : cloneAndFreeze(value);
|
|
22
26
|
}
|
|
23
27
|
|
|
28
|
+
function ownChildPrograms(children) {
|
|
29
|
+
if (!isPlainObject(children)) {
|
|
30
|
+
throw new TypeError("ChartProgram children must be a plain object.");
|
|
31
|
+
}
|
|
32
|
+
if (isOwned(children)) return children;
|
|
33
|
+
const owned = {};
|
|
34
|
+
for (const [id, program] of Object.entries(children)) {
|
|
35
|
+
if (typeof id !== "string" || id.length === 0) {
|
|
36
|
+
throw new TypeError("ChartProgram child IDs must be non-empty strings.");
|
|
37
|
+
}
|
|
38
|
+
if (!(program instanceof ChartProgram)) {
|
|
39
|
+
throw new TypeError(`ChartProgram child "${id}" must be a ChartProgram.`);
|
|
40
|
+
}
|
|
41
|
+
owned[id] = program;
|
|
42
|
+
}
|
|
43
|
+
return freezeOwned(owned);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function ownCompositionSpec(compositionSpec, children) {
|
|
47
|
+
const childIds = Object.keys(children);
|
|
48
|
+
if (compositionSpec === undefined) {
|
|
49
|
+
if (childIds.length > 0) {
|
|
50
|
+
throw new Error("ChartProgram children require a compositionSpec.");
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
if (!isPlainObject(compositionSpec)) {
|
|
55
|
+
throw new TypeError("ChartProgram compositionSpec must be a plain object.");
|
|
56
|
+
}
|
|
57
|
+
const allowed = [
|
|
58
|
+
"id", "type", "direction", "children", "columns", "gap", "align",
|
|
59
|
+
"padding", "facet"
|
|
60
|
+
];
|
|
61
|
+
const unknown = Object.keys(compositionSpec).find(key => !allowed.includes(key));
|
|
62
|
+
if (unknown !== undefined) {
|
|
63
|
+
throw new Error(`Unknown compositionSpec property "${unknown}".`);
|
|
64
|
+
}
|
|
65
|
+
if (typeof compositionSpec.id !== "string" || compositionSpec.id.length === 0) {
|
|
66
|
+
throw new TypeError("compositionSpec.id must be a non-empty string.");
|
|
67
|
+
}
|
|
68
|
+
const facet = compositionSpec.type === "facet";
|
|
69
|
+
if (compositionSpec.type !== undefined && !facet) {
|
|
70
|
+
throw new Error(`Unknown compositionSpec type "${compositionSpec.type}".`);
|
|
71
|
+
}
|
|
72
|
+
if (facet) {
|
|
73
|
+
if (compositionSpec.direction !== undefined) {
|
|
74
|
+
throw new Error("Facet compositionSpec does not use direction.");
|
|
75
|
+
}
|
|
76
|
+
} else if (!["horizontal", "vertical"].includes(compositionSpec.direction)) {
|
|
77
|
+
throw new Error("compositionSpec.direction must be horizontal or vertical.");
|
|
78
|
+
}
|
|
79
|
+
if (
|
|
80
|
+
!Array.isArray(compositionSpec.children) ||
|
|
81
|
+
compositionSpec.children.length < (facet ? 1 : 2) ||
|
|
82
|
+
!compositionSpec.children.every(id => typeof id === "string" && id.length > 0)
|
|
83
|
+
) {
|
|
84
|
+
throw new TypeError(
|
|
85
|
+
facet
|
|
86
|
+
? "Facet compositionSpec.children requires at least one child ID."
|
|
87
|
+
: "compositionSpec.children requires at least two child IDs."
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
if (new Set(compositionSpec.children).size !== compositionSpec.children.length) {
|
|
91
|
+
throw new Error("compositionSpec.children must not contain duplicate IDs.");
|
|
92
|
+
}
|
|
93
|
+
if (
|
|
94
|
+
compositionSpec.children.length !== childIds.length ||
|
|
95
|
+
compositionSpec.children.some(id => !Object.hasOwn(children, id))
|
|
96
|
+
) {
|
|
97
|
+
throw new Error("compositionSpec.children must match ChartProgram children exactly.");
|
|
98
|
+
}
|
|
99
|
+
if (facet) {
|
|
100
|
+
if (
|
|
101
|
+
!Number.isInteger(compositionSpec.columns) ||
|
|
102
|
+
compositionSpec.columns <= 0 ||
|
|
103
|
+
compositionSpec.columns > compositionSpec.children.length
|
|
104
|
+
) {
|
|
105
|
+
throw new RangeError(
|
|
106
|
+
"Facet compositionSpec.columns must be a positive integer no larger than its children."
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
if (!isPlainObject(compositionSpec.facet)) {
|
|
110
|
+
throw new TypeError("Facet compositionSpec.facet must be a plain object.");
|
|
111
|
+
}
|
|
112
|
+
const facetKeys = ["data", "field", "values", "scales", "guides"];
|
|
113
|
+
const unknownFacet = Object.keys(compositionSpec.facet).find(
|
|
114
|
+
key => !facetKeys.includes(key)
|
|
115
|
+
);
|
|
116
|
+
if (unknownFacet !== undefined) {
|
|
117
|
+
throw new Error(`Unknown compositionSpec.facet property "${unknownFacet}".`);
|
|
118
|
+
}
|
|
119
|
+
for (const property of ["data", "field"]) {
|
|
120
|
+
if (
|
|
121
|
+
typeof compositionSpec.facet[property] !== "string" ||
|
|
122
|
+
compositionSpec.facet[property].length === 0
|
|
123
|
+
) {
|
|
124
|
+
throw new TypeError(`compositionSpec.facet.${property} must be a non-empty string.`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const values = compositionSpec.facet.values;
|
|
128
|
+
if (
|
|
129
|
+
!Array.isArray(values) ||
|
|
130
|
+
values.length !== compositionSpec.children.length ||
|
|
131
|
+
values.some(value => !(
|
|
132
|
+
typeof value === "string" ||
|
|
133
|
+
typeof value === "boolean" ||
|
|
134
|
+
(typeof value === "number" && Number.isFinite(value))
|
|
135
|
+
)) ||
|
|
136
|
+
new Set(values).size !== values.length
|
|
137
|
+
) {
|
|
138
|
+
throw new TypeError(
|
|
139
|
+
"compositionSpec.facet.values must contain one unique scalar per child."
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
const scales = compositionSpec.facet.scales;
|
|
143
|
+
if (
|
|
144
|
+
!isPlainObject(scales) ||
|
|
145
|
+
Object.keys(scales).length !== FACET_SCALE_CHANNELS.length ||
|
|
146
|
+
FACET_SCALE_CHANNELS.some(channel =>
|
|
147
|
+
!FACET_SCALE_RESOLUTIONS.includes(scales[channel])
|
|
148
|
+
) ||
|
|
149
|
+
Object.keys(scales).some(channel => !FACET_SCALE_CHANNELS.includes(channel))
|
|
150
|
+
) {
|
|
151
|
+
throw new Error(
|
|
152
|
+
"compositionSpec.facet.scales requires one shared or independent policy per supported channel."
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
const guides = compositionSpec.facet.guides;
|
|
156
|
+
if (
|
|
157
|
+
!isPlainObject(guides) ||
|
|
158
|
+
Object.keys(guides).some(key => !["axes", "legend"].includes(key)) ||
|
|
159
|
+
!["each", "outer"].includes(guides.axes) ||
|
|
160
|
+
![false, "shared"].includes(guides.legend)
|
|
161
|
+
) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
'compositionSpec.facet.guides requires axes "each" or "outer" and legend false or "shared".'
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
} else if (
|
|
167
|
+
compositionSpec.columns !== undefined ||
|
|
168
|
+
compositionSpec.facet !== undefined
|
|
169
|
+
) {
|
|
170
|
+
throw new Error("Concat compositionSpec does not accept facet properties.");
|
|
171
|
+
}
|
|
172
|
+
if (!Number.isFinite(compositionSpec.gap) || compositionSpec.gap < 0) {
|
|
173
|
+
throw new RangeError("compositionSpec.gap must be a non-negative finite number.");
|
|
174
|
+
}
|
|
175
|
+
if (!["start", "center", "end"].includes(compositionSpec.align)) {
|
|
176
|
+
throw new Error("compositionSpec.align must be start, center, or end.");
|
|
177
|
+
}
|
|
178
|
+
if (!isPlainObject(compositionSpec.padding)) {
|
|
179
|
+
throw new TypeError("compositionSpec.padding must be a plain object.");
|
|
180
|
+
}
|
|
181
|
+
for (const side of ["top", "right", "bottom", "left"]) {
|
|
182
|
+
if (!Number.isFinite(compositionSpec.padding[side]) || compositionSpec.padding[side] < 0) {
|
|
183
|
+
throw new RangeError(
|
|
184
|
+
`compositionSpec.padding.${side} must be a non-negative finite number.`
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const paddingKeys = Object.keys(compositionSpec.padding);
|
|
189
|
+
if (
|
|
190
|
+
paddingKeys.length !== 4 ||
|
|
191
|
+
paddingKeys.some(key => !["top", "right", "bottom", "left"].includes(key))
|
|
192
|
+
) {
|
|
193
|
+
throw new Error("compositionSpec.padding must contain exactly four sides.");
|
|
194
|
+
}
|
|
195
|
+
return ownState(compositionSpec);
|
|
196
|
+
}
|
|
197
|
+
|
|
24
198
|
function createMaterializationConfigs(
|
|
25
199
|
markConfigs,
|
|
26
200
|
guideConfigs,
|
|
@@ -54,6 +228,8 @@ export class ChartProgram {
|
|
|
54
228
|
guideConfigs = {},
|
|
55
229
|
titleConfig,
|
|
56
230
|
canvasConfig,
|
|
231
|
+
children = {},
|
|
232
|
+
compositionSpec,
|
|
57
233
|
context = {},
|
|
58
234
|
trace = createTraceRoot(),
|
|
59
235
|
actionStack = [],
|
|
@@ -70,6 +246,8 @@ export class ChartProgram {
|
|
|
70
246
|
canvasConfig
|
|
71
247
|
)
|
|
72
248
|
: ownState(materializationConfigs);
|
|
249
|
+
this.children = ownChildPrograms(children);
|
|
250
|
+
this.compositionSpec = ownCompositionSpec(compositionSpec, this.children);
|
|
73
251
|
this.context = ownState(context);
|
|
74
252
|
this.trace = ownState(trace);
|
|
75
253
|
this.actionStack = ownState(actionStack);
|
|
@@ -118,6 +296,8 @@ export class ChartProgram {
|
|
|
118
296
|
graphicSpec = this.graphicSpec,
|
|
119
297
|
resolvedScales = this.resolvedScales,
|
|
120
298
|
materializationConfigs = this.materializationConfigs,
|
|
299
|
+
children = this.children,
|
|
300
|
+
compositionSpec = this.compositionSpec,
|
|
121
301
|
context = this.context,
|
|
122
302
|
trace = this.trace,
|
|
123
303
|
actionStack = this.actionStack,
|
|
@@ -128,6 +308,8 @@ export class ChartProgram {
|
|
|
128
308
|
graphicSpec,
|
|
129
309
|
resolvedScales,
|
|
130
310
|
materializationConfigs,
|
|
311
|
+
children,
|
|
312
|
+
compositionSpec,
|
|
131
313
|
context,
|
|
132
314
|
trace,
|
|
133
315
|
actionStack,
|
|
@@ -135,6 +317,22 @@ export class ChartProgram {
|
|
|
135
317
|
});
|
|
136
318
|
}
|
|
137
319
|
|
|
320
|
+
_assertUnitProgram(operation) {
|
|
321
|
+
if (this.compositionSpec !== undefined) {
|
|
322
|
+
throw new Error(`${operation} is not available on a composition ChartProgram.`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
_assertCompositionProgram(operation) {
|
|
327
|
+
if (this.compositionSpec === undefined) {
|
|
328
|
+
throw new Error(`${operation} requires a composition ChartProgram.`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
_withCompositionState({ children, compositionSpec, graphicSpec = this.graphicSpec }) {
|
|
333
|
+
return this._clone({ children, compositionSpec, graphicSpec });
|
|
334
|
+
}
|
|
335
|
+
|
|
138
336
|
_withContext(patch = {}) {
|
|
139
337
|
if (!isPlainObject(patch)) {
|
|
140
338
|
throw new TypeError("Context patch must be a plain object.");
|
|
@@ -257,7 +455,7 @@ export class ChartProgram {
|
|
|
257
455
|
}
|
|
258
456
|
|
|
259
457
|
_withGridConfig(direction, config) {
|
|
260
|
-
if (!["horizontal", "vertical"].includes(direction)) {
|
|
458
|
+
if (!["horizontal", "vertical", "theta", "radial"].includes(direction)) {
|
|
261
459
|
throw new Error(`Unknown grid direction "${direction}".`);
|
|
262
460
|
}
|
|
263
461
|
if (!isPlainObject(config)) {
|
package/src/core/action.js
CHANGED
|
@@ -12,6 +12,8 @@ function summarizeObject(value, ancestors = new WeakSet()) {
|
|
|
12
12
|
summary[`${key}Count`] = item.length;
|
|
13
13
|
} else if (isPlainObject(item)) {
|
|
14
14
|
summary[key] = summarizeObject(item, ancestors);
|
|
15
|
+
} else if (item !== null && typeof item === "object") {
|
|
16
|
+
summary[`${key}Type`] = item.constructor?.name ?? "object";
|
|
15
17
|
} else {
|
|
16
18
|
summary[key] = item;
|
|
17
19
|
}
|
|
@@ -100,11 +102,19 @@ export function action(metadata, implementation) {
|
|
|
100
102
|
throw new TypeError("Action implementation must be a function.");
|
|
101
103
|
}
|
|
102
104
|
|
|
105
|
+
const scope = metadata.scope ?? "unit";
|
|
106
|
+
if (!["unit", "composition", "any"].includes(scope)) {
|
|
107
|
+
throw new Error(`Unknown action scope "${scope}".`);
|
|
108
|
+
}
|
|
109
|
+
|
|
103
110
|
return function wrappedAction(args = {}) {
|
|
104
111
|
if (!isPlainObject(args)) {
|
|
105
112
|
throw new TypeError("Action arguments must be a plain object.");
|
|
106
113
|
}
|
|
107
114
|
|
|
115
|
+
if (scope === "unit") this._assertUnitProgram(metadata.op);
|
|
116
|
+
if (scope === "composition") this._assertCompositionProgram(metadata.op);
|
|
117
|
+
|
|
108
118
|
const entered = this._enterAction({
|
|
109
119
|
...metadata,
|
|
110
120
|
args: summarizeArgs(args)
|
package/src/core/validation.js
CHANGED
|
@@ -8,6 +8,22 @@ export function validateKeys(value, supported, label) {
|
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
export function validateOptionObject(value, supported, label, {
|
|
12
|
+
allowEmpty = true,
|
|
13
|
+
plainObjectMessage = `${label} options must be a plain object.`,
|
|
14
|
+
emptyMessage = `${label} requires at least one option.`,
|
|
15
|
+
emptyError = TypeError
|
|
16
|
+
} = {}) {
|
|
17
|
+
if (!isPlainObject(value)) {
|
|
18
|
+
throw new TypeError(plainObjectMessage);
|
|
19
|
+
}
|
|
20
|
+
if (supported !== undefined) validateKeys(value, supported, label);
|
|
21
|
+
if (!allowEmpty && Object.keys(value).length === 0) {
|
|
22
|
+
throw new emptyError(emptyMessage);
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
|
|
11
27
|
export function noOptions(args, operation) {
|
|
12
28
|
if (!isPlainObject(args) || Object.keys(args).length > 0) {
|
|
13
29
|
throw new Error(`${operation} does not accept options.`);
|
package/src/core/vocabulary.js
CHANGED
|
@@ -3,15 +3,30 @@ export const MARK_TYPES = Object.freeze([
|
|
|
3
3
|
"line",
|
|
4
4
|
"bar",
|
|
5
5
|
"area",
|
|
6
|
-
"
|
|
6
|
+
"arc",
|
|
7
|
+
"rule",
|
|
8
|
+
"text",
|
|
9
|
+
"rect"
|
|
7
10
|
]);
|
|
8
11
|
|
|
12
|
+
export const MARK_GRAPHIC_TYPES = Object.freeze({
|
|
13
|
+
point: Object.freeze(["circle", "rect", "path", "collection"]),
|
|
14
|
+
line: Object.freeze(["path"]),
|
|
15
|
+
bar: Object.freeze(["rect"]),
|
|
16
|
+
area: Object.freeze(["path"]),
|
|
17
|
+
arc: Object.freeze(["path"]),
|
|
18
|
+
rule: Object.freeze(["line"]),
|
|
19
|
+
text: Object.freeze(["text"]),
|
|
20
|
+
rect: Object.freeze(["rect"])
|
|
21
|
+
});
|
|
22
|
+
|
|
9
23
|
export const ENCODING_CHANNELS = Object.freeze([
|
|
10
24
|
"x",
|
|
11
25
|
"y",
|
|
12
26
|
"x2",
|
|
13
27
|
"y2",
|
|
14
28
|
"xOffset",
|
|
29
|
+
"yOffset",
|
|
15
30
|
"theta",
|
|
16
31
|
"radius",
|
|
17
32
|
"color",
|
|
@@ -19,14 +34,149 @@ export const ENCODING_CHANNELS = Object.freeze([
|
|
|
19
34
|
"size",
|
|
20
35
|
"shape",
|
|
21
36
|
"group",
|
|
22
|
-
"opacity"
|
|
37
|
+
"opacity",
|
|
38
|
+
"text"
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
const CARTESIAN_MARK_TYPES = Object.freeze([
|
|
42
|
+
"point", "line", "bar", "area", "rule", "text", "rect"
|
|
23
43
|
]);
|
|
44
|
+
const POLAR_MARK_TYPES = Object.freeze(["point", "line", "arc"]);
|
|
45
|
+
|
|
46
|
+
export const POSITION_CHANNEL_DEFINITIONS = Object.freeze({
|
|
47
|
+
x: Object.freeze({
|
|
48
|
+
family: "cartesian",
|
|
49
|
+
role: "primary",
|
|
50
|
+
scaleChannel: "x",
|
|
51
|
+
coordinate: Object.freeze({ id: "main", type: "cartesian" }),
|
|
52
|
+
guideChannel: "x",
|
|
53
|
+
gridDirection: "vertical",
|
|
54
|
+
markTypes: CARTESIAN_MARK_TYPES
|
|
55
|
+
}),
|
|
56
|
+
y: Object.freeze({
|
|
57
|
+
family: "cartesian",
|
|
58
|
+
role: "primary",
|
|
59
|
+
scaleChannel: "y",
|
|
60
|
+
coordinate: Object.freeze({ id: "main", type: "cartesian" }),
|
|
61
|
+
guideChannel: "y",
|
|
62
|
+
gridDirection: "horizontal",
|
|
63
|
+
markTypes: CARTESIAN_MARK_TYPES
|
|
64
|
+
}),
|
|
65
|
+
x2: Object.freeze({
|
|
66
|
+
family: "cartesian",
|
|
67
|
+
role: "secondary",
|
|
68
|
+
scaleChannel: "x",
|
|
69
|
+
coordinate: Object.freeze({ id: "main", type: "cartesian" }),
|
|
70
|
+
markTypes: Object.freeze(["area", "rule", "rect"])
|
|
71
|
+
}),
|
|
72
|
+
y2: Object.freeze({
|
|
73
|
+
family: "cartesian",
|
|
74
|
+
role: "secondary",
|
|
75
|
+
scaleChannel: "y",
|
|
76
|
+
coordinate: Object.freeze({ id: "main", type: "cartesian" }),
|
|
77
|
+
markTypes: Object.freeze(["area", "rule", "rect"])
|
|
78
|
+
}),
|
|
79
|
+
xOffset: Object.freeze({
|
|
80
|
+
family: "cartesian",
|
|
81
|
+
role: "offset",
|
|
82
|
+
scaleChannel: "xOffset",
|
|
83
|
+
coordinate: Object.freeze({ id: "main", type: "cartesian" }),
|
|
84
|
+
markTypes: Object.freeze(["bar"])
|
|
85
|
+
}),
|
|
86
|
+
yOffset: Object.freeze({
|
|
87
|
+
family: "cartesian",
|
|
88
|
+
role: "offset",
|
|
89
|
+
scaleChannel: "yOffset",
|
|
90
|
+
coordinate: Object.freeze({ id: "main", type: "cartesian" }),
|
|
91
|
+
markTypes: Object.freeze(["bar"])
|
|
92
|
+
}),
|
|
93
|
+
theta: Object.freeze({
|
|
94
|
+
family: "polar",
|
|
95
|
+
role: "primary",
|
|
96
|
+
scaleChannel: "theta",
|
|
97
|
+
coordinate: Object.freeze({ id: "polar", type: "polar" }),
|
|
98
|
+
guideChannel: "theta",
|
|
99
|
+
gridDirection: "theta",
|
|
100
|
+
markTypes: POLAR_MARK_TYPES
|
|
101
|
+
}),
|
|
102
|
+
radius: Object.freeze({
|
|
103
|
+
family: "polar",
|
|
104
|
+
role: "primary",
|
|
105
|
+
scaleChannel: "radius",
|
|
106
|
+
coordinate: Object.freeze({ id: "polar", type: "polar" }),
|
|
107
|
+
guideChannel: "radius",
|
|
108
|
+
gridDirection: "radial",
|
|
109
|
+
markTypes: POLAR_MARK_TYPES
|
|
110
|
+
})
|
|
111
|
+
});
|
|
24
112
|
|
|
25
113
|
export const SCALED_ENCODING_CHANNELS = Object.freeze(
|
|
26
|
-
ENCODING_CHANNELS.filter(channel =>
|
|
114
|
+
ENCODING_CHANNELS.filter(channel => !["group", "text"].includes(channel))
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
export const POSITION_ENCODING_CHANNELS = Object.freeze(
|
|
118
|
+
Object.keys(POSITION_CHANNEL_DEFINITIONS)
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
export const OFFSET_POSITION_CHANNELS = Object.freeze(
|
|
122
|
+
POSITION_ENCODING_CHANNELS.filter(channel =>
|
|
123
|
+
POSITION_CHANNEL_DEFINITIONS[channel].role === "offset"
|
|
124
|
+
)
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
export const CARTESIAN_POSITION_CHANNELS = Object.freeze(
|
|
128
|
+
POSITION_ENCODING_CHANNELS.filter(channel =>
|
|
129
|
+
POSITION_CHANNEL_DEFINITIONS[channel].family === "cartesian" &&
|
|
130
|
+
POSITION_CHANNEL_DEFINITIONS[channel].role === "primary"
|
|
131
|
+
)
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
export const POLAR_POSITION_CHANNELS = Object.freeze(
|
|
135
|
+
POSITION_ENCODING_CHANNELS.filter(channel =>
|
|
136
|
+
POSITION_CHANNEL_DEFINITIONS[channel].family === "polar" &&
|
|
137
|
+
POSITION_CHANNEL_DEFINITIONS[channel].role === "primary"
|
|
138
|
+
)
|
|
27
139
|
);
|
|
28
140
|
|
|
29
|
-
export const POSITION_CHANNELS = Object.freeze([
|
|
141
|
+
export const POSITION_CHANNELS = Object.freeze([
|
|
142
|
+
...CARTESIAN_POSITION_CHANNELS,
|
|
143
|
+
...POLAR_POSITION_CHANNELS
|
|
144
|
+
]);
|
|
145
|
+
|
|
146
|
+
export const FACET_SCALE_CHANNELS = Object.freeze([
|
|
147
|
+
"x",
|
|
148
|
+
"y",
|
|
149
|
+
"xOffset",
|
|
150
|
+
"yOffset",
|
|
151
|
+
"color",
|
|
152
|
+
"size",
|
|
153
|
+
"shape",
|
|
154
|
+
"opacity",
|
|
155
|
+
"strokeDash"
|
|
156
|
+
]);
|
|
157
|
+
|
|
158
|
+
export const FACET_SCALE_RESOLUTIONS = Object.freeze([
|
|
159
|
+
"shared",
|
|
160
|
+
"independent"
|
|
161
|
+
]);
|
|
162
|
+
|
|
163
|
+
export function getPositionChannelDefinition(channel) {
|
|
164
|
+
return POSITION_CHANNEL_DEFINITIONS[channel];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function normalizePositionScaleChannel(channel) {
|
|
168
|
+
return POSITION_CHANNEL_DEFINITIONS[channel]?.scaleChannel ?? channel;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function positionChannelsForFamily(family) {
|
|
172
|
+
return POSITION_CHANNELS.filter(channel =>
|
|
173
|
+
POSITION_CHANNEL_DEFINITIONS[channel].family === family
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function getMarkGraphicTypes(markType) {
|
|
178
|
+
return MARK_GRAPHIC_TYPES[markType];
|
|
179
|
+
}
|
|
30
180
|
|
|
31
181
|
export const COLOR_LAYOUTS = Object.freeze([
|
|
32
182
|
"stack",
|