ggaction 0.0.20 → 0.0.22
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 +77 -0
- package/README.md +3 -2
- package/knowledge/action-cards.json +1 -1
- package/knowledge/intent-taxonomy.json +1 -1
- package/knowledge/mcp-resources.json +1 -1
- package/package.json +6 -2
- package/src/BasicChartProgram.js +1 -1
- package/src/accessibility.js +1 -0
- package/src/actions/boxPlots/components.js +1 -96
- package/src/actions/boxPlots/create.js +6 -1
- package/src/actions/boxPlots/edit.js +8 -0
- package/src/actions/boxPlots/materialize.js +3 -0
- package/src/actions/boxPlots/options.js +1 -1
- package/src/actions/canvas/actions.js +2 -1
- package/src/actions/canvas/index.js +3 -0
- package/src/actions/canvas/plotBorder.js +2 -1
- package/src/actions/canvas/plotOrder.js +14 -0
- package/src/actions/charts/area.js +1 -1
- package/src/actions/charts/bar.js +4 -4
- package/src/actions/charts/density.js +1 -1
- package/src/actions/charts/ecdf.js +1 -1
- package/src/actions/charts/endpoints.js +54 -5
- package/src/actions/charts/heatmap.js +1 -1
- package/src/actions/charts/histogram.js +1 -1
- package/src/actions/charts/horizon.js +1 -1
- package/src/actions/charts/index.js +2 -1
- package/src/actions/charts/interval-regression.js +1 -1
- package/src/actions/charts/line.js +1 -1
- package/src/actions/charts/radar.js +63 -3
- package/src/actions/charts/raincloud.js +37 -6
- package/src/actions/charts/rug-strip.js +2 -2
- package/src/actions/charts/scatter.js +3 -3
- package/src/actions/charts/text.js +1 -1
- package/src/actions/composition/actions.js +10 -3
- package/src/actions/coordinates/actions.js +2 -0
- package/src/actions/data/basic.js +6 -0
- package/src/actions/data/derived.js +6 -2
- package/src/actions/data/edit.js +21 -35
- package/src/actions/data/filter.js +1 -376
- package/src/actions/data/flatten.js +19 -0
- package/src/actions/data/index.js +6 -0
- package/src/actions/data/lookup.js +50 -0
- package/src/actions/data/raincloudFilter.js +56 -0
- package/src/actions/data/shared.js +10 -0
- package/src/actions/distributions/revision.js +3 -1
- package/src/actions/encodings/angle.js +2 -1
- package/src/actions/encodings/appearance.js +38 -13
- package/src/actions/encodings/channels.js +14 -3
- package/src/actions/encodings/color/continuous.js +26 -10
- package/src/actions/encodings/color/index.js +50 -20
- package/src/actions/encodings/color/policy.js +57 -4
- package/src/actions/encodings/parallel.js +4 -0
- package/src/actions/encodings/position/policies/bar.js +10 -6
- package/src/actions/encodings/position/policies/line.js +1 -152
- package/src/actions/encodings/position/resolve.js +4 -1
- package/src/actions/encodings/ranged.js +46 -0
- package/src/actions/encodings/remove.js +1 -1
- package/src/actions/encodings/ruleAppearance.js +7 -6
- package/src/actions/encodings/shared.js +14 -2
- package/src/actions/encodings/stroke.js +20 -14
- package/src/actions/encodings/strokeDash.js +9 -11
- package/src/actions/encodings/text.js +30 -2
- package/src/actions/errorBars/edit.js +24 -4
- package/src/actions/facets/actions.js +1 -793
- package/src/actions/facets/derive.js +34 -5
- package/src/actions/facets/replay.js +1 -38
- package/src/actions/gradientPlots/components.js +21 -9
- package/src/actions/gradientPlots/create.js +4 -2
- package/src/actions/gradientPlots/edit.js +18 -1
- package/src/actions/gradientPlots/materialize.js +35 -36
- package/src/actions/gradientPlots/options.js +1 -1
- package/src/actions/gradientPlots/paint.js +13 -7
- package/src/actions/guides/applicability.js +2 -0
- package/src/actions/guides/axes/axes.js +39 -0
- package/src/actions/guides/axes/axis.js +9 -5
- package/src/actions/guides/axes/edit.js +11 -8
- package/src/actions/guides/axes/labels.js +24 -8
- package/src/actions/guides/axes/parallel/policy.js +3 -5
- package/src/actions/guides/axes/parallel/resolve.js +4 -3
- package/src/actions/guides/axes/policy.js +22 -0
- package/src/actions/guides/axes/tickGroups.js +4 -2
- package/src/actions/guides/axes/ticks.js +22 -4
- package/src/actions/guides/axes/titles.js +19 -6
- package/src/actions/guides/facadeAxes.js +3 -0
- package/src/actions/guides/grids/grid.js +3 -1
- package/src/actions/guides/grids/resolve.js +7 -11
- package/src/actions/guides/guides.js +21 -1
- package/src/actions/guides/legends/categorical/actions.js +1 -515
- package/src/actions/guides/legends/categorical/layout.js +5 -2
- package/src/actions/guides/legends/categorical/options.js +4 -0
- package/src/actions/guides/legends/categorical/resolve.js +8 -3
- package/src/actions/guides/legends/categorical/symbols.js +3 -3
- package/src/actions/guides/legends/colorSize.js +2 -0
- package/src/actions/guides/legends/continuous/common.js +1 -1
- package/src/actions/guides/legends/continuous/gradient.js +1 -360
- package/src/actions/guides/legends/continuous/interval.js +1 -1
- package/src/actions/guides/legends/continuous/opacity.js +15 -1
- package/src/actions/guides/legends/edit.js +1 -1
- package/src/actions/guides/legends/focused.js +1 -1
- package/src/actions/guides/legends/lane.js +1 -1
- package/src/actions/guides/legends/sampling.js +1 -140
- package/src/actions/guides/legends/strokeWidth.js +3 -2
- package/src/actions/guides/legends/transition.js +19 -2
- package/src/actions/guides/polar/axes/facade.js +5 -4
- package/src/actions/guides/polar/axes/labels.js +5 -4
- package/src/actions/guides/polar/axes/titles.js +5 -4
- package/src/actions/guides/polar/resolve.js +2 -1
- package/src/actions/guides/tickValues.js +7 -7
- package/src/actions/marks/arc/actions.js +5 -7
- package/src/actions/marks/area/actions.js +9 -3
- package/src/actions/marks/bar/create.js +4 -2
- package/src/actions/marks/bar/edit.js +9 -6
- package/src/actions/marks/bar/materialize.js +5 -3
- package/src/actions/marks/dependentReferences.js +87 -0
- package/src/actions/marks/index.js +2 -0
- package/src/actions/marks/lifecycle.js +2 -1
- package/src/actions/marks/line/actions.js +24 -6
- package/src/actions/marks/line/materialize.js +7 -3
- package/src/actions/marks/point/create.js +5 -4
- package/src/actions/marks/point/edit.js +6 -3
- package/src/actions/marks/point/jitter.js +6 -6
- package/src/actions/marks/point/materialize.js +36 -39
- package/src/actions/marks/rect/actions.js +16 -4
- package/src/actions/marks/references.js +183 -35
- package/src/actions/marks/rule/actions.js +20 -9
- package/src/actions/marks/shared.js +2 -1
- package/src/actions/marks/text/actions.js +10 -3
- package/src/actions/marks/tick/actions.js +11 -2
- package/src/actions/primitives/editGraphics.js +28 -3
- package/src/actions/primitives/index.js +9 -0
- package/src/actions/primitives/semanticAction.js +13 -7
- package/src/actions/primitives/semanticValidation/layer.js +20 -0
- package/src/actions/primitives/semanticValidation/scale.js +8 -1
- package/src/actions/scales/channels.js +1 -1
- package/src/actions/scales/consumers/common.js +19 -1
- package/src/actions/scales/consumers/families.js +2 -2
- package/src/actions/scales/consumers/seriesLayout.js +4 -3
- package/src/actions/scales/edit.js +11 -0
- package/src/actions/scales/editPolicy.js +5 -3
- package/src/actions/scales/preview.js +2 -1
- package/src/actions/scales/quantitativeColor.js +2 -4
- package/src/actions/selection/actions.js +10 -1
- package/src/actions/theme/reconcile.js +37 -9
- package/src/core/compositionState.js +21 -4
- package/src/core/datasetReferences.js +17 -0
- package/src/core/immutable.js +1 -0
- package/src/core/resourceReferences.js +5 -3
- package/src/core/validation.js +4 -0
- package/src/core/vocabulary.js +24 -2
- package/src/grammar/aggregate.js +1 -0
- package/src/grammar/arcs.js +1 -1
- package/src/grammar/areaSeries.js +1 -1
- package/src/grammar/axisTextPlacement.js +1 -1
- package/src/grammar/bars/policy.js +2 -2
- package/src/grammar/datasetDependencies.js +97 -0
- package/src/grammar/datasetSchema.js +31 -1
- package/src/grammar/facetHeaders.js +1 -0
- package/src/grammar/facets/dependencies.js +6 -2
- package/src/grammar/facets/legendIntent.js +6 -2
- package/src/grammar/facets/scales.js +27 -7
- package/src/grammar/flatten.js +85 -0
- package/src/grammar/histogram.js +13 -1
- package/src/grammar/horizon.js +1 -494
- package/src/grammar/interval.js +4 -3
- package/src/grammar/jitter.js +2 -2
- package/src/grammar/legendSpacing.js +45 -0
- package/src/grammar/lineMissing.js +90 -0
- package/src/grammar/lineSeries.js +1 -376
- package/src/grammar/lookup.js +97 -0
- package/src/grammar/markFilter.js +4 -2
- package/src/grammar/markLabels.js +8 -3
- package/src/grammar/numeric.js +1 -1
- package/src/grammar/pathCommands.js +1 -1
- package/src/grammar/pathSeries.js +1 -109
- package/src/grammar/pointPolygon.js +81 -0
- package/src/grammar/pointShapes.js +15 -15
- package/src/grammar/rects.js +1 -1
- package/src/grammar/regression/derive.js +1 -195
- package/src/grammar/scales/appearance.js +1 -1
- package/src/grammar/scales/color.js +1 -1
- package/src/grammar/scales/colorConsumers.js +1 -63
- package/src/grammar/scales/continuous.js +58 -1
- package/src/grammar/scales/definition.js +1 -1
- package/src/grammar/scales/ordinal.js +5 -2
- package/src/grammar/scales/policies.js +3 -3
- package/src/grammar/schemas/graphicBounds.js +40 -12
- package/src/grammar/schemas/graphicClip.js +23 -0
- package/src/grammar/schemas/graphicTree.js +16 -0
- package/src/grammar/statisticalReference.js +22 -0
- package/src/grammar/text.js +65 -1
- package/src/grammar/ticks.js +92 -1
- package/src/grammar/transformTopology.js +3 -1
- package/src/grammar/transforms.js +24 -0
- package/src/inspection.js +1 -495
- package/src/inspectionDescriptors.js +1 -1
- package/src/layout/canvas.js +1 -1
- package/src/layout/facetContentFlow.js +101 -0
- package/src/layout/facetHeaderTitles.js +1 -0
- package/src/layout/facetTopLane.js +29 -0
- package/src/layout/facets.js +1 -1
- package/src/layout/legendGroup.js +1 -1
- package/src/layout/legendItems.js +26 -10
- package/src/layout/legendSpacing.js +66 -0
- package/src/layout/plotDrawOrder.js +17 -0
- package/src/materialization/bars/histogram.js +8 -2
- package/src/materialization/bars/resolve.js +1 -1
- package/src/materialization/composition.js +1 -0
- package/src/materialization/compositionSnapshot.js +22 -2
- package/src/materialization/dataProvenance.js +1 -1
- package/src/materialization/facetGuides/layout.js +1 -1
- package/src/materialization/facetGuides/placement.js +23 -11
- package/src/materialization/facetGuides/preparation.js +43 -24
- package/src/materialization/facetHeaders.js +1 -1
- package/src/materialization/facetTopLane.js +25 -0
- package/src/materialization/facets.js +1 -1
- package/src/materialization/graphicHierarchy.js +5 -0
- package/src/materialization/graphicTranslation.js +2 -0
- package/src/materialization/guides/layout.js +1 -1
- package/src/materialization/guides/resources.js +1 -1
- package/src/materialization/legends.js +1 -1
- package/src/materialization/markClip.js +26 -0
- package/src/materialization/marks/capabilities.js +3 -2
- package/src/materialization/marks/grouping.js +7 -5
- package/src/materialization/marks/index.js +4 -2
- package/src/materialization/marks/policies.js +4 -0
- package/src/materialization/plotDrawOrder.js +44 -0
- package/src/materialization/rect.js +11 -3
- package/src/materialization/scaleGuideDependencies.js +13 -5
- package/src/materialization/selection/items/common.js +1 -1
- package/src/materialization/selection/items/path.js +1 -0
- package/src/materialization/selection/policies/index.js +2 -1
- package/src/materialization/text.js +10 -5
- package/src/mcp/cli.js +0 -0
- package/src/persistence/validation.js +108 -18
- package/src/persistence.js +4 -2
- package/src/renderers/canvas/index.js +16 -0
- package/src/renderers/canvas/isolated.js +11 -1
- package/src/renderers/canvas/native.js +7 -0
- package/src/renderers/canvas/scene.js +66 -0
- package/src/renderers/canvas/text.js +3 -2
- package/src/renderers/svg.js +1 -617
- package/src/renderers/svgSerialization.js +643 -0
- package/src/renderers/transitionScene.js +67 -0
- package/src/scene.js +89 -0
- package/src/selectors/axes.js +1 -1
- package/src/selectors/datasets.js +1 -0
- package/src/selectors/grids.js +9 -0
- package/src/selectors/markClip.js +28 -0
- package/src/selectors/raincloud.js +7 -0
- package/src/version.js +1 -1
- package/types/basic.d.ts +7 -0
- package/types/index.d.ts +8 -0
- package/types/program.d.ts +270 -60
- package/types/scene.d.ts +35 -0
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
resolveEffectiveThemeTokens,
|
|
7
7
|
setThemeStateOverrides
|
|
8
8
|
} from "./state.js";
|
|
9
|
+
import { isOwned } from "../../core/immutable.js";
|
|
9
10
|
import { findLayer } from "../../selectors/layers.js";
|
|
10
11
|
import { reconcileFollowingRegressions } from "../regression/follow.js";
|
|
11
12
|
|
|
@@ -285,6 +286,14 @@ function recolorGraphic(
|
|
|
285
286
|
targetTokens,
|
|
286
287
|
overrides
|
|
287
288
|
) {
|
|
289
|
+
if (id === `${program.compositionSpec?.id}-headers`) {
|
|
290
|
+
const headers = program.materializationConfigs.facets?.[program.compositionSpec.id]?.headers;
|
|
291
|
+
if (headers?.column?.title !== undefined || headers?.row?.title !== undefined) {
|
|
292
|
+
// Role titles and category labels have independently owned paint. Their
|
|
293
|
+
// reconciled header configs already materialized the complete collection.
|
|
294
|
+
return program;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
288
297
|
if (graphic.items === undefined) {
|
|
289
298
|
const mapped = mapGraphicProperties(
|
|
290
299
|
program,
|
|
@@ -663,18 +672,27 @@ function visitTrace(node, visitor) {
|
|
|
663
672
|
for (const child of node.children ?? []) visitTrace(child, visitor);
|
|
664
673
|
}
|
|
665
674
|
|
|
666
|
-
|
|
675
|
+
// Completed immutable action branches never acquire new removals. Retain only
|
|
676
|
+
// their distinct reset prefixes so later themed edits do not rescan old facet
|
|
677
|
+
// materialization traces. Prefix deletion commutes; authored overrides are still
|
|
678
|
+
// applied in their original top-level action order.
|
|
679
|
+
const removedResources = new WeakMap();
|
|
680
|
+
|
|
681
|
+
function removedResourcePrefixes(node) {
|
|
682
|
+
const cached = removedResources.get(node);
|
|
683
|
+
if (cached !== undefined) return cached;
|
|
684
|
+
const prefixes = new Set();
|
|
667
685
|
visitTrace(node, current => {
|
|
668
686
|
if (current.op === "editGraphics" && current.args.remove === true &&
|
|
669
687
|
typeof current.args.target === "string") {
|
|
670
|
-
|
|
688
|
+
prefixes.add(`g:${current.args.target}.`);
|
|
671
689
|
}
|
|
672
690
|
if (current.op !== "editSemantic" || current.args.remove !== true ||
|
|
673
691
|
typeof current.args.property !== "string") return;
|
|
674
692
|
const mark = /^layer\[([^\]]+)\]$/u.exec(current.args.property);
|
|
675
693
|
if (mark !== null) {
|
|
676
|
-
|
|
677
|
-
|
|
694
|
+
prefixes.add(`c:marks.${mark[1]}.`);
|
|
695
|
+
prefixes.add(`g:${mark[1]}.`);
|
|
678
696
|
return;
|
|
679
697
|
}
|
|
680
698
|
const axis = /^guide\.axis\.([A-Za-z0-9_-]+)$/u
|
|
@@ -682,21 +700,28 @@ function clearRemovedResources(overrides, node) {
|
|
|
682
700
|
if (axis !== null) {
|
|
683
701
|
const prefix = axis[1] === "radius" ? "radial" : axis[1];
|
|
684
702
|
const graphicPrefix = ["x", "y", "theta", "radius"].includes(axis[1]) ? `${prefix}Axis` : `axis_${axis[1]}__`;
|
|
685
|
-
|
|
686
|
-
|
|
703
|
+
prefixes.add(`c:guides.axis.${axis[1]}.`);
|
|
704
|
+
prefixes.add(`g:${graphicPrefix}`);
|
|
687
705
|
return;
|
|
688
706
|
}
|
|
689
707
|
const grid = /^guide\.grid\.([^\.]+)$/u.exec(current.args.property);
|
|
690
708
|
if (grid !== null) {
|
|
691
|
-
|
|
692
|
-
|
|
709
|
+
prefixes.add(`c:guides.grid.${grid[1]}.`);
|
|
710
|
+
prefixes.add(`g:${grid[1]}Grid`);
|
|
693
711
|
return;
|
|
694
712
|
}
|
|
695
713
|
const legend = /^guide\.legend\.([^\.]+)$/u.exec(current.args.property);
|
|
696
714
|
if (legend !== null) {
|
|
697
|
-
|
|
715
|
+
prefixes.add(`c:guides.legend.${legend[1]}.`);
|
|
698
716
|
}
|
|
699
717
|
});
|
|
718
|
+
const result = [...prefixes];
|
|
719
|
+
if (isOwned(node)) removedResources.set(node, result);
|
|
720
|
+
return result;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
function clearRemovedResources(overrides, node) {
|
|
724
|
+
for (const prefix of removedResourcePrefixes(node)) clearPrefix(overrides, prefix);
|
|
700
725
|
}
|
|
701
726
|
|
|
702
727
|
function clearLocalResets(overrides, program, node) {
|
|
@@ -970,6 +995,9 @@ function addFacetOverrides(overrides, program, args) {
|
|
|
970
995
|
["color", "fill"],
|
|
971
996
|
["fontFamily", "fontFamily"]
|
|
972
997
|
]) {
|
|
998
|
+
if (Object.hasOwn(args.title ?? {}, property)) {
|
|
999
|
+
overrides.add(`c:facets.${id}.headers.${owner}.title.${property}`);
|
|
1000
|
+
}
|
|
973
1001
|
if (!Object.hasOwn(args, property)) continue;
|
|
974
1002
|
overrides.add(`c:facets.${id}.headers.${owner}.${property}`);
|
|
975
1003
|
overrides.add(`g:${id}-headers.${graphicProperty}`);
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import { isPlainObject } from "./immutable.js";
|
|
1
|
+
import { isOwned, isPlainObject } from "./immutable.js";
|
|
2
2
|
import { ownProgramState } from "./programState.js";
|
|
3
3
|
import {
|
|
4
4
|
FACET_SCALE_CHANNELS,
|
|
5
|
-
FACET_SCALE_RESOLUTIONS
|
|
5
|
+
FACET_SCALE_RESOLUTIONS,
|
|
6
|
+
FACET_TOP_LANE_MODES,
|
|
7
|
+
DEFAULT_FACET_TOP_LANE
|
|
6
8
|
} from "./vocabulary.js";
|
|
7
9
|
|
|
10
|
+
// Primitive graphics/trace edits retain both immutable owners. Validate their
|
|
11
|
+
// relationship once, rather than scanning every facet cell on every primitive.
|
|
12
|
+
const validatedCompositions = new WeakMap();
|
|
13
|
+
|
|
8
14
|
function validateFacetSpec(compositionSpec) {
|
|
9
15
|
if (compositionSpec.direction !== undefined) {
|
|
10
16
|
throw new Error("Facet compositionSpec does not use direction.");
|
|
@@ -260,6 +266,7 @@ function validateLayout(compositionSpec) {
|
|
|
260
266
|
}
|
|
261
267
|
|
|
262
268
|
export function ownCompositionSpec(compositionSpec, children) {
|
|
269
|
+
if (validatedCompositions.get(compositionSpec)?.has(children)) return compositionSpec;
|
|
263
270
|
const childIds = Object.keys(children);
|
|
264
271
|
if (compositionSpec === undefined) {
|
|
265
272
|
if (childIds.length > 0) {
|
|
@@ -272,7 +279,7 @@ export function ownCompositionSpec(compositionSpec, children) {
|
|
|
272
279
|
}
|
|
273
280
|
const allowed = [
|
|
274
281
|
"id", "type", "direction", "children", "columns", "gap", "align",
|
|
275
|
-
"padding", "facet", "spacing"
|
|
282
|
+
"padding", "facet", "spacing", "topLane"
|
|
276
283
|
];
|
|
277
284
|
const unknown = Object.keys(compositionSpec).find(key => !allowed.includes(key));
|
|
278
285
|
if (unknown !== undefined) {
|
|
@@ -285,6 +292,11 @@ export function ownCompositionSpec(compositionSpec, children) {
|
|
|
285
292
|
if (compositionSpec.spacing !== undefined && (!facet || !["plot", "canvas"].includes(compositionSpec.spacing))) {
|
|
286
293
|
throw new Error("compositionSpec.spacing requires a facet and canvas or plot spacing.");
|
|
287
294
|
}
|
|
295
|
+
if (compositionSpec.topLane !== undefined && (!facet ||
|
|
296
|
+
!FACET_TOP_LANE_MODES.includes(compositionSpec.topLane) ||
|
|
297
|
+
compositionSpec.topLane === DEFAULT_FACET_TOP_LANE || compositionSpec.spacing !== "plot")) {
|
|
298
|
+
throw new Error('compositionSpec.topLane requires a facet with plot spacing and canonical shared intent.');
|
|
299
|
+
}
|
|
288
300
|
if (compositionSpec.type !== undefined && !facet) {
|
|
289
301
|
throw new Error(`Unknown compositionSpec type "${compositionSpec.type}".`);
|
|
290
302
|
}
|
|
@@ -301,5 +313,10 @@ export function ownCompositionSpec(compositionSpec, children) {
|
|
|
301
313
|
throw new Error("Concat compositionSpec does not accept facet properties.");
|
|
302
314
|
}
|
|
303
315
|
validateLayout(compositionSpec);
|
|
304
|
-
|
|
316
|
+
const owned = ownProgramState(compositionSpec);
|
|
317
|
+
if (isOwned(children)) {
|
|
318
|
+
if (!validatedCompositions.has(owned)) validatedCompositions.set(owned, new WeakSet());
|
|
319
|
+
validatedCompositions.get(owned).add(children);
|
|
320
|
+
}
|
|
321
|
+
return owned;
|
|
305
322
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Stored dataset reference paths have one owner across resource and replay policies.
|
|
2
|
+
export function datasetInputReferences(dataset) {
|
|
3
|
+
const refs = dataset.source === undefined ? [] : [{ id: dataset.source, path: ["source"] }];
|
|
4
|
+
if (dataset.transform?.[0]?.type === "lookup") {
|
|
5
|
+
refs.push({ id: dataset.transform[0].lookup.data, path: ["transform", 0, "lookup", "data"] });
|
|
6
|
+
}
|
|
7
|
+
return refs;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function datasetInputIds(dataset) {
|
|
11
|
+
return [...new Set(datasetInputReferences(dataset).map(ref => ref.id))];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function rebindTransformDatasetInputs(transform, replacements) {
|
|
15
|
+
if (transform.type !== "lookup" || !replacements.has(transform.lookup.data)) return transform;
|
|
16
|
+
return { ...transform, lookup: { ...transform.lookup, data: replacements.get(transform.lookup.data) } };
|
|
17
|
+
}
|
package/src/core/immutable.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { freezeOwned, isPlainObject } from "./immutable.js";
|
|
2
2
|
import { validateUserId } from "./identifiers.js";
|
|
3
|
+
import {datasetInputReferences} from "./datasetReferences.js";
|
|
3
4
|
|
|
4
5
|
export const RESOURCE_KINDS = Object.freeze([
|
|
5
6
|
"data", "scale", "coordinate", "mark", "selection"
|
|
@@ -71,8 +72,8 @@ export function collectSemanticDataReferences(program) {
|
|
|
71
72
|
append(references, reference("data", layer.data, "layer", layer.id, ["data"]));
|
|
72
73
|
}
|
|
73
74
|
for (const dataset of program.semanticSpec.datasets) {
|
|
74
|
-
append(references,
|
|
75
|
-
"data",
|
|
75
|
+
for(const input of datasetInputReferences(dataset))append(references,reference(
|
|
76
|
+
"data",input.id,"dataset",dataset.id,input.path
|
|
76
77
|
));
|
|
77
78
|
}
|
|
78
79
|
return freezeOwned(references);
|
|
@@ -167,7 +168,8 @@ export function collectMarkConfigReferences(program) {
|
|
|
167
168
|
(root === "errorBandBoundary" && key === "owner") ||
|
|
168
169
|
(parentPath.length === 0 && key === "boxSpanOwner") ||
|
|
169
170
|
(root === "raincloudPlot" && (
|
|
170
|
-
key === "ownerId" ||
|
|
171
|
+
key === "ownerId" ||
|
|
172
|
+
(parentPath[1] === "childIds" && config.raincloudPlot.ownedChildIds?.includes(value)) ||
|
|
171
173
|
parentPath[1] === "ownedChildIds"
|
|
172
174
|
))
|
|
173
175
|
) {
|
package/src/core/validation.js
CHANGED
|
@@ -133,3 +133,7 @@ export function validateTextStyle(config, label) {
|
|
|
133
133
|
validateNonEmptyString(config.fontFamily, `${label} fontFamily`);
|
|
134
134
|
validateFontWeight(config.fontWeight, `${label} fontWeight`);
|
|
135
135
|
}
|
|
136
|
+
export function validateBoolean(value, label) {
|
|
137
|
+
if (typeof value !== "boolean") throw new TypeError(`${label} must be a boolean.`);
|
|
138
|
+
return value;
|
|
139
|
+
}
|
package/src/core/vocabulary.js
CHANGED
|
@@ -41,7 +41,9 @@ export const ENCODING_CHANNELS = Object.freeze([
|
|
|
41
41
|
"group",
|
|
42
42
|
"pathOrder",
|
|
43
43
|
"opacity",
|
|
44
|
-
"text"
|
|
44
|
+
"text",
|
|
45
|
+
"dx",
|
|
46
|
+
"dy"
|
|
45
47
|
]);
|
|
46
48
|
|
|
47
49
|
const CARTESIAN_MARK_TYPES = Object.freeze([
|
|
@@ -118,7 +120,7 @@ export const POSITION_CHANNEL_DEFINITIONS = Object.freeze({
|
|
|
118
120
|
|
|
119
121
|
export const SCALED_ENCODING_CHANNELS = Object.freeze(
|
|
120
122
|
ENCODING_CHANNELS.filter(channel =>
|
|
121
|
-
!["angle", "group", "pathOrder", "text"].includes(channel)
|
|
123
|
+
!["angle", "group", "pathOrder", "text", "dx", "dy"].includes(channel)
|
|
122
124
|
)
|
|
123
125
|
);
|
|
124
126
|
|
|
@@ -176,6 +178,9 @@ export const FACET_SCALE_RESOLUTIONS = Object.freeze([
|
|
|
176
178
|
"independent"
|
|
177
179
|
]);
|
|
178
180
|
|
|
181
|
+
export const FACET_TOP_LANE_MODES = Object.freeze(["stacked", "shared"]);
|
|
182
|
+
export const DEFAULT_FACET_TOP_LANE = "stacked";
|
|
183
|
+
|
|
179
184
|
export function getPositionChannelDefinition(channel) {
|
|
180
185
|
return POSITION_CHANNEL_DEFINITIONS[channel];
|
|
181
186
|
}
|
|
@@ -242,3 +247,20 @@ export const LEGEND_POSITIONS = Object.freeze(["left", "right", "top", "bottom",
|
|
|
242
247
|
export const LEGEND_BLOCK_LAYOUTS = Object.freeze(["row", "column", "wrap"]);
|
|
243
248
|
|
|
244
249
|
export const AXIS_RESOURCE_CHANNELS = Object.freeze(["x", "y", "theta", "radius", "parallel"]);
|
|
250
|
+
|
|
251
|
+
// Shared target policy for authoring and read-only action inspection.
|
|
252
|
+
const marks = (...types) => Object.freeze(types);
|
|
253
|
+
export const APPEARANCE_MARK_TYPES = Object.freeze({
|
|
254
|
+
encodeColor: marks('point', 'line', 'bar', 'area', 'arc', 'rect', 'text'),
|
|
255
|
+
encodeStroke: marks('point', 'line', 'area', 'bar', 'rect', 'arc', 'rule', 'tick'),
|
|
256
|
+
encodeOpacity: marks('point', 'rule', 'line', 'bar', 'rect'),
|
|
257
|
+
encodeStrokeWidth: marks('rule', 'line', 'rect'),
|
|
258
|
+
encodeStrokeDash: marks('line', 'rule'),
|
|
259
|
+
encodeSize: marks('point'),
|
|
260
|
+
encodeShape: marks('point'),
|
|
261
|
+
encodeAngle: marks('point', 'tick')
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// Generated default action methods: start
|
|
265
|
+
export const DEFAULT_ACTION_METHODS = /* @__PURE__ */ Object.freeze(["createCanvas","editCanvas","fitCanvas","applyTextMetrics","removeTextMetrics","applyTheme","removeTheme","createData","reviseData","removeData","removeScale","removeCoordinate","bindMarkData","filterData","createSummaryData","createBinData","createLookupData","createFlattenData","createFoldData","createComputedData","createNormalizedData","createSortedData","createCompleteData","createImputedData","createStackData","createDensityData","createRegressionData","createRegressionStatisticsData","createWindowData","createTimeUnitData","createBin2DData","editBin2DData","editDerivedData","editComputedData","editFilteredData","editLookupData","editFlattenData","editFoldData","editSummaryData","editBinData","editTimeUnitData","editWindowData","editDensityData","editStackData","editRegressionData","editRegressionStatisticsData","editIntervalData","editECDFData","editNormalizedData","editSortedData","editCompleteData","editImputedData","createIntervalData","createECDFData","createPointMark","createTickMark","createStatisticalReference","createReferenceLine","createReferenceBand","createMarkLabels","editMarkLabelSelection","editMarkLabelPlacement","removeMarkLabels","createAnnotation","createTextMark","editTextMark","layoutLabels","removeLabelLayout","editPointMark","editTickMark","jitterPoints","removeJitter","packPoints","removePointPacking","removeMark","createLineMark","editLineMark","createBarMark","editBarMark","createAreaMark","createRuleMark","editRuleMark","editAreaMark","encodeX","encodeY","encodeChannels","encodeX2","encodeColor","encodeStrokeDash","encodeStroke","encodeStrokeWidth","encodeSize","encodeShape","encodeAngle","encodeOpacity","encodeRadius","encodeTheta","encodeR","encodePointRadius","removePointRadius","encodeXOffset","encodeYOffset","encodeY2","encodeYRange","encodeXRange","encodeGroup","encodePathOrder","encodeParallelCoordinates","removePathOrder","orderCategories","removeCategoryOrder","removeEncoding","encodeText","encodeDx","encodeDy","encodeHistogram","encodeDensity","editDensity","encodeHorizon","editHorizon","encodeBarWidth","createRegression","editRegression","createErrorBar","editErrorBar","createErrorBand","editErrorBand","editErrorBandBoundary","createBoxPlot","editBoxPlot","createGradientPlot","editGradientPlot","createViolinPlot","editViolinPlot","createParallelAxes","createParallelAxis","editParallelAxis","removeParallelAxis","removeParallelAxes","createAxes","createXAxis","createYAxis","createXAxisLine","createYAxisLine","editXAxisLine","editYAxisLine","createXAxisTicks","createYAxisTicks","editXAxisTicks","editYAxisTicks","createXAxisLabels","createYAxisLabels","editXAxisLabels","editYAxisLabels","createXAxisTicksAndLabels","createYAxisTicksAndLabels","editXAxisTicksAndLabels","editYAxisTicksAndLabels","createXAxisTitle","createYAxisTitle","editXAxisTitle","editYAxisTitle","editAxes","editXAxis","editYAxis","removeXAxis","removeYAxis","createGrid","createHorizontalGrid","createVerticalGrid","editHorizontalGrid","editVerticalGrid","editGrid","removeGrid","createLegend","editLegend","editLegendBlock","editLegendLayout","editLegendLabels","editLegendTitle","editLegendSymbols","editLegendBorder","createGuides","removeLegend","createTitle","editTitle","removeTitle","createCoordinate","editCoordinate","createScale","editScale","editXScale","editYScale","editXOffsetScale","editYOffsetScale","editParallelScale","editThetaScale","editRScale","editColorScale","editStrokeScale","editSizeScale","editOpacityScale","editShapeScale","editStrokeWidthScale","editStrokeDashScale","createDerivedData","createRegressionBand","editRegressionBand","createRegressionLine","editRegressionLine","filterMarks","removeMarkFilter","selectMarks","editMarkSelection","removeMarkHighlight","removeMarkSelection","highlightMarks","editSemantic","createGraphics","editGraphics","createArcMark","editArcMark","createThetaAxis","createRadialAxis","editThetaAxis","editRadialAxis","removeThetaAxis","removeRadialAxis","createThetaAxisLine","editThetaAxisLine","createRadialAxisLine","editRadialAxisLine","createThetaAxisTicks","editThetaAxisTicks","createRadialAxisTicks","editRadialAxisTicks","createThetaAxisLabels","editThetaAxisLabels","createRadialAxisLabels","editRadialAxisLabels","createThetaAxisTitle","editThetaAxisTitle","createRadialAxisTitle","editRadialAxisTitle","createThetaGrid","createRadialGrid","editThetaGrid","editRadialGrid","editCompositionLayout","replaceCompositionChild","insertCompositionChild","removeCompositionChild","reorderCompositionChildren","facet","facetGrid","repeatCharts","editFacetSource","editFacetHeaders","editFacetScales","editFacetGuides","createScatterPlot","createTextPlot","createDotPlot","createLollipopPlot","createDumbbellPlot","editEndpointPlot","createECDFPlot","editECDFPlot","createIntervalPlot","createRegressionPlot","createLinePlot","createPolarScatterPlot","createPolarLinePlot","createRadarPlot","editRadarPlot","createRugPlot","createStripPlot","createBeeswarmPlot","createRaincloudPlot","editRaincloudPlot","createBarPlot","createHistogram","createHeatmap","createParallelCoordinates","createRectMark","editRectMark","createPiePlot","createRosePlot","createRadialBarPlot","createDensityPlot","createHorizonPlot","createAreaPlot","layoutSeries"]);
|
|
266
|
+
// Generated default action methods: end
|
package/src/grammar/aggregate.js
CHANGED
|
@@ -297,6 +297,7 @@ export function aggregateRows(rows, field, operation) {
|
|
|
297
297
|
|
|
298
298
|
export function formatAggregateTitle(operation, field) {
|
|
299
299
|
const aggregate = validateAggregate(operation);
|
|
300
|
+
if (aggregate === "count" && field === undefined) return "count";
|
|
300
301
|
nonEmptyString(field, "Aggregate field");
|
|
301
302
|
if (isScalarAggregate(aggregate)) return `${aggregate}(${field})`;
|
|
302
303
|
if (aggregate.op === "quantile") {
|
package/src/grammar/arcs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{validateOptionObject as P,validateNonNegativeFinite as j}from"../core/validation.js";import{cloneAndFreeze as R,isPlainObject as
|
|
1
|
+
import{validateOptionObject as P,validateNonNegativeFinite as j}from"../core/validation.js";import{cloneAndFreeze as R,isPlainObject as S}from"../core/immutable.js";import{mapContinuousScaleValues as q,mapOrdinalPositionValues as M,readNominalField as b,readQuantitativeField as I,readScaleField as B}from"./scales/index.js";import{interpolateNumber as U,normalizedFiniteSum as _,stableFiniteSum as y}from"./numeric.js";function k(e){if(e?.mark?.type!=="arc")throw new Error("Arc derivation requires a semantic arc mark.");const t=e.encoding?.theta;if(t===void 0||!["quantitative","nominal","ordinal"].includes(t.fieldType))throw new Error(`Arc mark "${e.id}" requires quantitative or categorical theta.`);return t}function G(e,t){if(e?.type!=="linear"||!Array.isArray(e.range)||e.range.length!==2||!e.range.every(Number.isFinite)||e.range[0]===e.range[1])throw new Error(`${t} requires a resolved linear scale.`);return e}function L(e,t){if(e?.type!=="band"||!Array.isArray(e.domain)||!Array.isArray(e.range)||e.range.length!==2||!e.range.every(Number.isFinite)||e.range[0]===e.range[1]||!Number.isFinite(e.bandwidth)||e.bandwidth<=0)throw new Error(`${t} requires a resolved band scale.`);return e}function E(e,t){return t===void 0?e.map(()=>{}):B(e,t.field,t.fieldType??"nominal",{temporalUnit:t.temporalUnit})}function ne(e,t){const r=E(e,t.encoding?.color),n=t.encoding?.theta;if(!(n?.aggregate!==void 0||t.encoding?.radius?.aggregate!==void 0)||n?.field===void 0)return r;const s=b(e,n.field),o=new Map;return s.forEach((i,a)=>{const u=r[a];if(o.has(i)&&!Object.is(o.get(i),u))throw new Error(`Arc category "${i}" must resolve to one color value.`);o.set(i,u)}),[...o.values()]}function F(e,t,r,n){if(!Array.isArray(e))throw new TypeError(`Arc theta ${n}s require rows.`);if(typeof t!="string"||t.length===0)throw new TypeError(`Arc theta ${n} must be a non-empty string.`);const c=e.map((s,o)=>{const i=s?.[t];if(!Number.isFinite(i)||i<0)throw new TypeError(`Arc theta ${n} field "${t}" must contain non-negative finite numbers at row ${o}.`);return i});if(c.every(s=>s===0))throw new Error(`Arc mark "${r}" theta ${n}s must have a positive total.`);return R(c)}function N(e,t,r="arc"){return F(e,t,r,"weight")}function V(e,t,r="arc"){return F(e,t,r,"value")}function Q(e,t,r){const n=e.reduce((u,d)=>u+d,0),c=Number.isFinite(n)?void 0:_(e,"Arc theta values");if(n===0||c?.total<=0)throw new Error("Arc theta values must have a positive total.");const s=t[1]-t[0];let o=t[0],i=0,a=0;return e.map((u,d)=>{const h=c===void 0?u:u/c.scale,v=i+h;c!==void 0&&(a+=Math.abs(i)>=Math.abs(h)?i-v+h:h-v+i),i=v;const m=o+u/n*s,f=d===e.length-1?t[1]:c===void 0&&Number.isFinite(m)?m:U(t[0],t[1],(i+a)/(c?.total??n));if(!Number.isFinite(f)||f===o)throw new RangeError(`Arc theta ${r(d)} cannot resolve a distinct finite sector.`);const p={startTheta:o,endTheta:f};return o=f,p})}function W(e,t,r){if(!Number.isFinite(t?.availableRadius)||t.availableRadius<0)throw new RangeError(`Arc mark "${e.id}" requires a finite available radius.`);return{innerRadius:X(r,t.availableRadius),outerRadius:t.availableRadius}}function D(e,t,r,n,c){const s=t.encoding.theta,o=s.fieldType==="quantitative";if(!o&&!["count","sum"].includes(s.aggregate))throw new Error(`Arc mark "${t.id}" requires count, sum, or radial layout.`);const i=o?V(e,s.field,t.id):s.aggregate==="count"?e.map(()=>1):N(e,s.weight,t.id),a=o?e.map((g,w)=>w):b(e,s.field),u=E(e,t.encoding?.color),d=new Map(z(a,i).map(g=>[g.key,g])),h=o?a:r.domain,v=new Set(h);for(const g of d.keys())if(!v.has(g))throw new Error(`Arc theta value "${g}" is outside the scale domain.`);const m=h.filter(g=>d.get(g)?.values.some(w=>w>0)),f=g=>o?`row ${m[g]}`:`group "${String(m[g])}"`,p=m.map((g,w)=>o?i[g]:y(d.get(g).values,`Arc theta ${f(w)} aggregate`)),l=Q(p,r.range,f),A=W(t,n,c);return m.map((g,w)=>{const $=d.get(g),T=[...new Set($.sourceIndices.map(O=>u[O]))];if(T.length>1)throw new Error(`Aggregated arc theta group "${g}" must resolve to one color value.`);return{key:o?`${String(i[g])}:${g}`:g,theta:o?i[g]:g,count:$.sourceIndices.length,aggregateValue:p[w],color:T[0],...l[w],...A,sourceIndices:$.sourceIndices}})}function x(e,t,r){const n=t.aggregate==="count"?e.map(()=>1):I(e,t.field);return z(r,n)}function z(e,t){const r=new Map;for(const[n,c]of e.entries()){const s=r.get(c)??{key:c,sourceIndices:[],values:[]};s.sourceIndices.push(n),s.values.push(t[n]),r.set(c,s)}return[...r.values()]}function H(e,t){const r=k(t),n=t.encoding?.radius;if(!["nominal","ordinal"].includes(r.fieldType)||r.aggregate!==void 0||!["count","sum"].includes(n?.aggregate)||n.fieldType!=="quantitative")throw new Error("Measured Arc requires equal-angle categorical theta and count or sum radius.");if(n.aggregate==="count"&&Object.hasOwn(n,"field"))throw new Error("Measured count radius does not accept a field.");const c=b(e,r.field),s=E(e,t.encoding?.color),o=x(e,n,c);if(o.some(a=>a.values.some(u=>u<0)))throw new RangeError("Measured Arc radius inputs must be non-negative.");const i=o.map(a=>{const u=[...new Set(a.sourceIndices.map(d=>s[d]))];if(u.length>1)throw new Error(`Measured Arc category "${a.key}" must resolve to one color value.`);return{key:a.key,theta:a.key,count:a.sourceIndices.length,radius:y(a.values,"Measured Arc radius aggregate"),color:u[0],sourceIndices:a.sourceIndices}});if(!i.some(a=>a.radius>0))throw new Error("Measured Arc radius requires at least one positive category aggregate.");return R(i)}function J(e,t,r,n){const c=H(e,t),s=M(c.map(u=>u.theta),r),o=q(c.map(u=>u.radius),n),i=(Math.sign(r.step)||1)*r.bandwidth/2,a=new Map(c.map((u,d)=>[u.theta,{...u,startTheta:s[d]-i,endTheta:s[d]+i,innerRadius:n.range[0],outerRadius:o[d]}]));return r.domain.flatMap(u=>{const d=a.get(u);return d===void 0||d.radius===0?[]:[d]})}function K(e,t,r,n){const c=t.encoding.theta,s=t.encoding?.radius;if(s?.fieldType!=="quantitative")throw new Error(`Arc mark "${t.id}" requires quantitative radius.`);const o=b(e,c.field),i=I(e,s.field),a=E(e,t.encoding?.color),u=M(o,r),d=q(i,n),h=Math.min(...n.range),m=(Math.sign(r.step)||1)*r.bandwidth/2,f=new Map(r.domain.map(l=>[l,[]]));for(let l=0;l<e.length;l+=1){if(d[l]<=h)continue;const A=f.get(o[l]);if(A===void 0)throw new Error(`Arc theta value "${o[l]}" is outside the scale domain.`);A.push({key:`${String(o[l])}:${l}`,theta:o[l],radius:i[l],color:a[l],startTheta:u[l]-m,endTheta:u[l]+m,innerRadius:h,outerRadius:d[l],sourceIndices:[l]})}const p=[];for(const l of r.domain){const A=f.get(l);A.sort((g,w)=>w.outerRadius-g.outerRadius||g.sourceIndices[0]-w.sourceIndices[0]),p.push(...A)}return p}function ie(e,t,{thetaScale:r,radiusScale:n,frame:c,innerRadius:s=0}={}){if(!Array.isArray(e))throw new TypeError("Arc derivation requires rows.");const o=k(t);C(s);const i=n?.radialMapping!==void 0,a=o.fieldType==="quantitative",u=(a&&!i?G:L)(r,`Arc mark "${t.id}" theta`);let d=i?J(e,t,u,n):a||["count","sum"].includes(o.aggregate)?D(e,t,u,c,s):K(e,t,u,n);if(n!==void 0&&!i&&(a||o.aggregate!==void 0)){const h=Y(e,t),v=q(h.map(f=>f.radius),n),m=new Map(h.map((f,p)=>[f.sourceIndices[0],{radius:f.radius,outerRadius:v[p]}]));d=d.map(f=>{const p=m.get(f.sourceIndices[0]);if(p===void 0||!Number.isFinite(p.outerRadius)||p.outerRadius<=f.innerRadius)throw new RangeError("Arc outer radius must exceed inner radius.");return{...f,...p}})}return R({sectors:d})}function C(e){if(S(e)){if(P(e,["unit","value"],"Arc innerRadius"),e.unit!=="px")throw new RangeError("Arc innerRadius unit must be px.");return R({unit:"px",value:j(e.value,"Arc innerRadius")})}if(!Number.isFinite(e)||e<0||e>=1)throw new RangeError("Arc innerRadius must be from 0 (inclusive) to 1 (exclusive).");return e}function X(e,t){const r=C(e);if(typeof r=="number")return r*t;if(r.value>=t)throw new RangeError("Arc pixel innerRadius must be smaller than its outer radius.");return r.value}function Y(e,t){const r=t.encoding?.theta,n=t.encoding?.radius;if(r===void 0)return[];const c=r.fieldType==="quantitative";if(c?n.aggregate!==void 0:!["count","sum"].includes(r.aggregate))throw new Error("Grouped arc radius requires aggregated categorical theta.");const s=c?V(e,r.field,t.id):r.aggregate==="count"?e.map(()=>1):N(e,r.weight,t.id),o=c?e.map((i,a)=>a):b(e,r.field);return x(e,n,o).filter(i=>i.sourceIndices.some(a=>s[a]>0)).map(({sourceIndices:i,values:a})=>{if(n.aggregate===void 0&&new Set(a).size!==1)throw new Error("Arc radius requires one value per theta group or explicit aggregation.");return{sourceIndices:i,radius:n.aggregate===void 0?a[0]:y(a,"Arc radius aggregate")}})}export{ie as deriveArcSectors,H as deriveMeasuredArcValues,Y as deriveProportionalArcRadiusValues,C as normalizeArcInnerRadius,ne as readArcColorValues,V as readArcThetaValues,N as readArcThetaWeights,X as resolveArcInnerRadius};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cloneAndFreeze as
|
|
1
|
+
import{cloneAndFreeze as z}from"../core/immutable.js";import{readNominalField as B,readQuantitativeField as V,readTemporalField as P}from"./scales/index.js";import{layoutSeriesPartition as F,validateColorLayout as j}from"./seriesLayout.js";import{stableOrderPathValues as J}from"./pathOrder.js";import{deriveCategoricalDensitySeries as K}from"./categoricalDensity.js";import{validatePathSeriesAppearance as G}from"./pathSeries.js";import{readAreaEndpoint as N,validateAreaEndpointPair as Q}from"./areaEndpoints.js";import{validateGeneratedItemLimit as R}from"../core/validation.js";function U(n){const e=[];for(const a of n)e[a.index]=a;return e}function re(n,e,a={}){if(e?.mark?.type!=="area")throw new Error("Area series derivation requires a semantic area mark.");const{x:p,y:u,x2:l,y2:k}=e.encoding??{},o=["quantitative","temporal","nominal","ordinal"].includes(p?.fieldType)&&u?.fieldType==="quantitative"&&k?.fieldType==="quantitative"&&l===void 0,y=["quantitative","temporal","nominal","ordinal"].includes(u?.fieldType)&&p?.fieldType==="quantitative"&&l?.fieldType==="quantitative"&&k===void 0;if(o===y)throw new Error(`Area mark "${e.id}" requires exactly one quantitative x/x2 or y/y2 range and one quantitative, temporal, or categorical independent position.`);const v=G(n,e),c=o?"vertical":"horizontal",f=o?p:u,x=["nominal","ordinal"].includes(f.fieldType),m=x?B(n,f.field):f.fieldType==="temporal"?P(n,f.field,f.temporalUnit):V(n,f.field);if(Object.hasOwn(f,"datum"))throw new Error("Area independent position requires a field.");const w=o?u:p,i=o?k:l;Q(w,i);const r=e.mark.missing??"error",g=N(n,w,r),O=N(n,i,r),s=x?a[o?"xDomain":"yDomain"]??[...new Set(m)]:void 0,t=new Map(s?.map((d,h)=>[d,h]));if(x&&m.some(d=>!t.has(d)))throw new Error("Area categorical domain must contain every observed position.");const E=e.encoding?.pathOrder,$=E===void 0?void 0:V(n,E.field),D=new Map;for(let d=0;d<n.length;d+=1){const h=v.map(q=>n[d][q]),M=JSON.stringify(h),T=D.get(M)??{key:Object.fromEntries(v.map((q,C)=>[q,h[C]])),values:[]};T.values.push(o?{x:m[d],y:g[d],y2:O[d],...r==="break"?{sourceIndex:d}:{},...$===void 0?{}:{pathOrder:$[d]}}:{x:g[d],x2:O[d],...r==="break"?{sourceIndex:d}:{},y:m[d],...$===void 0?{}:{pathOrder:$[d]}}),D.set(M,T)}if(D.size===0)throw new Error(`Area mark "${e.id}" has no values.`);_(D,{layer:e,vertical:o,primary:w,secondary:i});const A=[...D.values()].flatMap(d=>{const h=o?"x":"y",M=E===void 0?d.values.sort((T,q)=>x?t.get(T[h])-t.get(q[h]):T[h]-q[h]):J(d.values.map(({pathOrder:T,...q})=>q),d.values.map(T=>T.pathOrder),E.order);if(r==="break"){const T=[];let q=[];const C=()=>{q.length>=2&&T.push({key:d.key,sourceIndices:q.map(S=>S.sourceIndex),values:q.map(({sourceIndex:S,...L})=>L)}),q=[]};for(const S of M)(o?S.y==null||S.y2==null:S.x==null||S.x2==null)?C():q.push(S);return C(),T}if(M.length<2)throw new Error(`Area series on mark "${e.id}" requires at least two points.`);return{key:d.key,values:M}}),b=A.length===0&&r==="break",I=[...g,...O].filter(Number.isFinite);return z({orientation:c,xValues:b?o?m:I:A.flatMap(d=>d.values.flatMap(h=>o?[h.x]:[h.x,h.x2])),yValues:b?o?I:m:A.flatMap(d=>d.values.flatMap(h=>o?[h.y,h.y2]:[h.y])),series:A})}function ie(n,e,a={}){if(e?.mark?.type!=="area")throw new Error("Centered area series derivation requires an area mark.");const{x:p,y:u,x2:l,y2:k,group:o,color:y}=e.encoding??{};if(!["quantitative","temporal","nominal","ordinal"].includes(p?.fieldType)||u?.fieldType!=="quantitative"||u?.stack!=="center"||l!==void 0||k!==void 0)throw new Error(`Centered area mark "${e.id}" requires one x field and one quantitative y field without ranged endpoints.`);if(o?.fieldType!=="nominal")throw new Error(`Centered area mark "${e.id}" requires a nominal group encoding.`);if(y!==void 0&&y.fieldType!=="quantitative"&&y.field!==o.field)throw new Error(`Centered area color on mark "${e.id}" must match its group field.`);G(n,e);const v=["nominal","ordinal"].includes(p.fieldType),c=v?B(n,p.field):p.fieldType==="temporal"?P(n,p.field,p.temporalUnit):V(n,p.field),f=V(n,u.field),x=B(n,o.field),m=[],w=new Set,i=new Map;for(let s=0;s<n.length;s+=1){const t=x[s];i.has(t)||(m.push(t),i.set(t,new Map));const E=i.get(t);if(E.has(c[s]))throw new Error(`Centered area mark "${e.id}" has duplicate ${o.field}/${p.field} rows.`);E.set(c[s],f[s]),w.add(c[s])}const r=v?(a.xDomain??[...w]).filter(s=>w.has(s)):[...w].sort((s,t)=>s-t);if(r.length!==w.size)throw new Error("Area categorical domain must contain every observed position.");if(m.length===0||r.length<2)throw new Error(`Centered area mark "${e.id}" requires at least two aligned positions.`);for(const[s,t]of i)if(t.size!==r.length||r.some(E=>!t.has(E)))throw new Error(`Centered area series "${String(s)}" requires one aligned value at every x position.`);const g=m.map(()=>[]);for(const s of r){const t=m.map(A=>i.get(A).get(s)),E=F(t,"center"),$=U(E);let D=E[0]?.start??0;for(let A=0;A<m.length;A+=1){const b=$[A];g[A].push({x:s,y:t[A],lower:b?.start??D,upper:b?.end??D}),b!==void 0&&(D=b.end)}}const O=m.map((s,t)=>({key:{[o.field]:s,...y===void 0||y.fieldType==="quantitative"?{}:{[y.field]:s}},values:g[t]}));return z({mode:"y-center",orientation:"vertical",xValues:r,yValues:O.flatMap(s=>s.values.flatMap(t=>[t.lower,t.upper])),series:O})}function oe(n,e,a){if(e?.mark?.type!=="area")throw new Error("Density area derivation requires a semantic area mark.");if(e.encoding?.pathOrder!==void 0)throw new Error(`Density area mark "${e.id}" does not support path order.`);if(a?.type!=="density"||!Array.isArray(a.as))throw new Error(`Area mark "${e.id}" requires density provenance.`);if(a.placement?.type==="category")return K(n,e,a);const{x:p,y:u,group:l}=e.encoding??{};if(p?.fieldType!=="quantitative"||u?.fieldType!=="quantitative")throw new Error(`Density area mark "${e.id}" requires quantitative x and y encodings.`);const[k,o]=a.as,y=p.field===k&&u.field===o?"y-density":p.field===o&&u.field===k?"x-density":void 0;if(y===void 0)throw new Error(`Density area mark "${e.id}" must encode its value and density fields.`);if(a.groupBy===void 0){if(l!==void 0)throw new Error(`Ungrouped density area mark "${e.id}" cannot encode group.`)}else if(l?.field!==a.groupBy||l.fieldType!=="nominal")throw new Error(`Density area mark "${e.id}" must group by "${a.groupBy}".`);const v=V(n,p.field),c=V(n,u.field),f=a.groupBy===void 0?n.map(()=>{}):B(n,a.groupBy),x=new Map;for(let i=0;i<n.length;i+=1){const r=f[i],g=x.get(r)??{key:a.groupBy===void 0?{}:{[a.groupBy]:r},values:[]};g.values.push({x:v[i],y:c[i]}),x.set(r,g)}if(x.size===0)throw new Error(`Density area mark "${e.id}" has no values.`);const m=y==="y-density"?"x":"y",w=[...x.values()].map(i=>{const r=i.values.sort((g,O)=>g[m]-O[m]);if(r.length<2)throw new Error(`Density area series on mark "${e.id}" requires at least two points.`);return{key:i.key,values:r}});return z({mode:y,series:w})}function ae(n,e="overlay"){if(j(e),e==="group")throw new Error('Density area series do not support "group" layout.');if(n?.mode!=="y-density"){if(e==="overlay")return n;throw new Error(`Area layout "${e}" currently requires vertical density series.`)}const a=n.series[0]?.values.length??0;if(a===0||n.series.some(u=>u.values.length!==a))throw new Error("Density area layout requires aligned non-empty samples.");const p=n.series.map(()=>[]);for(let u=0;u<a;u+=1){const l=n.series[0].values[u].x,k=n.series.map(c=>{if(c.values[u].x!==l)throw new Error("Density area layout requires one shared sample grid.");return c.values[u].y}),o=F(k,e),y=U(o);let v=e==="center"?o[0]?.start??0:0;for(let c=0;c<n.series.length;c+=1){const f=y[c];p[c].push({x:l,lower:f?.start??v,upper:f?.end??v}),f!==void 0&&["stack","fill","center"].includes(e)&&(v=f.end)}}return z({mode:n.mode,series:n.series.map((u,l)=>({key:u.key,values:p[l]}))})}function _(n,{layer:e,vertical:a,primary:p,secondary:u}){const l=e.layout?.mode??"overlay";if(j(l),l==="overlay")return;if(l==="group"||l==="center"&&!a)throw new Error(`Area layout "${l}" is incompatible with this orientation.`);const k=Object.hasOwn(p,"datum"),o=Object.hasOwn(u,"datum");if(k===o||(k?p.datum:u.datum)!==0)throw new Error("Stacked area requires one value field and one zero datum endpoint.");const y=a?"x":"y",v=a?"y":"x",c=`${v}2`,f=k?c:v,x=[...n.values()],m=x.map(i=>{const r=new Map;for(const g of i.values){if(r.has(g[y]))throw new Error("Area layout requires unique group/position rows.");r.set(g[y],g)}return r}),w=[...new Set(x.flatMap(i=>i.values.map(r=>r[y])))].sort((i,r)=>i-r);if(R(w.length*x.length,"Area layout cell count"),m.some(i=>i.size!==w.length||w.some(r=>!i.has(r))))throw new Error("Area layout requires an aligned row for every group and position.");for(const i of w){const r=m.map(t=>t.get(i));if(r.some(t=>t[f]==null)){for(const t of r)t[v]=null,t[c]=null;continue}const g=F(r.map(t=>t[f]),l),O=new Map(g.map(t=>[t.index,t]));let s=l==="center"?g[0]?.start??0:0;r.forEach((t,E)=>{const $=O.get(E);t[v]=$?.start??(l==="diverging"?0:s),t[c]=$?.end??(l==="diverging"?0:s),$!==void 0&&l!=="diverging"&&(s=$.end)})}}export{re as deriveAreaSeries,ie as deriveCenteredAreaSeries,oe as deriveDensityAreaSeries,ae as layoutDensityAreaSeries,_ as layoutRawAreaGroups};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolveTextLines as
|
|
1
|
+
import{resolveTextLines as l}from"../core/textMetrics.js";function a(e,{title:i=!1}={}){if(Object.hasOwn(e,"align")&&!["left","center","right"].includes(e.align))throw new TypeError("Axis text align must be left, center, or right.");if(i){if(Object.hasOwn(e,"overlap")&&!["error","allow"].includes(e.overlap))throw new TypeError("Axis title overlap must be error or allow.");if(Object.hasOwn(e,"baseline")&&!["top","hanging","middle","alphabetic","ideographic","bottom"].includes(e.baseline))throw new TypeError("Axis title baseline must be a supported text baseline.");for(const t of["offset","alongOffset"])if(Object.hasOwn(e,t)&&!Number.isFinite(e[t]))throw new TypeError(`Axis title ${t} must be a finite number.`)}}function f(e,i){if(!/[\r\n]/u.test(e))return;const t=i.baseline??"middle",n=l(e,{fontSize:i.fontSize,align:i.align??"center",baseline:t,...t==="middle"?{blockAlign:"middle"}:{}});if(["bottom","ideographic"].includes(t)){const o=(n.length-1)*i.fontSize*1.2;return n.map(r=>({...r,y:r.y-o}))}return n.length>1?n:void 0}function b(e){return e?.overlap==="allow"}export{b as axisTitleAllowsLabelOverlap,f as axisTitleTextLines,a as validateAxisTextPlacement};
|
|
@@ -32,7 +32,7 @@ export function resolveBarOrientation(layer) {
|
|
|
32
32
|
const x = layer.encoding?.x;
|
|
33
33
|
const y = layer.encoding?.y;
|
|
34
34
|
if (hasNumericCenters(layer)) return layer.mark.orientation;
|
|
35
|
-
if (x?.bin !== undefined && y?.aggregate
|
|
35
|
+
if (x?.bin !== undefined && isAggregate(y?.aggregate)) {
|
|
36
36
|
return BAR_ORIENTATIONS.vertical;
|
|
37
37
|
}
|
|
38
38
|
if (isBarCategoryEncoding(x) && y?.fieldType === "quantitative" && layer.encoding?.y2?.fieldType === "quantitative") {
|
|
@@ -68,7 +68,7 @@ export function resolveBarGrain(layer) {
|
|
|
68
68
|
|
|
69
69
|
if (
|
|
70
70
|
x?.bin !== undefined &&
|
|
71
|
-
y?.aggregate
|
|
71
|
+
isAggregate(y?.aggregate)
|
|
72
72
|
) {
|
|
73
73
|
return BAR_GRAINS.histogram;
|
|
74
74
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { datasetInputIds } from "../core/datasetReferences.js";
|
|
2
|
+
export { datasetInputReferences, datasetInputIds, rebindTransformDatasetInputs } from "../core/datasetReferences.js";
|
|
3
|
+
|
|
4
|
+
export function orderedDatasetAncestors(datasets, root) {
|
|
5
|
+
const byId = new Map(datasets.map(dataset => [dataset.id, dataset]));
|
|
6
|
+
const visiting = new Set(), visited = new Set(), result = [];
|
|
7
|
+
if (byId.size !== datasets.length) throw new Error("Dataset dependency graph contains duplicate ids.");
|
|
8
|
+
function visit(id) {
|
|
9
|
+
if (visiting.has(id)) throw new Error(`Dataset dependency graph contains a cycle at "${id}".`);
|
|
10
|
+
if (visited.has(id)) return;
|
|
11
|
+
const dataset = byId.get(id);
|
|
12
|
+
if (dataset === undefined) throw new Error(`Dataset dependency graph references missing input "${id}".`);
|
|
13
|
+
visiting.add(id);
|
|
14
|
+
for (const input of datasetInputIds(dataset)) visit(input);
|
|
15
|
+
visiting.delete(id);
|
|
16
|
+
visited.add(id);
|
|
17
|
+
result.push(dataset);
|
|
18
|
+
}
|
|
19
|
+
visit(root);
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// A stable topological extension of the historical primary-tree DFS order.
|
|
24
|
+
// A binary input waits for both changed parents; an ordinary tree keeps its old order.
|
|
25
|
+
export function orderedDatasetDependents(datasets, root, { skip = () => false } = {}) {
|
|
26
|
+
const byId = new Map(datasets.map(dataset => [dataset.id, dataset]));
|
|
27
|
+
if (byId.size !== datasets.length) throw new Error("Dataset dependency graph contains duplicate ids.");
|
|
28
|
+
if (!byId.has(root)) throw new Error(`Dataset dependency graph has no root "${root}".`);
|
|
29
|
+
const children = new Map();
|
|
30
|
+
for (const dataset of datasets) {
|
|
31
|
+
for (const input of datasetInputIds(dataset)) {
|
|
32
|
+
if (!byId.has(input)) throw new Error(`Dataset "${dataset.id}" references missing input "${input}".`);
|
|
33
|
+
const values = children.get(input) ?? [];
|
|
34
|
+
values.push(dataset);
|
|
35
|
+
children.set(input, values);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const rank = new Map(), visiting = new Set(), visited = new Set();
|
|
39
|
+
function visit(id) {
|
|
40
|
+
if (visiting.has(id)) throw new Error(`Dataset dependency graph contains a cycle at "${id}".`);
|
|
41
|
+
if (visited.has(id)) return;
|
|
42
|
+
visiting.add(id);
|
|
43
|
+
for (const dataset of children.get(id) ?? []) {
|
|
44
|
+
if (skip(dataset)) continue;
|
|
45
|
+
if (!rank.has(dataset.id)) rank.set(dataset.id, rank.size);
|
|
46
|
+
visit(dataset.id);
|
|
47
|
+
}
|
|
48
|
+
visiting.delete(id);
|
|
49
|
+
visited.add(id);
|
|
50
|
+
}
|
|
51
|
+
visit(root);
|
|
52
|
+
const remaining = new Map([...rank].map(([id]) => [id,
|
|
53
|
+
datasetInputIds(byId.get(id)).filter(parent => rank.has(parent)).length]));
|
|
54
|
+
const ready = [];
|
|
55
|
+
const less = (a, b) => rank.get(a) < rank.get(b);
|
|
56
|
+
function push(id) {
|
|
57
|
+
ready.push(id);
|
|
58
|
+
let index = ready.length - 1;
|
|
59
|
+
while (index > 0) {
|
|
60
|
+
const parent = Math.floor((index - 1) / 2);
|
|
61
|
+
if (!less(ready[index], ready[parent])) break;
|
|
62
|
+
[ready[index], ready[parent]] = [ready[parent], ready[index]];
|
|
63
|
+
index = parent;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function pop() {
|
|
67
|
+
const first = ready[0], last = ready.pop();
|
|
68
|
+
if (ready.length) {
|
|
69
|
+
ready[0] = last;
|
|
70
|
+
let index = 0;
|
|
71
|
+
for (;;) {
|
|
72
|
+
const left = index * 2 + 1, right = left + 1;
|
|
73
|
+
let next = index;
|
|
74
|
+
if (left < ready.length && less(ready[left], ready[next])) next = left;
|
|
75
|
+
if (right < ready.length && less(ready[right], ready[next])) next = right;
|
|
76
|
+
if (next === index) break;
|
|
77
|
+
[ready[index], ready[next]] = [ready[next], ready[index]];
|
|
78
|
+
index = next;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return first;
|
|
82
|
+
}
|
|
83
|
+
for (const [id, count] of remaining) if (count === 0) push(id);
|
|
84
|
+
const result = [];
|
|
85
|
+
while (ready.length) {
|
|
86
|
+
const id = pop();
|
|
87
|
+
result.push(byId.get(id));
|
|
88
|
+
for (const child of children.get(id) ?? []) {
|
|
89
|
+
if (!remaining.has(child.id)) continue;
|
|
90
|
+
const count = remaining.get(child.id) - 1;
|
|
91
|
+
remaining.set(child.id, count);
|
|
92
|
+
if (count === 0) push(child.id);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (result.length !== rank.size) throw new Error("Dataset dependency graph contains a cycle.");
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
@@ -268,8 +268,33 @@ function generatedField(name, storageType, transform, role, inputs = []) {
|
|
|
268
268
|
};
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
-
export function
|
|
271
|
+
export function datasetSchemaContext(source,transform,ownerId,resolveInput) {
|
|
272
|
+
const context={sourceId:source.id,ownerId,sourceTransform:source.transform?.[0]};
|
|
273
|
+
if(transform.type==="lookup"){
|
|
274
|
+
const input=resolveInput(transform.lookup.data);
|
|
275
|
+
if(input?.schema===undefined)throw new Error(`Lookup dataset "${transform.lookup.data}" requires schema.`);
|
|
276
|
+
assertFieldsAvailable(source.schema,[transform.field],{data:source.id,operation:"Lookup"});
|
|
277
|
+
assertFieldsAvailable(input.schema,[transform.lookup.key,...transform.lookup.fields],{data:input.id,operation:"Lookup"});
|
|
278
|
+
context.lookupSchema=input.schema;context.lookupSourceId=input.id;
|
|
279
|
+
}
|
|
280
|
+
return context;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export function deriveTransformSchema(inputSchema, transform, values, { sourceId, ownerId, sourceTransform,lookupSchema,lookupSourceId } = {}) {
|
|
272
284
|
const input = validateDatasetSchema(inputSchema);
|
|
285
|
+
if(transform.type==="lookup"){
|
|
286
|
+
const right=validateDatasetSchema(lookupSchema),fields=input.fields.map(field=>({...field}));
|
|
287
|
+
for(let i=0;i<transform.lookup.fields.length;i++){
|
|
288
|
+
const field=transform.lookup.fields[i],name=transform.lookup.as[i];
|
|
289
|
+
if(fields.some(f=>f.name===name))throw new Error(`Lookup output field "${name}" already exists in primary data.`);
|
|
290
|
+
const inherited=right.fields.find(f=>f.name===field);
|
|
291
|
+
const references=[{data:sourceId,field:transform.field},{data:lookupSourceId,field:transform.lookup.key},{data:lookupSourceId,field}];
|
|
292
|
+
fields.push({name,storageType:inherited?.storageType??"unknown",nullable:true,optional:false,lineage:{
|
|
293
|
+
inputs:[...new Map(references.map(r=>[JSON.stringify(r),r])).values()],owner:ownerId,role:`lookup:${field}`
|
|
294
|
+
}});
|
|
295
|
+
}
|
|
296
|
+
return cloneAndFreeze({version:1,completeness:input.completeness==="known"||fields.length>0?"known":"unknown",origin:"derived",fields});
|
|
297
|
+
}
|
|
273
298
|
const definition = { ...transform, __sourceId: sourceId, __ownerId: ownerId };
|
|
274
299
|
const retain = ["filter", "markFilter", "sort", "computed", "timeUnit", "window", "normalize", "stack", "impute", "complete"];
|
|
275
300
|
let fields = retain.includes(transform.type) ? input.fields.map(field => ({ ...field })) : [];
|
|
@@ -354,6 +379,11 @@ export function deriveTransformSchema(inputSchema, transform, values, { sourceId
|
|
|
354
379
|
for (const [role, name] of Object.entries(transform.as ?? {})) {
|
|
355
380
|
push(generatedField(name, "number", definition, role, [transform.field]));
|
|
356
381
|
}
|
|
382
|
+
} else if (transform.type === "flatten") {
|
|
383
|
+
fields = input.fields.map(field => ({ ...field }));
|
|
384
|
+
transform.fields.forEach((field, index) => {
|
|
385
|
+
push(generatedField(transform.as[index], "unknown", definition, `value:${field}`, [field]));
|
|
386
|
+
});
|
|
357
387
|
} else if (transform.type === "fold") {
|
|
358
388
|
fields = input.fields.filter(field => !transform.fields.includes(field.name)).map(field => ({ ...field }));
|
|
359
389
|
push(generatedField(transform.as.key, "string", definition, "key", transform.fields));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{validateOptionObject as o,validateNonEmptyString as r,validatePositiveFinite as a,validateNonNegativeFinite as c}from"../core/validation.js";import{resolveRotation as s}from"./rotation.js";const f=Object.freeze(["text","fontSize","fontFamily","fontWeight","color","offset","align","rotation","alongOffset"]);function d(e){if(o(e,f,"Facet header title",{allowEmpty:!1}),r(e.text,"Facet header title text"),/[\r\n\u2028\u2029]/u.test(e.text))throw new TypeError("Facet header title text must be one line.");e.fontSize!==void 0&&a(e.fontSize,"Facet header title fontSize");for(const t of["fontFamily","color"])e[t]!==void 0&&r(e[t],`Facet header title ${t}`);if(e.fontWeight!==void 0&&!(typeof e.fontWeight=="string"&&e.fontWeight.length>0||Number.isFinite(e.fontWeight)))throw new TypeError("Facet header title fontWeight must be a non-empty string or finite number.");if(e.offset!==void 0&&c(e.offset,"Facet header title offset"),e.align!==void 0&&!["start","center","end"].includes(e.align))throw new TypeError("Facet header title align must be start, center, or end.");for(const t of["rotation","alongOffset"])if(Object.hasOwn(e,t)&&!Number.isFinite(e[t]))throw new TypeError(`Facet header title ${t} must contain finite normalized radians or coordinates.`)}function h(e,t){if(t===!1)return;o(t,f,"Facet header title",{allowEmpty:!1});for(const i of Object.keys(t))if(t[i]===void 0)throw new TypeError(`Facet header title ${i} must not be undefined.`);const n={...e,...t};return Object.hasOwn(t,"rotation")&&(n.rotation=s(t.rotation,"Facet header title rotation")),d(n),n}function w(e){for(const t of e.common===void 0?[e]:[e.common,e.row,e.column]){if(t!==void 0&&Object.hasOwn(t,"anchor")&&!["plot","content"].includes(t.anchor))throw new TypeError("Facet header anchor must be plot or content.");if(t?.rotation!==void 0&&!Number.isFinite(t.rotation))throw new TypeError("Facet header rotation must contain finite normalized radians.");t?.title!==void 0&&d(t.title)}if(e.title!==void 0||e.common?.title!==void 0)throw new TypeError("Facet header title requires an explicit row or column role.");if(e.mode!=="roles"&&(e.row?.title!==void 0||e.column?.title!==void 0))throw new TypeError("Facet field titles require role header mode.")}export{h as normalizeFacetHeaderTitle,w as validateFacetHeaderRotations,d as validateFacetHeaderTitle};
|
|
@@ -227,9 +227,13 @@ function resolveAnchor(paths, datasets, classifications, requested) {
|
|
|
227
227
|
if (!datasets.has(id)) {
|
|
228
228
|
throw new Error(`Facet partition dataset "${id}" does not exist.`);
|
|
229
229
|
}
|
|
230
|
-
|
|
230
|
+
const explicitExpansion = ["fold", "flatten"].includes(
|
|
231
|
+
datasets.get(id).value.transform?.[0]?.type
|
|
232
|
+
) &&
|
|
233
|
+
paths.every(path => path.includes(id));
|
|
234
|
+
if (!common.includes(id) && !explicitExpansion) {
|
|
231
235
|
throw new Error(
|
|
232
|
-
`Facet partition dataset "${id}" is not a common row-preserving ancestor of every layer.`
|
|
236
|
+
`Facet partition dataset "${id}" is not a common row-preserving ancestor or materialized fold boundary or aligned-array boundary of every layer.`
|
|
233
237
|
);
|
|
234
238
|
}
|
|
235
239
|
return id;
|