ggaction 0.0.16 → 0.0.18
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 +66 -0
- package/README.md +3 -2
- package/knowledge/action-card.schema.json +1 -1
- package/knowledge/action-cards.json +1 -1
- package/knowledge/intent-taxonomy.json +1 -1
- package/knowledge/mcp-resources.json +1 -1
- package/knowledge/task-resolver.js +1 -1
- package/package.json +10 -4
- package/src/BasicChartProgram.js +1 -1
- package/src/accessibility.js +3 -2
- package/src/actions/boxPlots/create.js +14 -4
- package/src/actions/boxPlots/edit.js +39 -33
- package/src/actions/boxPlots/materialize.js +7 -3
- package/src/actions/boxPlots/options.js +37 -11
- package/src/actions/canvas/actions.js +70 -95
- package/src/actions/canvas/fitting.js +16 -1
- package/src/actions/canvas/index.js +3 -0
- package/src/actions/canvas/plotBorder.js +28 -0
- package/src/actions/charts/area.js +2 -2
- package/src/actions/charts/bar.js +34 -7
- package/src/actions/charts/ecdf.js +4 -18
- package/src/actions/charts/endpoints.js +4 -18
- package/src/actions/charts/heatmap.js +4 -4
- package/src/actions/charts/index.js +2 -0
- package/src/actions/charts/interval-regression.js +2 -2
- package/src/actions/charts/line.js +1 -1
- package/src/actions/charts/pie.js +3 -47
- package/src/actions/charts/radial.js +34 -12
- package/src/actions/charts/scatter.js +16 -2
- package/src/actions/charts/shared.js +2 -2
- package/src/actions/charts/text.js +35 -0
- package/src/actions/composition/actions.js +6 -2
- package/src/actions/data/bin.js +1 -1
- package/src/actions/data/box.js +2 -1
- package/src/actions/data/create.js +18 -5
- package/src/actions/data/density.js +1 -1
- package/src/actions/data/derived.js +15 -1
- package/src/actions/data/edit.js +20 -7
- package/src/actions/data/filter.js +15 -4
- package/src/actions/data/index.js +3 -0
- package/src/actions/data/interval.js +2 -0
- package/src/actions/data/regression.js +6 -2
- package/src/actions/data/revise.js +37 -5
- package/src/actions/data/shared.js +33 -4
- package/src/actions/data/sort.js +21 -0
- package/src/actions/data/stack.js +3 -1
- package/src/actions/data/summary.js +1 -1
- package/src/actions/encodings/angle.js +4 -1
- package/src/actions/encodings/appearance.js +43 -31
- package/src/actions/encodings/barWidth.js +36 -11
- package/src/actions/encodings/channels.js +0 -1
- package/src/actions/encodings/color/continuous.js +5 -2
- package/src/actions/encodings/color/index.js +5 -2
- package/src/actions/encodings/color/policy.js +10 -3
- package/src/actions/encodings/density/resolve.js +1 -6
- package/src/actions/encodings/offset.js +4 -1
- package/src/actions/encodings/position/policies/arc.js +12 -10
- package/src/actions/encodings/position/policies/area.js +3 -3
- package/src/actions/encodings/position/policies/bar.js +15 -6
- package/src/actions/encodings/position/policies/line.js +17 -6
- package/src/actions/encodings/position/resolve.js +9 -3
- package/src/actions/encodings/ranged.js +23 -1
- package/src/actions/encodings/remove.js +3 -2
- package/src/actions/encodings/ruleAppearance.js +4 -1
- package/src/actions/encodings/seriesLayout.js +1 -0
- package/src/actions/encodings/stroke.js +4 -1
- package/src/actions/encodings/strokeDash.js +6 -1
- package/src/actions/encodings/text.js +12 -4
- package/src/actions/errorBands/create.js +2 -2
- package/src/actions/errorBands/edit.js +2 -1
- package/src/actions/facets/actions.js +158 -240
- package/src/actions/gradientPlots/resolve.js +2 -17
- package/src/actions/guides/applicability.js +1 -1
- package/src/actions/guides/axes/axis.js +16 -4
- package/src/actions/guides/axes/edit.js +4 -4
- package/src/actions/guides/axes/labels.js +120 -46
- package/src/actions/guides/axes/lines.js +6 -5
- package/src/actions/guides/axes/parallel/lifecycle.js +1 -1
- package/src/actions/guides/axes/parallel/policy.js +8 -7
- package/src/actions/guides/axes/remove.js +3 -2
- package/src/actions/guides/axes/tickGroups.js +5 -2
- package/src/actions/guides/axes/ticks.js +6 -3
- package/src/actions/guides/axes/titles.js +71 -26
- package/src/actions/guides/basic.js +2 -0
- package/src/actions/guides/facadeLegend.js +10 -2
- package/src/actions/guides/grids/grid.js +7 -1
- package/src/actions/guides/grids/resolve.js +59 -12
- package/src/actions/guides/legends/blocks.js +3 -1
- package/src/actions/guides/legends/categorical/actions.js +70 -17
- package/src/actions/guides/legends/categorical/components.js +12 -8
- package/src/actions/guides/legends/categorical/index.js +6 -0
- package/src/actions/guides/legends/categorical/layout.js +37 -8
- package/src/actions/guides/legends/categorical/options.js +31 -20
- package/src/actions/guides/legends/categorical/resolve.js +2 -2
- package/src/actions/guides/legends/categorical/symbols.js +18 -12
- package/src/actions/guides/legends/categorical/validation.js +1 -6
- package/src/actions/guides/legends/continuous/colorSymbols.js +1 -0
- package/src/actions/guides/legends/continuous/common.js +1 -447
- package/src/actions/guides/legends/continuous/gradient.js +47 -34
- package/src/actions/guides/legends/continuous/interval.js +4 -4
- package/src/actions/guides/legends/continuous/opacity.js +19 -12
- package/src/actions/guides/legends/edit.js +1 -604
- package/src/actions/guides/legends/focused.js +2 -2
- package/src/actions/guides/legends/group.js +1 -0
- package/src/actions/guides/legends/lane.js +1 -516
- package/src/actions/guides/legends/remove.js +3 -0
- package/src/actions/guides/legends/sampling.js +1 -1
- package/src/actions/guides/legends/size.js +51 -43
- package/src/actions/guides/legends/strokeWidth.js +11 -29
- package/src/actions/guides/legends/target.js +3 -3
- package/src/actions/guides/legends/transition.js +21 -7
- package/src/actions/guides/polar/axes/labels.js +4 -2
- package/src/actions/guides/polar/axes/titles.js +7 -5
- package/src/actions/guides/polar/resolve.js +6 -14
- package/src/actions/index.js +16 -16
- package/src/actions/marks/arc/actions.js +3 -10
- package/src/actions/marks/area/actions.js +8 -5
- package/src/actions/marks/area/materialize.js +8 -9
- package/src/actions/marks/bar/create.js +17 -4
- package/src/actions/marks/bar/edit.js +18 -7
- package/src/actions/marks/bar/materialize.js +9 -6
- package/src/actions/marks/line/actions.js +11 -4
- package/src/actions/marks/line/materialize.js +9 -5
- package/src/actions/marks/point/create.js +8 -1
- package/src/actions/marks/point/edit.js +13 -6
- package/src/actions/marks/point/materialize.js +4 -2
- package/src/actions/marks/rect/actions.js +26 -11
- package/src/actions/marks/remove.js +2 -0
- package/src/actions/marks/rule/actions.js +31 -12
- package/src/actions/marks/text/actions.js +42 -11
- package/src/actions/marks/text/layout.js +3 -0
- package/src/actions/marks/tick/actions.js +23 -8
- package/src/actions/primitives/semanticAction.js +37 -3
- package/src/actions/primitives/semanticValidation/dataset.js +13 -1
- package/src/actions/primitives/semanticValidation/layer.js +26 -2
- package/src/actions/primitives/semanticValidation/scale.js +6 -0
- package/src/actions/regression/create.js +26 -5
- package/src/actions/regression/edit.js +50 -15
- package/src/actions/regression/follow.js +22 -0
- package/src/actions/regression/resolve.js +2 -6
- package/src/actions/scales/channels.js +20 -15
- package/src/actions/scales/consumers/common.js +2 -1
- package/src/actions/scales/consumers/families.js +16 -5
- package/src/actions/scales/create.js +10 -4
- package/src/actions/scales/definitions.js +1 -425
- package/src/actions/scales/edit.js +3 -3
- package/src/actions/scales/editPolicy.js +9 -3
- package/src/actions/scales/materialize.js +1 -1
- package/src/actions/scales/parallel.js +1 -1
- package/src/actions/scales/patch.js +1 -1
- package/src/actions/scales/preview.js +49 -2
- package/src/actions/scales/quantitativeColor.js +3 -3
- package/src/actions/selection/actions.js +1 -13
- package/src/actions/theme/reconcile.js +83 -27
- package/src/actions/theme/state.js +12 -0
- package/src/actions/titles/actions.js +5 -18
- package/src/actions/titles/resolve.js +41 -19
- package/src/actions/violinPlots/edit.js +3 -20
- package/src/core/action.js +3 -2
- package/src/core/compositionState.js +4 -1
- package/src/core/font.js +9 -2
- package/src/core/optionRequirements.js +1 -0
- package/src/core/programState.js +5 -1
- package/src/core/resourceReferences.js +3 -0
- package/src/core/textMetricProfile.js +3 -2
- package/src/core/textMetrics.js +41 -4
- package/src/core/validation.js +26 -0
- package/src/core/vocabulary.js +4 -0
- package/src/grammar/aggregate.js +4 -18
- package/src/grammar/arcs.js +1 -386
- package/src/grammar/areaPaint.js +10 -0
- package/src/grammar/areaSeries.js +18 -10
- package/src/grammar/bars/geometry.js +30 -3
- package/src/grammar/bars/policy.js +12 -1
- package/src/grammar/bin.js +41 -22
- package/src/grammar/bin2d.js +1 -6
- package/src/grammar/boxPlot.js +56 -9
- package/src/grammar/categoryOrder.js +1 -6
- package/src/grammar/complete.js +15 -19
- package/src/grammar/computed.js +92 -25
- package/src/grammar/curveCommands.js +29 -16
- package/src/grammar/datasetSchema.js +382 -0
- package/src/grammar/density.js +53 -5
- package/src/grammar/displayLabels.js +28 -0
- package/src/grammar/ecdf.js +2 -9
- package/src/grammar/facets/dependencies.js +6 -2
- package/src/grammar/facets/guides.js +5 -0
- package/src/grammar/facets/index.js +2 -5
- package/src/grammar/filter.js +133 -39
- package/src/grammar/fold.js +2 -12
- package/src/grammar/gradientProfile.js +1 -6
- package/src/grammar/impute.js +21 -48
- package/src/grammar/interval.js +34 -10
- package/src/grammar/itemMissing.js +59 -0
- package/src/grammar/legendGradient.js +18 -0
- package/src/grammar/legendGroups.js +1 -0
- package/src/grammar/legendTitleStyle.js +22 -0
- package/src/grammar/lineSeries.js +32 -17
- package/src/grammar/markLabels.js +5 -2
- package/src/grammar/normalize.js +4 -54
- package/src/grammar/numeric.js +1 -290
- package/src/grammar/parallelCoordinates.js +1 -6
- package/src/grammar/parseTime.js +41 -0
- package/src/grammar/pathCommands.js +11 -4
- package/src/grammar/polar.js +1 -184
- package/src/grammar/positionCompatibility.js +3 -3
- package/src/grammar/regression/derive.js +63 -16
- package/src/grammar/regression/index.js +1 -0
- package/src/grammar/regression/models.js +116 -59
- package/src/grammar/regression/parameters.js +69 -6
- package/src/grammar/roundedRect.js +42 -54
- package/src/grammar/scales/appearance.js +1 -206
- package/src/grammar/scales/color.js +1 -382
- package/src/grammar/scales/colorConsumers.js +5 -2
- package/src/grammar/scales/definition.js +1 -190
- package/src/grammar/scales/discretized.js +1 -195
- package/src/grammar/scales/size.js +1 -456
- package/src/grammar/scales/temporal.js +2 -13
- package/src/grammar/scales/transformed.js +1 -383
- package/src/grammar/scales/types.js +1 -1
- package/src/grammar/scales/validation.js +1 -66
- package/src/grammar/schemas/concreteGraphic.js +11 -0
- package/src/grammar/schemas/graphic.js +3 -0
- package/src/grammar/schemas/semanticPath.js +5 -3
- package/src/grammar/sort.js +88 -0
- package/src/grammar/stack.js +2 -10
- package/src/grammar/statisticalReference.js +1 -6
- package/src/grammar/summary.js +120 -45
- package/src/grammar/text.js +19 -0
- package/src/grammar/timeUnit.js +16 -7
- package/src/grammar/transformKeys.js +47 -0
- package/src/grammar/transformTopology.js +1 -0
- package/src/grammar/transforms.js +25 -12
- package/src/grammar/valueFormat.js +17 -5
- package/src/grammar/weightedStatistics.js +1 -6
- package/src/grammar/window.js +4 -58
- package/src/inspection.js +495 -0
- package/src/inspectionDescriptors.js +3 -0
- package/src/layout/canvas.js +1 -110
- package/src/layout/facets.js +1 -244
- package/src/layout/labels.js +1 -0
- package/src/layout/legend.js +13 -0
- package/src/layout/legendGroup.js +1 -0
- package/src/layout/legendItems.js +59 -15
- package/src/layout/legendLane.js +1 -441
- package/src/layout/legendText.js +1 -0
- package/src/layout/title.js +35 -13
- package/src/materialization/bars/aggregate.js +11 -5
- package/src/materialization/bars/histogram.js +4 -1
- package/src/materialization/bars/ranged.js +19 -6
- package/src/materialization/bars/resolve.js +26 -3
- package/src/materialization/coordinateBounds.js +8 -0
- package/src/materialization/dependencies.js +4 -44
- package/src/materialization/facetGuides/layout.js +1 -0
- package/src/materialization/facetGuides/placement.js +19 -8
- package/src/materialization/facetGuides/preparation.js +15 -7
- package/src/materialization/facetHeaders.js +1 -347
- package/src/materialization/facets.js +1 -185
- package/src/materialization/graphicHierarchy.js +1 -1
- package/src/materialization/guides/layout.js +1 -57
- package/src/materialization/guides/resources.js +1 -123
- package/src/materialization/layout.js +53 -0
- package/src/materialization/legendGroups.js +1 -0
- package/src/materialization/legendText.js +1 -0
- package/src/materialization/legends.js +1 -1
- package/src/materialization/marks/capabilities.js +5 -6
- package/src/materialization/rect.js +1 -1
- package/src/materialization/revisionIdentity.js +19 -0
- package/src/materialization/scaleGuideDependencies.js +1 -1
- package/src/materialization/scales/map.js +3 -0
- package/src/materialization/scales/policies/arc.js +8 -7
- package/src/materialization/scales/policies/bar.js +19 -7
- package/src/materialization/scales/policies/series.js +2 -1
- package/src/materialization/scales/resolve.js +72 -7
- package/src/materialization/selection/items/arc.js +1 -1
- package/src/materialization/selection/items/bar.js +7 -6
- package/src/materialization/selection/items/path.js +7 -6
- package/src/materialization/selection/items/rect.js +2 -2
- package/src/materialization/text.js +17 -6
- package/src/materialization/textLines.js +15 -0
- package/src/persistence/codec.js +37 -6
- package/src/persistence/validation.js +47 -0
- package/src/persistence.js +94 -8
- package/src/renderers/canvas/circle.js +6 -3
- package/src/renderers/canvas/fill.js +7 -1
- package/src/renderers/canvas/index.js +2 -1
- package/src/renderers/canvas/line.js +1 -0
- package/src/renderers/canvas/path.js +1 -1
- package/src/renderers/canvas/rect.js +11 -8
- package/src/renderers/canvas/stroke.js +2 -1
- package/src/renderers/canvas/text.js +5 -3
- package/src/renderers/svg.js +4 -1
- package/src/selectors/layers.js +19 -0
- package/src/theme/defaults.js +8 -2
- package/src/version.js +1 -1
- package/types/basic.d.ts +1 -0
- package/types/index.d.ts +7 -0
- package/types/inspection.d.ts +17 -0
- package/types/program.d.ts +494 -112
|
@@ -6,16 +6,48 @@ import { requireLayer } from "../../selectors/layers.js";
|
|
|
6
6
|
import { applyRevisionPlan, buildSourceRevisionPlan } from "./edit.js";
|
|
7
7
|
import { resolveStoredSelection } from "../../materialization/selection/state.js";
|
|
8
8
|
import { withGuideLayoutTransaction } from "../../materialization/guides/layout.js";
|
|
9
|
+
import { assertFieldsAvailable } from "../../grammar/datasetSchema.js";
|
|
9
10
|
|
|
10
11
|
let reviseFacet;
|
|
11
12
|
|
|
13
|
+
function layerInputFields(layer) {
|
|
14
|
+
const fields = [];
|
|
15
|
+
const add = value => {
|
|
16
|
+
if (typeof value === "string" && value.length > 0) fields.push(value);
|
|
17
|
+
};
|
|
18
|
+
for (const encoding of Object.values(layer.encoding ?? {})) {
|
|
19
|
+
if (!encoding || typeof encoding !== "object") continue;
|
|
20
|
+
add(encoding.field);
|
|
21
|
+
add(encoding.weight);
|
|
22
|
+
add(encoding.key);
|
|
23
|
+
add(encoding.pathOrder?.field);
|
|
24
|
+
add(encoding.categoryOrder?.by?.field);
|
|
25
|
+
for (const field of encoding.fields ?? []) add(field);
|
|
26
|
+
for (const dimension of encoding.dimensions ?? []) add(dimension.field);
|
|
27
|
+
}
|
|
28
|
+
return [...new Set(fields)];
|
|
29
|
+
}
|
|
30
|
+
|
|
12
31
|
export function registerFacetDataRevision(handler) {
|
|
13
32
|
reviseFacet = handler;
|
|
14
33
|
}
|
|
15
34
|
|
|
16
|
-
export function reviseUnitData(program, { source, id, values }) {
|
|
35
|
+
export function reviseUnitData(program, { source, id, values, schema }) {
|
|
17
36
|
const plan = buildSourceRevisionPlan(program, source, id);
|
|
18
|
-
const
|
|
37
|
+
const original = requireDataset(program, source);
|
|
38
|
+
const inheritedSchema = schema ?? (original.schema?.origin === "declared"
|
|
39
|
+
? { fields: original.schema.fields.map(({ name, storageType, nullable, optional }) => ({
|
|
40
|
+
name, storageType, nullable, optional
|
|
41
|
+
})) }
|
|
42
|
+
: undefined);
|
|
43
|
+
const created = program.createData({ id, values, ...(inheritedSchema === undefined ? {} : { schema: inheritedSchema }) });
|
|
44
|
+
const revisedSource = requireDataset(created, id);
|
|
45
|
+
for (const layer of program.semanticSpec.layers.filter(layer => layer.data === source)) {
|
|
46
|
+
assertFieldsAvailable(revisedSource.schema, layerInputFields(layer), {
|
|
47
|
+
data: id,
|
|
48
|
+
operation: "reviseData"
|
|
49
|
+
});
|
|
50
|
+
}
|
|
19
51
|
const next = withGuideLayoutTransaction(created, candidate =>
|
|
20
52
|
applyRevisionPlan(candidate, undefined, plan, { retain: new Set([source]) }));
|
|
21
53
|
for (const [selection, config] of Object.entries(next.materializationConfigs.selections ?? {})) {
|
|
@@ -27,7 +59,7 @@ export function reviseUnitData(program, { source, id, values }) {
|
|
|
27
59
|
}
|
|
28
60
|
|
|
29
61
|
export const reviseData = /* @__PURE__ */ closedAction(
|
|
30
|
-
{ op: "reviseData", description: "Revise source data and its dependent chart.", scope: "any" }, ["source", "id", "values"],
|
|
62
|
+
{ op: "reviseData", description: "Revise source data and its dependent chart.", scope: "any" }, ["source", "id", "values", "schema"],
|
|
31
63
|
function (args = {}) {
|
|
32
64
|
const source = validateUserId(args.source, "Source dataset id");
|
|
33
65
|
const id = validateUserId(args.id, "Revision dataset id");
|
|
@@ -39,8 +71,8 @@ export const reviseData = /* @__PURE__ */ closedAction(
|
|
|
39
71
|
throw new Error(`Source "${source}" must be a materialized original dataset.`);
|
|
40
72
|
}
|
|
41
73
|
const apply = this.compositionSpec === undefined
|
|
42
|
-
? program => reviseUnitData(program, { source, id, values: args.values }).program
|
|
43
|
-
: program => reviseFacet(program, { source, id, values: args.values });
|
|
74
|
+
? program => reviseUnitData(program, { source, id, values: args.values, schema: args.schema }).program
|
|
75
|
+
: program => reviseFacet(program, { source, id, values: args.values, schema: args.schema });
|
|
44
76
|
// The entire dependent materialization must succeed before returning a revision.
|
|
45
77
|
apply(this);
|
|
46
78
|
return apply(this);
|
|
@@ -5,6 +5,12 @@ import {
|
|
|
5
5
|
findDataset,
|
|
6
6
|
resolveDatasetReference
|
|
7
7
|
} from "../../selectors/datasets.js";
|
|
8
|
+
import {
|
|
9
|
+
assertFieldsAvailable,
|
|
10
|
+
deriveTransformSchema,
|
|
11
|
+
transformInputFields,
|
|
12
|
+
validateRowsAgainstSchema
|
|
13
|
+
} from "../../grammar/datasetSchema.js";
|
|
8
14
|
|
|
9
15
|
export const MATERIALIZE_OPTIONS = Object.freeze(["id"]);
|
|
10
16
|
|
|
@@ -37,17 +43,39 @@ export function derivedMaterializer(op, description, type, derive, resolve) {
|
|
|
37
43
|
args.id,
|
|
38
44
|
type
|
|
39
45
|
);
|
|
46
|
+
assertFieldsAvailable(source.schema, transformInputFields(transform), {
|
|
47
|
+
data: source.id,
|
|
48
|
+
operation: op
|
|
49
|
+
});
|
|
40
50
|
const result = derive(source.values, transform);
|
|
51
|
+
const values = Array.isArray(result) ? result : result.values;
|
|
52
|
+
const schema = deriveTransformSchema(source.schema, transform, values, {
|
|
53
|
+
sourceId: source.id,
|
|
54
|
+
ownerId: id
|
|
55
|
+
});
|
|
56
|
+
validateRowsAgainstSchema(values, schema, `Derived dataset "${id}"`);
|
|
41
57
|
const program = resolve === undefined
|
|
42
58
|
? this
|
|
43
59
|
: this.editSemantic({
|
|
44
60
|
property: `dataset[${id}].transform`,
|
|
45
61
|
value: resolve(result, transform)
|
|
46
62
|
});
|
|
47
|
-
|
|
63
|
+
let next = program.editSemantic({
|
|
48
64
|
property: `dataset[${id}].values`,
|
|
49
|
-
value:
|
|
65
|
+
value: values
|
|
50
66
|
});
|
|
67
|
+
if (!Array.isArray(result) && result.report !== undefined) {
|
|
68
|
+
const report = {
|
|
69
|
+
...result.report,
|
|
70
|
+
owner: { kind: "data", id },
|
|
71
|
+
inputs: [{ kind: "data", id: source.id }]
|
|
72
|
+
};
|
|
73
|
+
next = next._withMaterializationConfig(
|
|
74
|
+
["calculations", "datasets", id],
|
|
75
|
+
report
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
return next;
|
|
51
79
|
});
|
|
52
80
|
}
|
|
53
81
|
|
|
@@ -59,11 +87,12 @@ export function derivedCreator(
|
|
|
59
87
|
sourceLabel,
|
|
60
88
|
transform,
|
|
61
89
|
materialize,
|
|
62
|
-
requireSource = false
|
|
90
|
+
requireSource = false,
|
|
91
|
+
defaultId
|
|
63
92
|
) {
|
|
64
93
|
return action({ op, description }, function (args = {}) {
|
|
65
94
|
validateKeys(args, options, op);
|
|
66
|
-
const id = validateUserId(args.id, idLabel);
|
|
95
|
+
const id = validateUserId(args.id === undefined ? defaultId : args.id, idLabel);
|
|
67
96
|
const requestedSource = validateUserId(
|
|
68
97
|
requireSource ? args.source : args.source ?? this.context.currentData,
|
|
69
98
|
sourceLabel
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { deriveSortedRows, normalizeSortTransform } from "../../grammar/sort.js";
|
|
2
|
+
import { derivedCreator, derivedMaterializer } from "./shared.js";
|
|
3
|
+
|
|
4
|
+
const OPTIONS = Object.freeze(["id", "source", "sortBy"]);
|
|
5
|
+
|
|
6
|
+
export const materializeSortedData = /* @__PURE__ */ derivedMaterializer(
|
|
7
|
+
"materializeSortedData",
|
|
8
|
+
"Materialize one immutable stable sorted dataset.",
|
|
9
|
+
"sort",
|
|
10
|
+
deriveSortedRows
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
export const createSortedData = /* @__PURE__ */ derivedCreator(
|
|
14
|
+
"createSortedData",
|
|
15
|
+
"Create reusable rows in a stable explicit order.",
|
|
16
|
+
OPTIONS,
|
|
17
|
+
"Sorted dataset id",
|
|
18
|
+
"Sorted source dataset id",
|
|
19
|
+
normalizeSortTransform,
|
|
20
|
+
"materializeSortedData"
|
|
21
|
+
);
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
import { derivedCreator, derivedMaterializer } from "./shared.js";
|
|
6
6
|
|
|
7
7
|
const OPTIONS = Object.freeze([
|
|
8
|
-
"id", "source", "groupBy", "aggregates", "members", "weight"
|
|
8
|
+
"id", "source", "groupBy", "aggregates", "members", "weight", "missing", "empty"
|
|
9
9
|
]);
|
|
10
10
|
|
|
11
11
|
export const materializeSummaryData = /* @__PURE__ */ derivedMaterializer(
|
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
setEncodingProperties,
|
|
6
6
|
validateOptions
|
|
7
7
|
} from "./shared.js";
|
|
8
|
+
import { applyRequestedItemMissingPolicy } from
|
|
9
|
+
"../../grammar/itemMissing.js";
|
|
8
10
|
|
|
9
11
|
const OPTIONS = Object.freeze(["target", "value", "field", "fieldType"]);
|
|
10
12
|
|
|
@@ -29,7 +31,7 @@ export const encodeAngle = /* @__PURE__ */ action(
|
|
|
29
31
|
if (hasValue && args.fieldType !== undefined) {
|
|
30
32
|
throw new Error("Constant angle does not accept fieldType.");
|
|
31
33
|
}
|
|
32
|
-
const { id: target, dataset, layer } = resolveTarget(
|
|
34
|
+
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
33
35
|
this,
|
|
34
36
|
args.target,
|
|
35
37
|
["point", "tick"],
|
|
@@ -39,6 +41,7 @@ export const encodeAngle = /* @__PURE__ */ action(
|
|
|
39
41
|
throw new TypeError("encodeAngle value must be finite degrees.");
|
|
40
42
|
}
|
|
41
43
|
if (hasField) {
|
|
44
|
+
const dataset = applyRequestedItemMissingPolicy(layer, sourceDataset, args.field);
|
|
42
45
|
const fieldType = args.fieldType ?? "quantitative";
|
|
43
46
|
if (fieldType !== "quantitative") {
|
|
44
47
|
throw new Error("encodeAngle requires a quantitative field.");
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
+
import { canMaterializeBar } from "../../materialization/marks/index.js";
|
|
1
2
|
import { action } from "../../core/action.js";
|
|
2
3
|
import { validatePathSeriesAppearance } from "../../grammar/pathSeries.js";
|
|
3
4
|
import { assertEncodingSelectionCompatibility } from "../../materialization/selection/compatibility.js";
|
|
4
5
|
import {
|
|
5
|
-
readNominalField,
|
|
6
|
-
readQuantitativeField,
|
|
7
6
|
readScaleField,
|
|
8
7
|
validateOpacityValue
|
|
9
8
|
} from "../../grammar/scales/index.js";
|
|
@@ -25,6 +24,8 @@ import {
|
|
|
25
24
|
setEncodingProperties,
|
|
26
25
|
validateOptions
|
|
27
26
|
} from "./shared.js";
|
|
27
|
+
import { applyRequestedItemMissingPolicy } from
|
|
28
|
+
"../../grammar/itemMissing.js";
|
|
28
29
|
|
|
29
30
|
const RADIUS_OPTIONS = Object.freeze(["value", "target"]);
|
|
30
31
|
const REMOVE_RADIUS_OPTIONS = Object.freeze(["target"]);
|
|
@@ -34,16 +35,17 @@ const OPACITY_OPTIONS = Object.freeze([
|
|
|
34
35
|
const FIELD_OPTIONS = Object.freeze(["field", "target", "fieldType", "scale"]);
|
|
35
36
|
function encodeAppearanceField(program, channel, args, operation) {
|
|
36
37
|
validateOptions(args, FIELD_OPTIONS, operation);
|
|
37
|
-
const { id: target, dataset, layer } = resolveTarget(
|
|
38
|
+
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
38
39
|
program,
|
|
39
40
|
args.target,
|
|
40
41
|
["point"],
|
|
41
42
|
"point mark"
|
|
42
43
|
);
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
const fieldTypes = channel === "shape" ? ["nominal"] : ["quantitative", "nominal", "ordinal"];
|
|
45
|
+
const dataset = applyRequestedItemMissingPolicy(layer, sourceDataset, args.field);
|
|
46
|
+
const fieldType = args.fieldType ?? fieldTypes[0];
|
|
47
|
+
if (!fieldTypes.includes(fieldType)) {
|
|
48
|
+
throw new Error(`${operation} requires a ${fieldTypes.join(" or ")} field.`);
|
|
47
49
|
}
|
|
48
50
|
if (channel === "size" && program.markConfigs[target]?.radius !== undefined) {
|
|
49
51
|
throw new Error("encodeSize cannot be combined with a constant radius.");
|
|
@@ -56,17 +58,12 @@ function encodeAppearanceField(program, channel, args, operation) {
|
|
|
56
58
|
const scale = resolveAppearanceScaleDefinition(
|
|
57
59
|
program,
|
|
58
60
|
channel,
|
|
59
|
-
requestedScale
|
|
61
|
+
requestedScale,
|
|
62
|
+
fieldType
|
|
60
63
|
);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
});
|
|
65
|
-
} else if (channel === "shape") {
|
|
66
|
-
readNominalField(dataset.values, args.field);
|
|
67
|
-
} else {
|
|
68
|
-
readQuantitativeField(dataset.values, args.field);
|
|
69
|
-
}
|
|
64
|
+
readScaleField(dataset.values, args.field, fieldType, {
|
|
65
|
+
allowUnknown: Object.hasOwn(scale, "unknown")
|
|
66
|
+
});
|
|
70
67
|
|
|
71
68
|
let next = setEncodingProperties(program, target, channel, {
|
|
72
69
|
field: args.field,
|
|
@@ -149,7 +146,7 @@ const removePointRadius = /* @__PURE__ */ action(
|
|
|
149
146
|
const encodeSize = /* @__PURE__ */ action(
|
|
150
147
|
{
|
|
151
148
|
op: "encodeSize",
|
|
152
|
-
description: "Encode a
|
|
149
|
+
description: "Encode a field as equal-area point size."
|
|
153
150
|
},
|
|
154
151
|
function (args = {}) {
|
|
155
152
|
return encodeAppearanceField(this, "size", args, "encodeSize");
|
|
@@ -181,6 +178,17 @@ const clearOpacityEncoding = /* @__PURE__ */ action(
|
|
|
181
178
|
}
|
|
182
179
|
);
|
|
183
180
|
|
|
181
|
+
function withoutConstantOpacity(program, target) {
|
|
182
|
+
const { opacity, ...config } = program.markConfigs[target] ?? {};
|
|
183
|
+
void opacity;
|
|
184
|
+
if (config.barAppearance?.opacity !== undefined) {
|
|
185
|
+
const { opacity: barOpacity, ...appearance } = config.barAppearance;
|
|
186
|
+
void barOpacity;
|
|
187
|
+
config.barAppearance = appearance;
|
|
188
|
+
}
|
|
189
|
+
return program._withMarkConfig(target, config);
|
|
190
|
+
}
|
|
191
|
+
|
|
184
192
|
const encodeOpacity = /* @__PURE__ */ action(
|
|
185
193
|
{
|
|
186
194
|
op: "encodeOpacity",
|
|
@@ -196,17 +204,16 @@ const encodeOpacity = /* @__PURE__ */ action(
|
|
|
196
204
|
if (hasValue && (args.fieldType !== undefined || args.scale !== undefined)) {
|
|
197
205
|
throw new Error("Constant opacity does not accept fieldType or scale.");
|
|
198
206
|
}
|
|
199
|
-
const { id: target, dataset, layer } = resolveTarget(
|
|
207
|
+
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
200
208
|
this,
|
|
201
209
|
args.target,
|
|
202
|
-
["point", "rule", "line"],
|
|
203
|
-
"point, rule or
|
|
210
|
+
["point", "rule", "line", "bar"],
|
|
211
|
+
"point, rule, line or bar mark"
|
|
204
212
|
);
|
|
205
213
|
if (hasValue) {
|
|
206
214
|
validateOpacityValue(args.value, "encodeOpacity");
|
|
207
215
|
assertEncodingSelectionCompatibility(this, target, ["opacity"]);
|
|
208
|
-
const
|
|
209
|
-
void opacity;
|
|
216
|
+
const config = withoutConstantOpacity(this, target).markConfigs[target];
|
|
210
217
|
const withoutLegend = this.guideConfigs.legend?.opacity?.target === target
|
|
211
218
|
? this.removeOpacityLegend()
|
|
212
219
|
: this;
|
|
@@ -217,11 +224,17 @@ const encodeOpacity = /* @__PURE__ */ action(
|
|
|
217
224
|
if (layer.mark.type === "line") {
|
|
218
225
|
return rematerializeEncoding(next, target, "opacity", undefined, layer);
|
|
219
226
|
}
|
|
227
|
+
if (layer.mark.type === "bar") {
|
|
228
|
+
return canMaterializeBar(next, findLayer(next, target))
|
|
229
|
+
? applyDetachedScaleRematerialization(next.rematerializeBarMark({ id: target }), [layer])
|
|
230
|
+
: next;
|
|
231
|
+
}
|
|
220
232
|
const materialized = layer.mark.type === "rule"
|
|
221
233
|
? next.rematerializeRuleMark({ id: target })
|
|
222
234
|
: next.rematerializePointMark({ id: target });
|
|
223
235
|
return applyDetachedScaleRematerialization(materialized, [layer]);
|
|
224
236
|
}
|
|
237
|
+
const dataset = applyRequestedItemMissingPolicy(layer, sourceDataset, args.field);
|
|
225
238
|
const fieldType = args.fieldType ?? "quantitative";
|
|
226
239
|
if (fieldType !== "quantitative") {
|
|
227
240
|
throw new Error("encodeOpacity requires a quantitative field.");
|
|
@@ -240,17 +253,13 @@ const encodeOpacity = /* @__PURE__ */ action(
|
|
|
240
253
|
...layer, encoding: { ...layer.encoding, opacity: { field: args.field, fieldType } }
|
|
241
254
|
});
|
|
242
255
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
});
|
|
247
|
-
} else {
|
|
248
|
-
readQuantitativeField(dataset.values, args.field);
|
|
249
|
-
}
|
|
256
|
+
readScaleField(dataset.values, args.field, fieldType, {
|
|
257
|
+
allowUnknown: Object.hasOwn(scale, "unknown")
|
|
258
|
+
});
|
|
250
259
|
const { opacity, ...config } = this.markConfigs[target] ?? {};
|
|
251
260
|
void opacity;
|
|
252
261
|
let next = setEncodingProperties(
|
|
253
|
-
this
|
|
262
|
+
withoutConstantOpacity(this, target),
|
|
254
263
|
target,
|
|
255
264
|
"opacity",
|
|
256
265
|
{ field: args.field, fieldType, scale: scale.id }
|
|
@@ -258,6 +267,9 @@ const encodeOpacity = /* @__PURE__ */ action(
|
|
|
258
267
|
next = applyEncodingScale(next, scale, requestedScale, {
|
|
259
268
|
reassignment: previous?.scale === scale.id
|
|
260
269
|
});
|
|
270
|
+
if (layer.mark.type === "bar" && !canMaterializeBar(next, findLayer(next, target))) {
|
|
271
|
+
return next.rematerializeScale({ id: scale.id });
|
|
272
|
+
}
|
|
261
273
|
return rematerializeEncoding(next, target, "opacity", scale.id, layer);
|
|
262
274
|
}
|
|
263
275
|
);
|
|
@@ -4,11 +4,33 @@ import {
|
|
|
4
4
|
BAR_GRAINS,
|
|
5
5
|
resolveBarColorLayout,
|
|
6
6
|
resolveBarOffsetChannel,
|
|
7
|
+
resolveBarChannels,
|
|
7
8
|
resolveBarGrain
|
|
8
9
|
} from "../../grammar/bars/policy.js";
|
|
9
10
|
import { normalizeBarWidth } from "../../grammar/bars/geometry.js";
|
|
11
|
+
import { applyMaterializationPlan } from "../../materialization/dependencies.js";
|
|
12
|
+
import {
|
|
13
|
+
getScaleConsumerMarkSteps,
|
|
14
|
+
getSourceDependentMarkSteps
|
|
15
|
+
} from "../../materialization/marks/index.js";
|
|
16
|
+
|
|
17
|
+
function rematerializeWidth(program, target, layer, width) {
|
|
18
|
+
const next = program._withMarkConfig(target, {
|
|
19
|
+
...program.markConfigs[target], barWidth: width
|
|
20
|
+
}).rematerializeBarMark({ id: target });
|
|
21
|
+
const category = resolveBarChannels(layer)?.category;
|
|
22
|
+
const encoding = layer.encoding?.[category];
|
|
23
|
+
if (encoding?.fieldType !== "temporal") return next;
|
|
24
|
+
const marks = getScaleConsumerMarkSteps(next, [encoding.scale])
|
|
25
|
+
.filter(step => step.args.id !== target);
|
|
26
|
+
return applyMaterializationPlan(next, [
|
|
27
|
+
...marks,
|
|
28
|
+
...[target, ...marks.map(step => step.args.id)]
|
|
29
|
+
.flatMap(id => getSourceDependentMarkSteps(next, id))
|
|
30
|
+
]);
|
|
31
|
+
}
|
|
10
32
|
|
|
11
|
-
const OPTIONS = Object.freeze(["band", "pixels", "target"]);
|
|
33
|
+
const OPTIONS = Object.freeze(["band", "pixels", "interval", "target"]);
|
|
12
34
|
|
|
13
35
|
const encodeBarWidth = /* @__PURE__ */ action(
|
|
14
36
|
{
|
|
@@ -26,22 +48,30 @@ const encodeBarWidth = /* @__PURE__ */ action(
|
|
|
26
48
|
const layout = resolveBarColorLayout(layer);
|
|
27
49
|
const width = normalizeBarWidth(
|
|
28
50
|
args,
|
|
29
|
-
this.markConfigs[target]?.barWidth
|
|
51
|
+
this.markConfigs[target]?.barWidth ?? (layer.mark.orientation === undefined ? undefined : { pixels: 5 })
|
|
30
52
|
);
|
|
31
53
|
const grouped = layout === "group";
|
|
32
54
|
const offsetChannel = resolveBarOffsetChannel(layer);
|
|
33
55
|
const grain = resolveBarGrain(layer);
|
|
56
|
+
if (layer.mark.orientation !== undefined && width.band !== undefined) {
|
|
57
|
+
throw new Error("Numeric-center bars require pixel width.");
|
|
58
|
+
}
|
|
34
59
|
if (grain === undefined && this.markConfigs[target]?.boxPlot === undefined && (
|
|
35
60
|
layer.encoding?.x === undefined || layer.encoding?.y === undefined
|
|
36
61
|
)) {
|
|
37
62
|
return this._withMarkConfig(target, { ...this.markConfigs[target], barWidth: width });
|
|
38
63
|
}
|
|
64
|
+
if (width.interval !== undefined && grain !== undefined) {
|
|
65
|
+
const category = resolveBarChannels(layer)?.category;
|
|
66
|
+
if (grain !== BAR_GRAINS.aggregate || grouped || layer.encoding?.[category]?.fieldType !== "temporal") {
|
|
67
|
+
throw new Error("Bar interval width requires ungrouped aggregate temporal categories.");
|
|
68
|
+
}
|
|
69
|
+
}
|
|
39
70
|
if (grain === BAR_GRAINS.histogram) {
|
|
40
71
|
throw new Error("encodeBarWidth requires an aggregate or ranged category slot, not histogram bins.");
|
|
41
72
|
}
|
|
42
|
-
if (
|
|
43
|
-
return this
|
|
44
|
-
.rematerializeBarMark({ id: target });
|
|
73
|
+
if ([BAR_GRAINS.ranged, BAR_GRAINS.centered].includes(grain)) {
|
|
74
|
+
return rematerializeWidth(this, target, layer, width);
|
|
45
75
|
}
|
|
46
76
|
if (
|
|
47
77
|
grain !== BAR_GRAINS.aggregate ||
|
|
@@ -56,12 +86,7 @@ const encodeBarWidth = /* @__PURE__ */ action(
|
|
|
56
86
|
);
|
|
57
87
|
}
|
|
58
88
|
|
|
59
|
-
return this
|
|
60
|
-
._withMarkConfig(target, {
|
|
61
|
-
...this.markConfigs[target],
|
|
62
|
-
barWidth: width
|
|
63
|
-
})
|
|
64
|
-
.rematerializeBarMark({ id: target });
|
|
89
|
+
return rematerializeWidth(this, target, layer, width);
|
|
65
90
|
}
|
|
66
91
|
);
|
|
67
92
|
|
|
@@ -26,6 +26,8 @@ import {
|
|
|
26
26
|
} from "./policy.js";
|
|
27
27
|
|
|
28
28
|
import { applyTemporalUnit } from "../temporal.js";
|
|
29
|
+
import { applyRequestedItemMissingPolicy } from
|
|
30
|
+
"../../../grammar/itemMissing.js";
|
|
29
31
|
|
|
30
32
|
export function encodeContinuousColor(program, args) {
|
|
31
33
|
if (!["quantitative", "temporal"].includes(args.fieldType)) {
|
|
@@ -34,12 +36,13 @@ export function encodeContinuousColor(program, args) {
|
|
|
34
36
|
if (args.layout !== undefined) {
|
|
35
37
|
throw new Error("Continuous color does not support layout.");
|
|
36
38
|
}
|
|
37
|
-
const { id: target, dataset, layer } = resolveTarget(
|
|
39
|
+
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
38
40
|
program,
|
|
39
41
|
args.target,
|
|
40
|
-
["point", "bar", "rect"],
|
|
42
|
+
["point", "bar", "rect", "text"],
|
|
41
43
|
"continuous color mark"
|
|
42
44
|
);
|
|
45
|
+
const dataset = applyRequestedItemMissingPolicy(layer, sourceDataset, args.field);
|
|
43
46
|
assertNoConstantColor(program, layer);
|
|
44
47
|
const temporalUnit = resolveTemporalUnit(args, args.fieldType, layer.encoding?.color);
|
|
45
48
|
const requestedScale = resolveReassignmentScaleOptions(
|
|
@@ -38,6 +38,8 @@ import {
|
|
|
38
38
|
} from "./policy.js";
|
|
39
39
|
|
|
40
40
|
import { applyTemporalUnit } from "../temporal.js";
|
|
41
|
+
import { applyRequestedItemMissingPolicy } from
|
|
42
|
+
"../../../grammar/itemMissing.js";
|
|
41
43
|
|
|
42
44
|
const encodeColor = /* @__PURE__ */ action(
|
|
43
45
|
{
|
|
@@ -58,12 +60,13 @@ const encodeColor = /* @__PURE__ */ action(
|
|
|
58
60
|
}
|
|
59
61
|
const fieldType = validateCategoricalFieldType(requestedFieldType);
|
|
60
62
|
resolveTemporalUnit(args, fieldType);
|
|
61
|
-
const { id: target, dataset, layer } = resolveTarget(
|
|
63
|
+
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
62
64
|
this,
|
|
63
65
|
args.target,
|
|
64
|
-
["point", "line", "bar", "area", "arc", "rect"],
|
|
66
|
+
["point", "line", "bar", "area", "arc", "rect", "text"],
|
|
65
67
|
"color mark"
|
|
66
68
|
);
|
|
69
|
+
const dataset = applyRequestedItemMissingPolicy(layer, sourceDataset, args.field);
|
|
67
70
|
assertNoConstantColor(this, layer);
|
|
68
71
|
const densityTransform = dataset.transform?.length === 1 &&
|
|
69
72
|
dataset.transform[0].type === "density"
|
|
@@ -20,6 +20,9 @@ export function resolveColorScaleOptions(args) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export function assertNoConstantColor(program, layer) {
|
|
23
|
+
if (layer.mark.type === "text" && layer.source !== undefined) {
|
|
24
|
+
throw new Error("Source-owned text uses inheritColor or constant fill; field color requires row-backed text.");
|
|
25
|
+
}
|
|
23
26
|
const config = program.markConfigs[layer.id];
|
|
24
27
|
const hasConstant = {
|
|
25
28
|
point: config?.fill !== undefined,
|
|
@@ -27,7 +30,8 @@ export function assertNoConstantColor(program, layer) {
|
|
|
27
30
|
area: config?.errorBand?.fill !== undefined,
|
|
28
31
|
bar: config?.barAppearance?.fill !== undefined,
|
|
29
32
|
arc: config?.fill !== undefined,
|
|
30
|
-
rect: config?.fillExplicit === true
|
|
33
|
+
rect: config?.fillExplicit === true,
|
|
34
|
+
text: config?.fillExplicit === true
|
|
31
35
|
}[layer.mark.type] ?? false;
|
|
32
36
|
if (hasConstant) {
|
|
33
37
|
throw new Error(
|
|
@@ -58,15 +62,18 @@ export function resolveColorLayout(layer, requested, barGrain) {
|
|
|
58
62
|
layer.mark.type === "bar"
|
|
59
63
|
? barGrain === BAR_GRAINS.histogram
|
|
60
64
|
? "stack"
|
|
61
|
-
:
|
|
65
|
+
: [BAR_GRAINS.ranged, BAR_GRAINS.centered].includes(barGrain) ? "overlay" : "group"
|
|
62
66
|
: layer.mark.type === "area"
|
|
63
67
|
? "overlay"
|
|
64
68
|
: undefined
|
|
65
69
|
);
|
|
66
70
|
|
|
67
|
-
if (["point", "line", "rect"].includes(layer.mark.type) && layout !== undefined) {
|
|
71
|
+
if (["point", "line", "rect", "text"].includes(layer.mark.type) && layout !== undefined) {
|
|
68
72
|
throw new Error(`Color layout is not supported for ${layer.mark.type} marks.`);
|
|
69
73
|
}
|
|
74
|
+
if (barGrain === BAR_GRAINS.centered && layout !== "overlay") {
|
|
75
|
+
throw new Error("Numeric-center bars support only overlay layout.");
|
|
76
|
+
}
|
|
70
77
|
if (layer.mark.type === "area" && layout === "group") {
|
|
71
78
|
throw new Error('Area color layout does not support "group".');
|
|
72
79
|
}
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
+
export { requireStringValue as requireDensityField } from "../../../core/validation.js";
|
|
1
2
|
import { isPlainObject } from "../../../core/immutable.js";
|
|
2
3
|
import { findSemanticScale } from "../../../selectors/scales.js";
|
|
3
4
|
|
|
4
|
-
export function requireDensityField(value, label) {
|
|
5
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
6
|
-
throw new TypeError(`${label} must be a non-empty string.`);
|
|
7
|
-
}
|
|
8
|
-
return value;
|
|
9
|
-
}
|
|
10
5
|
|
|
11
6
|
export function resolveDensityScaleOptions(value, defaults, label) {
|
|
12
7
|
if (value !== undefined && !isPlainObject(value)) {
|
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
resolveBarChannels,
|
|
19
19
|
resolveBarGrain
|
|
20
20
|
} from "../../grammar/bars/policy.js";
|
|
21
|
+
import { applyRequestedItemMissingPolicy } from
|
|
22
|
+
"../../grammar/itemMissing.js";
|
|
21
23
|
|
|
22
24
|
const ENCODING_OPTIONS = Object.freeze([
|
|
23
25
|
"field", "target", "fieldType", "scale", "paddingInner", "paddingOuter"
|
|
@@ -33,12 +35,13 @@ function createOffsetEncoding(channel) {
|
|
|
33
35
|
function (args = {}) {
|
|
34
36
|
validateOptions(args, ENCODING_OPTIONS, operation);
|
|
35
37
|
const fieldType = validateCategoricalFieldType(args.fieldType ?? "nominal");
|
|
36
|
-
const { id: target, dataset, layer } = resolveTarget(
|
|
38
|
+
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
37
39
|
this,
|
|
38
40
|
args.target,
|
|
39
41
|
["bar", "point", "rule"],
|
|
40
42
|
"offset-compatible mark"
|
|
41
43
|
);
|
|
44
|
+
const dataset = applyRequestedItemMissingPolicy(layer, sourceDataset, args.field);
|
|
42
45
|
|
|
43
46
|
if (layer.mark.type === "bar") {
|
|
44
47
|
const channels = resolveBarChannels(layer);
|
|
@@ -11,11 +11,6 @@ export function resolveArcPositionPolicy({ channel, args, fieldType, layer }) {
|
|
|
11
11
|
throw new Error(`Arc ${channel} encoding does not support stack.`);
|
|
12
12
|
}
|
|
13
13
|
if (channel === "radius") {
|
|
14
|
-
if (layer.encoding?.theta?.fieldType === "quantitative") {
|
|
15
|
-
throw new Error(
|
|
16
|
-
"Arc radius encoding cannot be combined with quantitative theta."
|
|
17
|
-
);
|
|
18
|
-
}
|
|
19
14
|
if (args.mapping !== undefined) {
|
|
20
15
|
validateRadialMapping(args.mapping);
|
|
21
16
|
if (fieldType !== "quantitative" || !["count", "sum"].includes(args.aggregate)) {
|
|
@@ -30,7 +25,16 @@ export function resolveArcPositionPolicy({ channel, args, fieldType, layer }) {
|
|
|
30
25
|
return { ...emptyPositionPolicy(), aggregate: args.aggregate };
|
|
31
26
|
}
|
|
32
27
|
if (args.aggregate !== undefined) {
|
|
33
|
-
throw new Error("Arc radius
|
|
28
|
+
if (!["count", "sum"].includes(args.aggregate)) throw new Error("Arc radius aggregate supports count or sum.");
|
|
29
|
+
const theta = layer.encoding?.theta;
|
|
30
|
+
if (theta !== undefined && !["count", "sum"].includes(theta.aggregate)) {
|
|
31
|
+
throw new Error("Grouped arc radius requires aggregated categorical theta.");
|
|
32
|
+
}
|
|
33
|
+
if (args.aggregate === "count" && Object.hasOwn(args, "field")) {
|
|
34
|
+
throw new Error("Arc radius count does not accept a field.");
|
|
35
|
+
}
|
|
36
|
+
if (args.weight !== undefined) throw new Error("Arc radius encoding does not support weight.");
|
|
37
|
+
return { ...emptyPositionPolicy(), aggregate: args.aggregate };
|
|
34
38
|
}
|
|
35
39
|
if (args.weight !== undefined) {
|
|
36
40
|
throw new Error("Arc radius encoding does not support weight.");
|
|
@@ -38,10 +42,8 @@ export function resolveArcPositionPolicy({ channel, args, fieldType, layer }) {
|
|
|
38
42
|
return emptyPositionPolicy();
|
|
39
43
|
}
|
|
40
44
|
if (fieldType === "quantitative") {
|
|
41
|
-
if (layer.encoding?.radius !== undefined) {
|
|
42
|
-
throw new Error(
|
|
43
|
-
"Quantitative arc theta cannot be combined with radius encoding."
|
|
44
|
-
);
|
|
45
|
+
if (layer.encoding?.radius?.aggregate !== undefined) {
|
|
46
|
+
throw new Error("Grouped arc radius requires aggregated categorical theta.");
|
|
45
47
|
}
|
|
46
48
|
if (args.aggregate !== undefined) {
|
|
47
49
|
throw new Error("Quantitative arc theta does not support aggregate.");
|
|
@@ -26,16 +26,16 @@ export function resolveAreaPositionPolicy({
|
|
|
26
26
|
const density = dataset.transform?.find(transform => transform.type === "density");
|
|
27
27
|
if (["nominal", "ordinal"].includes(fieldType)) {
|
|
28
28
|
if (
|
|
29
|
-
density
|
|
29
|
+
density !== undefined && (density.placement?.type !== "category" ||
|
|
30
30
|
density.placement.channel !== channel ||
|
|
31
|
-
density.placement.categoryField !== field
|
|
31
|
+
density.placement.categoryField !== field)
|
|
32
32
|
) {
|
|
33
33
|
throw new Error(
|
|
34
34
|
"Categorical area position requires a matching category density placement."
|
|
35
35
|
);
|
|
36
36
|
}
|
|
37
37
|
if (args.aggregate !== undefined || args.bin !== undefined || args.stack !== undefined) {
|
|
38
|
-
throw new Error("Categorical
|
|
38
|
+
throw new Error("Categorical area position does not support aggregate, bin, or stack.");
|
|
39
39
|
}
|
|
40
40
|
return emptyPositionPolicy();
|
|
41
41
|
}
|