ggaction 0.0.16 → 0.0.17
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 +20 -0
- package/README.md +2 -1
- package/knowledge/action-cards.json +1 -1
- package/knowledge/intent-taxonomy.json +1 -1
- package/knowledge/mcp-resources.json +1 -1
- package/package.json +8 -2
- package/src/actions/data/bin.js +1 -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 +19 -6
- 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 +4 -1
- package/src/actions/data/revise.js +37 -5
- package/src/actions/data/shared.js +30 -2
- package/src/actions/data/sort.js +21 -0
- package/src/actions/data/summary.js +1 -1
- package/src/actions/encodings/angle.js +4 -1
- package/src/actions/encodings/appearance.js +6 -2
- package/src/actions/encodings/color/continuous.js +4 -1
- package/src/actions/encodings/color/index.js +4 -1
- package/src/actions/encodings/offset.js +4 -1
- package/src/actions/encodings/position/resolve.js +6 -1
- package/src/actions/encodings/ranged.js +6 -1
- package/src/actions/encodings/ruleAppearance.js +4 -1
- package/src/actions/encodings/stroke.js +4 -1
- package/src/actions/encodings/strokeDash.js +6 -1
- package/src/actions/encodings/text.js +6 -1
- package/src/actions/marks/bar/create.js +8 -1
- package/src/actions/marks/bar/edit.js +12 -5
- package/src/actions/marks/point/create.js +8 -1
- package/src/actions/marks/point/edit.js +12 -5
- package/src/actions/marks/point/materialize.js +4 -2
- package/src/actions/marks/rect/actions.js +23 -8
- package/src/actions/marks/rule/actions.js +24 -8
- package/src/actions/marks/text/actions.js +25 -6
- 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 +22 -2
- package/src/actions/primitives/semanticValidation/scale.js +6 -0
- package/src/actions/regression/create.js +25 -4
- package/src/actions/regression/edit.js +47 -12
- package/src/actions/regression/follow.js +22 -0
- package/src/actions/scales/channels.js +11 -11
- package/src/actions/scales/consumers/common.js +2 -1
- package/src/actions/scales/create.js +2 -1
- package/src/actions/scales/definitions.js +1 -1
- package/src/actions/scales/edit.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 +28 -1
- package/src/actions/theme/reconcile.js +2 -0
- package/src/core/action.js +3 -2
- package/src/core/programState.js +5 -1
- package/src/grammar/bin.js +39 -10
- package/src/grammar/datasetSchema.js +386 -0
- package/src/grammar/density.js +53 -5
- package/src/grammar/filter.js +133 -39
- package/src/grammar/interval.js +32 -1
- package/src/grammar/itemMissing.js +59 -0
- package/src/grammar/regression/derive.js +57 -16
- package/src/grammar/regression/index.js +1 -0
- package/src/grammar/regression/models.js +51 -22
- package/src/grammar/regression/parameters.js +56 -4
- package/src/grammar/scales/definition.js +7 -0
- package/src/grammar/schemas/semanticPath.js +3 -2
- package/src/grammar/sort.js +88 -0
- package/src/grammar/summary.js +117 -19
- package/src/grammar/transformTopology.js +1 -0
- package/src/grammar/transforms.js +21 -11
- package/src/inspection.js +495 -0
- package/src/inspectionDescriptors.js +3 -0
- package/src/materialization/bars/resolve.js +4 -2
- package/src/materialization/revisionIdentity.js +19 -0
- package/src/materialization/scales/resolve.js +67 -4
- package/src/materialization/text.js +4 -2
- package/src/persistence.js +79 -6
- package/src/version.js +1 -1
- package/types/inspection.d.ts +17 -0
- package/types/program.d.ts +147 -27
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
import { createCompleteData, materializeCompleteData } from "./complete.js";
|
|
49
49
|
import { createImputedData, materializeImputedData } from "./impute.js";
|
|
50
50
|
import { createStackData, materializeStackData } from "./stack.js";
|
|
51
|
+
import { createSortedData, materializeSortedData } from "./sort.js";
|
|
51
52
|
import { EDIT_DERIVED_DATA_ACTIONS } from "./edit.js";
|
|
52
53
|
import { createBoxSummaryData, createBoxOutlierData, materializeBoxSummaryData, materializeBoxOutlierData } from "./box.js";
|
|
53
54
|
|
|
@@ -96,6 +97,8 @@ export function registerDataActions(ProgramClass) {
|
|
|
96
97
|
materializeImputedData,
|
|
97
98
|
createStackData,
|
|
98
99
|
materializeStackData,
|
|
100
|
+
createSortedData,
|
|
101
|
+
materializeSortedData,
|
|
99
102
|
createTimeUnitData,
|
|
100
103
|
materializeTimeUnitData,
|
|
101
104
|
createBin2DData,
|
|
@@ -13,6 +13,7 @@ const OPTIONS = Object.freeze([
|
|
|
13
13
|
"extent",
|
|
14
14
|
"method",
|
|
15
15
|
"level",
|
|
16
|
+
"missing",
|
|
16
17
|
"as"
|
|
17
18
|
]);
|
|
18
19
|
|
|
@@ -42,6 +43,7 @@ export const createIntervalData = /* @__PURE__ */ derivedCreator(
|
|
|
42
43
|
extent: args.extent,
|
|
43
44
|
method: args.method,
|
|
44
45
|
level: args.level,
|
|
46
|
+
missing: args.missing,
|
|
45
47
|
as
|
|
46
48
|
});
|
|
47
49
|
},
|
|
@@ -11,6 +11,7 @@ import { derivedMaterializer } from "./shared.js";
|
|
|
11
11
|
const OPTIONS = Object.freeze([
|
|
12
12
|
"id", "source", "x", "y", "groupBy", "method", "degree", "span",
|
|
13
13
|
"confidenceMethod", "level", "confidence", "interval"
|
|
14
|
+
, "predict", "missing"
|
|
14
15
|
]);
|
|
15
16
|
|
|
16
17
|
export const materializeRegressionData = /* @__PURE__ */ derivedMaterializer(
|
|
@@ -27,7 +28,9 @@ export const materializeRegressionData = /* @__PURE__ */ derivedMaterializer(
|
|
|
27
28
|
confidenceMethod: transform.confidenceMethod,
|
|
28
29
|
level: transform.level,
|
|
29
30
|
confidence: transform.confidence,
|
|
30
|
-
interval: transform.interval
|
|
31
|
+
interval: transform.interval,
|
|
32
|
+
predict: transform.predict
|
|
33
|
+
, missing: transform.missing
|
|
31
34
|
})
|
|
32
35
|
);
|
|
33
36
|
|
|
@@ -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
|
|
|
@@ -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.");
|
|
@@ -25,6 +25,8 @@ import {
|
|
|
25
25
|
setEncodingProperties,
|
|
26
26
|
validateOptions
|
|
27
27
|
} from "./shared.js";
|
|
28
|
+
import { applyRequestedItemMissingPolicy } from
|
|
29
|
+
"../../grammar/itemMissing.js";
|
|
28
30
|
|
|
29
31
|
const RADIUS_OPTIONS = Object.freeze(["value", "target"]);
|
|
30
32
|
const REMOVE_RADIUS_OPTIONS = Object.freeze(["target"]);
|
|
@@ -34,13 +36,14 @@ const OPACITY_OPTIONS = Object.freeze([
|
|
|
34
36
|
const FIELD_OPTIONS = Object.freeze(["field", "target", "fieldType", "scale"]);
|
|
35
37
|
function encodeAppearanceField(program, channel, args, operation) {
|
|
36
38
|
validateOptions(args, FIELD_OPTIONS, operation);
|
|
37
|
-
const { id: target, dataset, layer } = resolveTarget(
|
|
39
|
+
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
38
40
|
program,
|
|
39
41
|
args.target,
|
|
40
42
|
["point"],
|
|
41
43
|
"point mark"
|
|
42
44
|
);
|
|
43
45
|
const expectedFieldType = channel === "shape" ? "nominal" : "quantitative";
|
|
46
|
+
const dataset = applyRequestedItemMissingPolicy(layer, sourceDataset, args.field);
|
|
44
47
|
const fieldType = args.fieldType ?? expectedFieldType;
|
|
45
48
|
if (fieldType !== expectedFieldType) {
|
|
46
49
|
throw new Error(`${operation} requires a ${expectedFieldType} field.`);
|
|
@@ -196,7 +199,7 @@ const encodeOpacity = /* @__PURE__ */ action(
|
|
|
196
199
|
if (hasValue && (args.fieldType !== undefined || args.scale !== undefined)) {
|
|
197
200
|
throw new Error("Constant opacity does not accept fieldType or scale.");
|
|
198
201
|
}
|
|
199
|
-
const { id: target, dataset, layer } = resolveTarget(
|
|
202
|
+
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
200
203
|
this,
|
|
201
204
|
args.target,
|
|
202
205
|
["point", "rule", "line"],
|
|
@@ -222,6 +225,7 @@ const encodeOpacity = /* @__PURE__ */ action(
|
|
|
222
225
|
: next.rematerializePointMark({ id: target });
|
|
223
226
|
return applyDetachedScaleRematerialization(materialized, [layer]);
|
|
224
227
|
}
|
|
228
|
+
const dataset = applyRequestedItemMissingPolicy(layer, sourceDataset, args.field);
|
|
225
229
|
const fieldType = args.fieldType ?? "quantitative";
|
|
226
230
|
if (fieldType !== "quantitative") {
|
|
227
231
|
throw new Error("encodeOpacity requires a quantitative field.");
|
|
@@ -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
42
|
["point", "bar", "rect"],
|
|
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
66
|
["point", "line", "bar", "area", "arc", "rect"],
|
|
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"
|
|
@@ -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);
|
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
validateFieldType,
|
|
14
14
|
validatePositionChannel
|
|
15
15
|
} from "../../../grammar/scales/index.js";
|
|
16
|
+
import { applyRequestedItemMissingPolicy } from
|
|
17
|
+
"../../../grammar/itemMissing.js";
|
|
16
18
|
import { resolvePositionScaleDefinition } from "../../scales/definitions.js";
|
|
17
19
|
import { findCoordinate } from "../../../selectors/coordinates.js";
|
|
18
20
|
import {
|
|
@@ -92,7 +94,7 @@ function resolveCoordinate(program, channel, layer, requestedId) {
|
|
|
92
94
|
export function resolvePositionEncoding(program, channel, args, operation) {
|
|
93
95
|
validateOptions(args, POSITION_ENCODING_OPTIONS, operation);
|
|
94
96
|
validatePositionChannel(channel);
|
|
95
|
-
const { id: target, dataset, layer } = resolveTarget(
|
|
97
|
+
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
96
98
|
program,
|
|
97
99
|
args.target,
|
|
98
100
|
getPositionChannelDefinition(channel).markTypes
|
|
@@ -157,6 +159,9 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
157
159
|
xEncoding?.bin !== undefined && args.field === undefined
|
|
158
160
|
? xEncoding.field
|
|
159
161
|
: args.field;
|
|
162
|
+
const dataset = hasField
|
|
163
|
+
? applyRequestedItemMissingPolicy(layer, sourceDataset, field)
|
|
164
|
+
: applyRequestedItemMissingPolicy(layer, sourceDataset);
|
|
160
165
|
const datum = args.datum;
|
|
161
166
|
const temporalUnit = resolveTemporalUnit({ ...args, ...(hasDatum ? {} : { field }) }, fieldType, previous);
|
|
162
167
|
const usesField = !countRadius && (!["rule", "area", "rect", "text", "point", "tick"].includes(layer.mark.type) || hasField);
|
|
@@ -14,6 +14,8 @@ import { deriveAreaSeries } from "../../grammar/areaSeries.js";
|
|
|
14
14
|
import { resolvePositionEncoding } from "./position/resolve.js";
|
|
15
15
|
import { resolveScalePreview } from "../scales/preview.js";
|
|
16
16
|
import { findUpstreamTransform } from "../../materialization/dataProvenance.js";
|
|
17
|
+
import { applyRequestedItemMissingPolicy } from
|
|
18
|
+
"../../grammar/itemMissing.js";
|
|
17
19
|
|
|
18
20
|
const SECONDARY_OPTIONS = Object.freeze([
|
|
19
21
|
"field", "datum", "target", "fieldType", "scale", "coordinate", "temporalUnit"
|
|
@@ -46,7 +48,7 @@ function validateSecondaryField(dataset, field, fieldType, temporalUnit) {
|
|
|
46
48
|
|
|
47
49
|
function encodeSecondaryPosition(program, channel, args, operation, types) {
|
|
48
50
|
validateOptions(args, SECONDARY_OPTIONS, operation);
|
|
49
|
-
const { id: target, dataset, layer } = resolveTarget(
|
|
51
|
+
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
50
52
|
program,
|
|
51
53
|
args.target,
|
|
52
54
|
types,
|
|
@@ -72,6 +74,9 @@ function encodeSecondaryPosition(program, channel, args, operation, types) {
|
|
|
72
74
|
const rect = layer.mark.type === "rect";
|
|
73
75
|
const hasField = Object.hasOwn(args, "field");
|
|
74
76
|
const hasDatum = Object.hasOwn(args, "datum");
|
|
77
|
+
const dataset = hasField
|
|
78
|
+
? applyRequestedItemMissingPolicy(layer, sourceDataset, args.field)
|
|
79
|
+
: applyRequestedItemMissingPolicy(layer, sourceDataset);
|
|
75
80
|
if ((rule || area || rect) && hasField === hasDatum) {
|
|
76
81
|
throw new Error(`${operation} requires exactly one of field or datum for a ${layer.mark.type} mark.`);
|
|
77
82
|
}
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
import { findLayer } from "../../selectors/layers.js";
|
|
15
15
|
import { validatePathSeriesAppearance } from "../../grammar/pathSeries.js";
|
|
16
16
|
import { assertEncodingSelectionCompatibility } from "../../materialization/selection/compatibility.js";
|
|
17
|
+
import { applyRequestedItemMissingPolicy } from
|
|
18
|
+
"../../grammar/itemMissing.js";
|
|
17
19
|
|
|
18
20
|
const WIDTH_OPTIONS = Object.freeze([
|
|
19
21
|
"target", "value", "field", "fieldType", "scale"
|
|
@@ -57,12 +59,13 @@ const encodeStrokeWidth = /* @__PURE__ */ action(
|
|
|
57
59
|
next = next.rematerializeRuleMark({ id });
|
|
58
60
|
return applyDetachedScaleRematerialization(next, [layer]);
|
|
59
61
|
}
|
|
60
|
-
const { id, dataset, layer } = resolveTarget(
|
|
62
|
+
const { id, dataset: sourceDataset, layer } = resolveTarget(
|
|
61
63
|
this,
|
|
62
64
|
args.target,
|
|
63
65
|
["rule", "line"],
|
|
64
66
|
"rule or line mark"
|
|
65
67
|
);
|
|
68
|
+
const dataset = applyRequestedItemMissingPolicy(layer, sourceDataset, args.field);
|
|
66
69
|
const fieldType = args.fieldType ?? "quantitative";
|
|
67
70
|
if (fieldType !== "quantitative") {
|
|
68
71
|
throw new Error("encodeStrokeWidth requires a quantitative field.");
|
|
@@ -26,6 +26,8 @@ import {
|
|
|
26
26
|
setEncodingProperties,
|
|
27
27
|
validateOptions
|
|
28
28
|
} from "./shared.js";
|
|
29
|
+
import { applyRequestedItemMissingPolicy } from
|
|
30
|
+
"../../grammar/itemMissing.js";
|
|
29
31
|
|
|
30
32
|
const OPTIONS = Object.freeze([
|
|
31
33
|
"target", "value", "field", "fieldType", "temporalUnit", "scale"
|
|
@@ -122,7 +124,7 @@ export const encodeStroke = /* @__PURE__ */ action(
|
|
|
122
124
|
"Constant stroke does not accept fieldType, temporalUnit, or scale."
|
|
123
125
|
);
|
|
124
126
|
}
|
|
125
|
-
const { id: target, dataset, layer } = resolveTarget(
|
|
127
|
+
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
126
128
|
this,
|
|
127
129
|
args.target,
|
|
128
130
|
MARK_TYPES,
|
|
@@ -155,6 +157,7 @@ export const encodeStroke = /* @__PURE__ */ action(
|
|
|
155
157
|
}
|
|
156
158
|
|
|
157
159
|
const fieldType = args.fieldType ?? "nominal";
|
|
160
|
+
const dataset = applyRequestedItemMissingPolicy(layer, sourceDataset, args.field);
|
|
158
161
|
const previous = layer.encoding?.stroke;
|
|
159
162
|
const temporalUnit = resolveTemporalUnit(args, fieldType, previous);
|
|
160
163
|
const requestedScale = resolveReassignmentScaleOptions(
|
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
} from "./shared.js";
|
|
16
16
|
import { findLayer } from "../../selectors/layers.js";
|
|
17
17
|
import { validatePathSeriesAppearance } from "../../grammar/pathSeries.js";
|
|
18
|
+
import { applyRequestedItemMissingPolicy } from
|
|
19
|
+
"../../grammar/itemMissing.js";
|
|
18
20
|
|
|
19
21
|
const STROKE_DASH_ENCODING_OPTIONS = Object.freeze([
|
|
20
22
|
"field", "value", "target", "fieldType", "scale"
|
|
@@ -73,12 +75,15 @@ const encodeStrokeDash = /* @__PURE__ */ action(
|
|
|
73
75
|
if (hasValue && (args.fieldType !== undefined || args.scale !== undefined)) {
|
|
74
76
|
throw new Error("Constant stroke dash does not accept fieldType or scale.");
|
|
75
77
|
}
|
|
76
|
-
const { id: target, dataset, layer } = resolveTarget(
|
|
78
|
+
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
77
79
|
this,
|
|
78
80
|
args.target,
|
|
79
81
|
["line", "rule"],
|
|
80
82
|
"line or rule mark"
|
|
81
83
|
);
|
|
84
|
+
const dataset = hasField
|
|
85
|
+
? applyRequestedItemMissingPolicy(layer, sourceDataset, args.field)
|
|
86
|
+
: sourceDataset;
|
|
82
87
|
if (hasValue) {
|
|
83
88
|
normalizeStrokeDashPattern(args.value);
|
|
84
89
|
validatePathSeriesAppearance(dataset.values, {
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
setEncodingProperties,
|
|
10
10
|
validateOptions
|
|
11
11
|
} from "./shared.js";
|
|
12
|
+
import { applyRequestedItemMissingPolicy } from
|
|
13
|
+
"../../grammar/itemMissing.js";
|
|
12
14
|
|
|
13
15
|
const OPTIONS = Object.freeze(["target", "field", "value", "content", "normalizeBy", "format"]);
|
|
14
16
|
|
|
@@ -41,12 +43,15 @@ export const encodeText = /* @__PURE__ */ action(
|
|
|
41
43
|
if (Object.hasOwn(args, "normalizeBy") && (!hasContent || args.content !== "share")) {
|
|
42
44
|
throw new Error("Text normalizeBy is only supported with share content.");
|
|
43
45
|
}
|
|
44
|
-
const { id: target, dataset, layer } = resolveTarget(
|
|
46
|
+
const { id: target, dataset: sourceDataset, layer } = resolveTarget(
|
|
45
47
|
this,
|
|
46
48
|
args.target,
|
|
47
49
|
["text"],
|
|
48
50
|
"text mark"
|
|
49
51
|
);
|
|
52
|
+
const dataset = hasField
|
|
53
|
+
? applyRequestedItemMissingPolicy(layer, sourceDataset, args.field)
|
|
54
|
+
: applyRequestedItemMissingPolicy(layer, sourceDataset);
|
|
50
55
|
const previous = layer.encoding?.text;
|
|
51
56
|
const format = validateTextFormat(args.format ?? previous?.format ?? "auto");
|
|
52
57
|
const content = hasContent
|
|
@@ -14,9 +14,10 @@ import { requestedRectStyleDetails } from
|
|
|
14
14
|
"../../../grammar/roundedRect.js";
|
|
15
15
|
import { STROKE_STYLE_PROPERTIES } from
|
|
16
16
|
"../../../grammar/strokeStyle.js";
|
|
17
|
+
import { validateItemMissing } from "../../../grammar/itemMissing.js";
|
|
17
18
|
|
|
18
19
|
const CREATE_OPTIONS = Object.freeze([
|
|
19
|
-
"id", "data", "fill", "opacity", "stroke", "strokeWidth",
|
|
20
|
+
"id", "data", "missing", "fill", "opacity", "stroke", "strokeWidth",
|
|
20
21
|
"cornerRadius", ...STROKE_STYLE_PROPERTIES
|
|
21
22
|
]);
|
|
22
23
|
|
|
@@ -52,6 +53,12 @@ export const createBarMark = /* @__PURE__ */ action(
|
|
|
52
53
|
property: `layer[${id}].data`,
|
|
53
54
|
value: data
|
|
54
55
|
});
|
|
56
|
+
if (Object.hasOwn(args, "missing")) {
|
|
57
|
+
created = created.editSemantic({
|
|
58
|
+
property: `layer[${id}].mark.missing`,
|
|
59
|
+
value: validateItemMissing(args.missing, "Bar missing")
|
|
60
|
+
});
|
|
61
|
+
}
|
|
55
62
|
created = applyLayeredMarkInheritance(created, id, inherited);
|
|
56
63
|
created = created
|
|
57
64
|
.createGraphics({
|
|
@@ -14,9 +14,10 @@ import { requestedRectStyleDetails } from
|
|
|
14
14
|
import { STROKE_STYLE_PROPERTIES } from
|
|
15
15
|
"../../../grammar/strokeStyle.js";
|
|
16
16
|
import { rematerializeExistingLegend } from "../../encodings/shared.js";
|
|
17
|
+
import { validateItemMissing } from "../../../grammar/itemMissing.js";
|
|
17
18
|
|
|
18
19
|
const EDIT_OPTIONS = Object.freeze([
|
|
19
|
-
"target", "fill", "opacity", "stroke", "strokeWidth", "cornerRadius",
|
|
20
|
+
"target", "missing", "fill", "opacity", "stroke", "strokeWidth", "cornerRadius",
|
|
20
21
|
...STROKE_STYLE_PROPERTIES
|
|
21
22
|
]);
|
|
22
23
|
|
|
@@ -29,12 +30,12 @@ export const editBarMark = /* @__PURE__ */ action(
|
|
|
29
30
|
validateMarkOptions(args, EDIT_OPTIONS, "editBarMark");
|
|
30
31
|
const styleDetails = requestedRectStyleDetails(args, "editBarMark");
|
|
31
32
|
const changes = [
|
|
32
|
-
"fill", "opacity", "stroke", "strokeWidth", "cornerRadius",
|
|
33
|
+
"missing", "fill", "opacity", "stroke", "strokeWidth", "cornerRadius",
|
|
33
34
|
...STROKE_STYLE_PROPERTIES
|
|
34
35
|
];
|
|
35
36
|
if (!changes.some(key => Object.hasOwn(args, key))) {
|
|
36
37
|
throw new Error(
|
|
37
|
-
"editBarMark requires fill, opacity, stroke, or strokeWidth."
|
|
38
|
+
"editBarMark requires fill, opacity, stroke, or strokeWidth; missing is also editable."
|
|
38
39
|
);
|
|
39
40
|
}
|
|
40
41
|
const requested = Object.hasOwn(args, "target")
|
|
@@ -61,7 +62,13 @@ export const editBarMark = /* @__PURE__ */ action(
|
|
|
61
62
|
);
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
const
|
|
65
|
+
const semantic = Object.hasOwn(args, "missing")
|
|
66
|
+
? this.editSemantic({
|
|
67
|
+
property: `layer[${layer.id}].mark.missing`,
|
|
68
|
+
value: validateItemMissing(args.missing, "Bar missing")
|
|
69
|
+
})
|
|
70
|
+
: this;
|
|
71
|
+
const config = { ...semantic.markConfigs[layer.id] };
|
|
65
72
|
const appearance = { ...config.barAppearance };
|
|
66
73
|
Object.assign(appearance, styleDetails);
|
|
67
74
|
if (Object.hasOwn(args, "fill")) {
|
|
@@ -95,7 +102,7 @@ export const editBarMark = /* @__PURE__ */ action(
|
|
|
95
102
|
);
|
|
96
103
|
}
|
|
97
104
|
|
|
98
|
-
const next =
|
|
105
|
+
const next = semantic._withMarkConfig(layer.id, {
|
|
99
106
|
...config,
|
|
100
107
|
barAppearance: appearance
|
|
101
108
|
});
|
|
@@ -16,9 +16,10 @@ import {
|
|
|
16
16
|
resolveMarkId,
|
|
17
17
|
validateMarkOptions
|
|
18
18
|
} from "../shared.js";
|
|
19
|
+
import { validateItemMissing } from "../../../grammar/itemMissing.js";
|
|
19
20
|
|
|
20
21
|
const OPTIONS = Object.freeze([
|
|
21
|
-
"id", "data", "shape", "fill", "opacity", "stroke", "strokeWidth",
|
|
22
|
+
"id", "data", "missing", "shape", "fill", "opacity", "stroke", "strokeWidth",
|
|
22
23
|
...STROKE_STYLE_PROPERTIES
|
|
23
24
|
]);
|
|
24
25
|
|
|
@@ -61,6 +62,12 @@ export const createPointMark = /* @__PURE__ */ action(
|
|
|
61
62
|
property: `layer[${id}].data`,
|
|
62
63
|
value: data
|
|
63
64
|
});
|
|
65
|
+
if (Object.hasOwn(args, "missing")) {
|
|
66
|
+
next = next.editSemantic({
|
|
67
|
+
property: `layer[${id}].mark.missing`,
|
|
68
|
+
value: validateItemMissing(args.missing, "Point missing")
|
|
69
|
+
});
|
|
70
|
+
}
|
|
64
71
|
next = applyLayeredMarkInheritance(next, id, inherited)
|
|
65
72
|
.createGraphics({
|
|
66
73
|
id,
|