ggaction 0.0.13 → 0.0.16
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 +146 -0
- package/README.md +41 -12
- package/knowledge/action-cards.json +1 -1
- package/knowledge/intent-taxonomy.json +1 -1
- package/knowledge/mcp-resources.json +1 -1
- package/knowledge/task-packet.schema.json +1 -1
- package/knowledge/task-resolver.js +1 -1
- package/package.json +39 -7
- package/src/BasicChartProgram.js +3 -1
- package/src/ChartProgram.js +2 -0
- package/src/accessibility.js +157 -0
- package/src/actions/basic.js +2 -2
- package/src/actions/boxPlots/components.js +20 -12
- package/src/actions/boxPlots/create.js +4 -5
- package/src/actions/boxPlots/edit.js +34 -9
- package/src/actions/boxPlots/materialize.js +12 -4
- package/src/actions/boxPlots/options.js +48 -16
- package/src/actions/canvas/actions.js +3 -3
- package/src/actions/canvas/fitting.js +1 -1
- package/src/actions/categoryOrder/index.js +21 -12
- package/src/actions/charts/area.js +6 -2
- package/src/actions/charts/bar.js +3 -2
- package/src/actions/charts/beeswarm.js +1 -1
- package/src/actions/charts/density.js +7 -3
- package/src/actions/charts/ecdf.js +6 -3
- package/src/actions/charts/endpoints.js +29 -7
- package/src/actions/charts/heatmap.js +3 -2
- package/src/actions/charts/histogram.js +5 -4
- package/src/actions/charts/horizon.js +4 -2
- package/src/actions/charts/interval-regression.js +7 -4
- package/src/actions/charts/line.js +4 -2
- package/src/actions/charts/parallel.js +9 -3
- package/src/actions/charts/pie.js +11 -3
- package/src/actions/charts/polar.js +17 -6
- package/src/actions/charts/radar.js +9 -3
- package/src/actions/charts/radial.js +12 -4
- package/src/actions/charts/raincloud.js +2 -2
- package/src/actions/charts/rug-strip.js +8 -4
- package/src/actions/charts/scatter.js +4 -2
- package/src/actions/composition/actions.js +29 -24
- package/src/actions/coordinates/actions.js +2 -2
- package/src/actions/coordinates/edit.js +76 -0
- package/src/actions/data/basicBin2d.js +4 -5
- package/src/actions/data/bin.js +3 -3
- package/src/actions/data/bin2d.js +63 -193
- package/src/actions/data/bin2dMaterialize.js +4 -5
- package/src/actions/data/box.js +4 -4
- package/src/actions/data/complete.js +26 -0
- package/src/actions/data/computed.js +4 -4
- package/src/actions/data/create.js +7 -14
- package/src/actions/data/density.js +15 -23
- package/src/actions/data/derived.js +57 -31
- package/src/actions/data/ecdf.js +2 -2
- package/src/actions/data/edit.js +551 -0
- package/src/actions/data/filter.js +28 -22
- package/src/actions/data/fold.js +2 -2
- package/src/actions/data/gradientProfile.js +2 -2
- package/src/actions/data/horizon.js +2 -2
- package/src/actions/data/impute.js +26 -0
- package/src/actions/data/index.js +62 -47
- package/src/actions/data/interval.js +2 -2
- package/src/actions/data/intervalEdit.js +26 -18
- package/src/actions/data/normalize.js +27 -0
- package/src/actions/data/regression.js +21 -27
- package/src/actions/data/revise.js +48 -0
- package/src/actions/data/revisionBindings.js +125 -0
- package/src/actions/data/shared.js +20 -4
- package/src/actions/data/stack.js +2 -2
- package/src/actions/data/summary.js +3 -3
- package/src/actions/data/timeUnit.js +6 -3
- package/src/actions/data/window.js +3 -3
- package/src/actions/encodings/angle.js +1 -1
- package/src/actions/encodings/appearance.js +4 -4
- package/src/actions/encodings/barWidth.js +1 -1
- package/src/actions/encodings/channels.js +415 -0
- package/src/actions/encodings/color/index.js +3 -2
- package/src/actions/encodings/density.js +7 -4
- package/src/actions/encodings/histogram.js +4 -3
- package/src/actions/encodings/horizon.js +6 -8
- package/src/actions/encodings/index.js +4 -0
- package/src/actions/encodings/offset.js +22 -15
- package/src/actions/encodings/parallel.js +4 -5
- package/src/actions/encodings/pathOrder.js +6 -11
- package/src/actions/encodings/position/apply.js +53 -7
- package/src/actions/encodings/position/index.js +14 -6
- package/src/actions/encodings/position/policies/bar.js +9 -0
- package/src/actions/encodings/position/resolve.js +24 -7
- package/src/actions/encodings/ranged.js +5 -5
- package/src/actions/encodings/remove.js +13 -9
- package/src/actions/encodings/ruleAppearance.js +2 -24
- package/src/actions/encodings/seriesLayout.js +2 -1
- package/src/actions/encodings/shared.js +82 -14
- package/src/actions/encodings/stroke.js +226 -0
- package/src/actions/encodings/strokeDash.js +2 -2
- package/src/actions/encodings/text.js +1 -1
- package/src/actions/errorBands/create.js +34 -20
- package/src/actions/errorBands/edit.js +34 -137
- package/src/actions/errorBars/create.js +43 -20
- package/src/actions/errorBars/edit.js +29 -125
- package/src/actions/facets/actions.js +267 -73
- package/src/actions/facets/derive.js +99 -28
- package/src/actions/facets/guides.js +4 -5
- package/src/actions/facets/replay.js +4 -5
- package/src/actions/gradientPlots/components.js +15 -8
- package/src/actions/gradientPlots/create.js +4 -5
- package/src/actions/gradientPlots/edit.js +18 -9
- package/src/actions/gradientPlots/materialize.js +5 -3
- package/src/actions/gradientPlots/options.js +10 -2
- package/src/actions/gradientPlots/rebind.js +4 -5
- package/src/actions/guides/applicability.js +8 -2
- package/src/actions/guides/axes/axes.js +23 -4
- package/src/actions/guides/axes/axis.js +2 -2
- package/src/actions/guides/axes/edit.js +2 -2
- package/src/actions/guides/axes/labels.js +67 -21
- package/src/actions/guides/axes/lines.js +15 -9
- package/src/actions/guides/axes/parallel/lifecycle.js +13 -13
- package/src/actions/guides/axes/parallel/resolve.js +6 -2
- package/src/actions/guides/axes/parallel.js +5 -5
- package/src/actions/guides/axes/remove.js +4 -4
- package/src/actions/guides/axes/tickGroups.js +6 -5
- package/src/actions/guides/axes/ticks.js +19 -6
- package/src/actions/guides/axes/titles.js +21 -13
- package/src/actions/guides/grids/grid.js +18 -29
- package/src/actions/guides/grids/resolve.js +7 -2
- package/src/actions/guides/guides.js +20 -4
- package/src/actions/guides/legends/blocks.js +426 -0
- package/src/actions/guides/legends/categorical/actions.js +138 -52
- package/src/actions/guides/legends/categorical/components.js +55 -45
- package/src/actions/guides/legends/categorical/index.js +37 -33
- package/src/actions/guides/legends/categorical/layout.js +98 -16
- package/src/actions/guides/legends/categorical/options.js +2 -2
- package/src/actions/guides/legends/categorical/recipes.js +111 -20
- package/src/actions/guides/legends/categorical/resolve.js +12 -8
- package/src/actions/guides/legends/categorical/symbols.js +104 -52
- package/src/actions/guides/legends/continuous/common.js +76 -20
- package/src/actions/guides/legends/continuous/gradient.js +95 -158
- package/src/actions/guides/legends/continuous/index.js +2 -0
- package/src/actions/guides/legends/continuous/interval.js +21 -16
- package/src/actions/guides/legends/continuous/opacity.js +37 -83
- package/src/actions/guides/legends/continuous/stroke.js +225 -0
- package/src/actions/guides/legends/creation.js +14 -1
- package/src/actions/guides/legends/edit.js +159 -60
- package/src/actions/guides/legends/focused.js +5 -5
- package/src/actions/guides/legends/index.js +2 -0
- package/src/actions/guides/legends/lane.js +59 -24
- package/src/actions/guides/legends/lifecycle.js +19 -8
- package/src/actions/guides/legends/remove.js +17 -8
- package/src/actions/guides/legends/sampling.js +140 -0
- package/src/actions/guides/legends/size.js +100 -108
- package/src/actions/guides/legends/strokeWidth.js +38 -92
- package/src/actions/guides/legends/target.js +112 -1
- package/src/actions/guides/legends/transition.js +165 -16
- package/src/actions/guides/polar/axes/facade.js +8 -8
- package/src/actions/guides/polar/axes/labels.js +29 -10
- package/src/actions/guides/polar/axes/lines.js +19 -19
- package/src/actions/guides/polar/axes/shared.js +16 -0
- package/src/actions/guides/polar/axes/ticks.js +10 -16
- package/src/actions/guides/polar/axes/titles.js +6 -6
- package/src/actions/guides/polar/grids.js +12 -14
- package/src/actions/guides/polar/resolve.js +12 -8
- package/src/actions/index.js +16 -3
- package/src/actions/intervals/revision.js +126 -0
- package/src/actions/marks/arc/actions.js +80 -22
- package/src/actions/marks/area/actions.js +38 -13
- package/src/actions/marks/area/materialize.js +17 -1
- package/src/actions/marks/bar/create.js +12 -3
- package/src/actions/marks/bar/edit.js +21 -4
- package/src/actions/marks/bar/materialize.js +34 -40
- package/src/actions/marks/index.js +10 -1
- package/src/actions/marks/line/actions.js +40 -17
- package/src/actions/marks/line/materialize.js +29 -10
- package/src/actions/marks/point/create.js +9 -3
- package/src/actions/marks/point/edit.js +19 -7
- package/src/actions/marks/point/jitter.js +6 -8
- package/src/actions/marks/point/materialize.js +48 -22
- package/src/actions/marks/point/packing.js +6 -8
- package/src/actions/marks/rect/actions.js +36 -15
- package/src/actions/marks/references.js +602 -33
- package/src/actions/marks/remove.js +67 -41
- package/src/actions/marks/rule/actions.js +51 -14
- package/src/actions/marks/shared.js +1 -1
- package/src/actions/marks/text/actions.js +79 -14
- package/src/actions/marks/text/index.js +4 -0
- package/src/actions/marks/text/layout.js +157 -17
- package/src/actions/marks/text/removal.js +173 -0
- package/src/actions/marks/text/selection.js +72 -0
- package/src/actions/marks/tick/actions.js +25 -8
- package/src/actions/primitives/createGraphics.js +5 -4
- package/src/actions/primitives/editGraphics.js +31 -4
- package/src/actions/primitives/graphicProperties.js +6 -0
- package/src/actions/primitives/semantic.js +11 -3
- package/src/actions/primitives/semanticAction.js +90 -14
- package/src/actions/primitives/semanticValidation/dataset.js +2 -4
- package/src/actions/primitives/semanticValidation/guide.js +1 -1
- package/src/actions/primitives/semanticValidation/index.js +9 -1
- package/src/actions/primitives/semanticValidation/layer.js +5 -0
- package/src/actions/primitives/semanticValidation/scale.js +28 -7
- package/src/actions/regression/components.js +36 -25
- package/src/actions/regression/create.js +13 -6
- package/src/actions/regression/edit.js +24 -11
- package/src/actions/resources/index.js +7 -0
- package/src/actions/resources/remove.js +173 -0
- package/src/actions/scales/channels.js +212 -0
- package/src/actions/scales/consumers/common.js +1 -1
- package/src/actions/scales/consumers/families.js +3 -2
- package/src/actions/scales/consumers/index.js +19 -2
- package/src/actions/scales/consumers/seriesLayout.js +28 -14
- package/src/actions/scales/create.js +55 -23
- package/src/actions/scales/definitions.js +94 -40
- package/src/actions/scales/edit.js +67 -12
- package/src/actions/scales/editPolicy.js +81 -11
- package/src/actions/scales/index.js +6 -0
- package/src/actions/scales/materialize.js +5 -4
- package/src/actions/scales/offset.js +48 -0
- package/src/actions/scales/parallel.js +71 -0
- package/src/actions/scales/preview.js +111 -10
- package/src/actions/scales/quantitativeColor.js +4 -5
- package/src/actions/selection/actions.js +179 -66
- package/src/actions/selection/index.js +3 -0
- package/src/actions/textMetrics/index.js +56 -0
- package/src/actions/theme/actions.js +70 -15
- package/src/actions/theme/composition.js +238 -0
- package/src/actions/theme/index.js +9 -2
- package/src/actions/theme/reconcile.js +313 -121
- package/src/actions/theme/state.js +160 -0
- package/src/actions/titles/actions.js +8 -8
- package/src/actions/titles/resolve.js +3 -3
- package/src/actions/violinPlots/create.js +6 -3
- package/src/actions/violinPlots/edit.js +2 -1
- package/src/core/action.js +117 -35
- package/src/core/compositionState.js +6 -1
- package/src/core/diagnostics.js +16 -0
- package/src/core/extensionRegistry.js +27 -0
- package/src/core/font.js +18 -0
- package/src/core/identifiers.js +5 -4
- package/src/core/immutable.js +10 -3
- package/src/core/optionRequirements.js +8 -0
- package/src/core/programState.js +21 -1
- package/src/core/resourceReferences.js +341 -0
- package/src/core/textMetricProfile.js +28 -0
- package/src/core/textMetrics.js +24 -3
- package/src/core/validation.js +22 -10
- package/src/core/vocabulary.js +16 -3
- package/src/diagnostics.js +1 -0
- package/src/grammar/bars/aggregate.js +4 -1
- package/src/grammar/bars/geometry.js +44 -0
- package/src/grammar/bin.js +54 -9
- package/src/grammar/complete.js +279 -0
- package/src/grammar/computed.js +265 -64
- package/src/grammar/coordinates.js +37 -0
- package/src/grammar/density.js +239 -37
- package/src/grammar/displayLabels.js +57 -0
- package/src/grammar/facets/dependencies.js +133 -1
- package/src/grammar/facets/guides.js +3 -0
- package/src/grammar/facets/index.js +7 -47
- package/src/grammar/facets/scales.js +15 -18
- package/src/grammar/impute.js +341 -0
- package/src/grammar/markLabelSelection.js +57 -0
- package/src/grammar/markLabels.js +18 -1
- package/src/grammar/normalize.js +366 -0
- package/src/grammar/pathSeries.js +26 -8
- package/src/grammar/pointShapes.js +13 -4
- package/src/grammar/polar.js +90 -8
- package/src/grammar/regression/index.js +1 -0
- package/src/grammar/regression/parameters.js +23 -0
- package/src/grammar/roundedRect.js +180 -0
- package/src/grammar/rules.js +1 -0
- package/src/grammar/scales/colorConsumers.js +20 -1
- package/src/grammar/scales/continuous.js +2 -2
- package/src/grammar/scales/definition.js +27 -4
- package/src/grammar/scales/index.js +1 -0
- package/src/grammar/scales/ordinal.js +12 -3
- package/src/grammar/scales/policies.js +2 -2
- package/src/grammar/scales/size.js +456 -0
- package/src/grammar/scales/types.js +8 -0
- package/src/grammar/schemas/concreteGraphic.js +14 -2
- package/src/grammar/schemas/graphic.js +15 -0
- package/src/grammar/schemas/graphicBounds.js +190 -41
- package/src/grammar/schemas/semanticPath.js +7 -2
- package/src/grammar/statisticalReference.js +103 -0
- package/src/grammar/strokeStyle.js +77 -0
- package/src/grammar/summary.js +78 -16
- package/src/grammar/timeUnit.js +216 -5
- package/src/grammar/transformTopology.js +4 -0
- package/src/grammar/transforms.js +394 -28
- package/src/grammar/weightedStatistics.js +374 -0
- package/src/grammar/window.js +221 -16
- package/src/layout/aspect.js +80 -0
- package/src/layout/facets.js +59 -5
- package/src/layout/labels.js +351 -7
- package/src/layout/legend.js +4 -4
- package/src/layout/legendItems.js +6 -6
- package/src/layout/text.js +10 -9
- package/src/layout/title.js +9 -9
- package/src/materialization/bars/aggregate.js +25 -2
- package/src/materialization/bars/histogram.js +84 -24
- package/src/materialization/bars/ranged.js +19 -3
- package/src/materialization/bars/resolve.js +7 -4
- package/src/materialization/composition.js +3 -1
- package/src/materialization/coordinateBounds.js +204 -0
- package/src/materialization/dependencies.js +36 -0
- package/src/materialization/facetGuides/legacyCategorical.js +3 -0
- package/src/materialization/facetGuides/placement.js +9 -3
- package/src/materialization/facetGuides/preparation.js +13 -8
- package/src/materialization/facetHeaders.js +347 -0
- package/src/materialization/facets.js +15 -65
- package/src/materialization/guides/layout.js +3 -3
- package/src/materialization/guides/resources.js +26 -0
- package/src/materialization/legends.js +1 -1
- package/src/materialization/marks/index.js +11 -5
- package/src/materialization/marks/policies.js +2 -1
- package/src/materialization/rect.js +25 -12
- package/src/materialization/rectConfig.js +5 -1
- package/src/materialization/scaleGuideDependencies.js +10 -0
- package/src/materialization/scales/map.js +5 -3
- package/src/materialization/scales/policies/binnedPosition.js +2 -2
- package/src/materialization/scales/policies/offset.js +50 -8
- package/src/materialization/scales/policies/series.js +4 -1
- package/src/materialization/scales/resolve.js +22 -6
- package/src/materialization/selection/items/arc.js +28 -4
- package/src/materialization/selection/items/bar.js +30 -3
- package/src/materialization/selection/items/common.js +20 -5
- package/src/materialization/selection/items/rect.js +28 -4
- package/src/materialization/selection/path.js +1 -1
- package/src/materialization/selection/point.js +3 -1
- package/src/materialization/text.js +265 -14
- package/src/materialization/typography.js +31 -0
- package/src/mcp/cli.js +5 -3
- package/src/persistence/codec.js +65 -0
- package/src/persistence/validation.js +185 -0
- package/src/persistence.js +74 -0
- package/src/renderers/canvas/circle.js +2 -3
- package/src/renderers/canvas/index.js +5 -4
- package/src/renderers/canvas/line.js +2 -3
- package/src/renderers/canvas/native.js +4 -4
- package/src/renderers/canvas/path.js +2 -3
- package/src/renderers/canvas/rect.js +2 -3
- package/src/renderers/canvas/stroke.js +11 -0
- package/src/renderers/nativeBackend.js +10 -0
- package/src/renderers/options.js +13 -0
- package/src/renderers/pdf.js +25 -54
- package/src/renderers/png.js +22 -17
- package/src/renderers/svg.js +29 -14
- package/src/renderers/text.js +1 -11
- package/src/selectors/coordinates.js +3 -1
- package/src/selectors/datasets.js +38 -5
- package/src/selectors/layers.js +10 -4
- package/src/selectors/markOwners.js +46 -0
- package/src/selectors/scales.js +6 -4
- package/src/theme/defaults.js +103 -1
- package/src/version.js +2 -0
- package/types/accessibility.d.ts +37 -0
- package/types/basic.d.ts +10 -2
- package/types/diagnostics.d.ts +15 -0
- package/types/index.d.ts +90 -0
- package/types/pdf.d.ts +9 -0
- package/types/persistence.d.ts +7 -0
- package/types/png.d.ts +9 -0
- package/types/program.d.ts +944 -118
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cloneAndFreeze,
|
|
3
|
+
freezeOwned,
|
|
4
|
+
isPlainObject
|
|
5
|
+
} from "../../core/immutable.js";
|
|
6
|
+
import {
|
|
7
|
+
normalizeThemeDefinition,
|
|
8
|
+
resolveThemeTokens
|
|
9
|
+
} from "../../theme/defaults.js";
|
|
10
|
+
|
|
11
|
+
export const LOCAL_THEME_OWNER = "local";
|
|
12
|
+
|
|
13
|
+
function ownArray(value) {
|
|
14
|
+
return freezeOwned([...(value ?? [])]);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function requireThemeScope(value) {
|
|
18
|
+
if (!["self", "descendants"].includes(value)) {
|
|
19
|
+
throw new Error(`Unsupported theme scope "${value}".`);
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function requireThemeOwner(value) {
|
|
25
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
26
|
+
throw new TypeError("Theme frame owner must be a non-empty string.");
|
|
27
|
+
}
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function compositionThemeOwner(id, scope = "descendants") {
|
|
32
|
+
if (typeof id !== "string" || id.length === 0) {
|
|
33
|
+
throw new TypeError("Composition theme owner requires a non-empty id.");
|
|
34
|
+
}
|
|
35
|
+
requireThemeScope(scope);
|
|
36
|
+
return scope === "self" ? `composition:${id}:self` : `composition:${id}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function createThemeFrame({
|
|
40
|
+
owner = LOCAL_THEME_OWNER,
|
|
41
|
+
scope = "self",
|
|
42
|
+
definition
|
|
43
|
+
}) {
|
|
44
|
+
const normalized = normalizeThemeDefinition(definition);
|
|
45
|
+
return freezeOwned({
|
|
46
|
+
owner: requireThemeOwner(owner),
|
|
47
|
+
scope: requireThemeScope(scope),
|
|
48
|
+
name: normalized.name,
|
|
49
|
+
tokens: normalized.tokens
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function upsertThemeFrame(frames = [], frame) {
|
|
54
|
+
if (!Array.isArray(frames)) {
|
|
55
|
+
throw new TypeError("Theme frames must be an array.");
|
|
56
|
+
}
|
|
57
|
+
if (!isPlainObject(frame)) {
|
|
58
|
+
throw new TypeError("Theme frame must be a plain object.");
|
|
59
|
+
}
|
|
60
|
+
return freezeOwned([
|
|
61
|
+
...frames.filter(current => current.owner !== frame.owner),
|
|
62
|
+
frame
|
|
63
|
+
]);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function removeThemeFrameOwner(frames = [], owner) {
|
|
67
|
+
if (!Array.isArray(frames)) {
|
|
68
|
+
throw new TypeError("Theme frames must be an array.");
|
|
69
|
+
}
|
|
70
|
+
requireThemeOwner(owner);
|
|
71
|
+
const next = frames.filter(frame => frame.owner !== owner);
|
|
72
|
+
return freezeOwned({
|
|
73
|
+
frames: freezeOwned(next),
|
|
74
|
+
removed: next.length !== frames.length
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function moveLocalThemeScope(localOrder = [], scope) {
|
|
79
|
+
if (!Array.isArray(localOrder)) {
|
|
80
|
+
throw new TypeError("Theme localOrder must be an array.");
|
|
81
|
+
}
|
|
82
|
+
requireThemeScope(scope);
|
|
83
|
+
return freezeOwned([
|
|
84
|
+
...localOrder.filter(current => current !== scope),
|
|
85
|
+
scope
|
|
86
|
+
]);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function ownCanonicalThemeState(state, composition) {
|
|
90
|
+
const frames = ownArray(state.frames);
|
|
91
|
+
const localOrder = ownArray(state.localOrder);
|
|
92
|
+
const overrides = ownArray(state.overrides);
|
|
93
|
+
const result = { frames, localOrder, overrides };
|
|
94
|
+
if (composition || state.descendantFrames !== undefined) {
|
|
95
|
+
result.descendantFrames = ownArray(state.descendantFrames);
|
|
96
|
+
}
|
|
97
|
+
return cloneAndFreeze(result);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function normalizeThemeState(state, { composition = false } = {}) {
|
|
101
|
+
if (state === undefined) {
|
|
102
|
+
return freezeOwned({
|
|
103
|
+
frames: freezeOwned([]),
|
|
104
|
+
...(composition ? { descendantFrames: freezeOwned([]) } : {}),
|
|
105
|
+
localOrder: freezeOwned([]),
|
|
106
|
+
overrides: freezeOwned([])
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (!isPlainObject(state)) {
|
|
110
|
+
throw new TypeError("Program theme state must be a plain object.");
|
|
111
|
+
}
|
|
112
|
+
if (Array.isArray(state.frames)) {
|
|
113
|
+
return ownCanonicalThemeState(state, composition);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const name = state.name ?? "light";
|
|
117
|
+
const tokens = state.tokens ?? {};
|
|
118
|
+
const scope = state.scope ?? "self";
|
|
119
|
+
const origin = state.origin?.ownerCompositionId;
|
|
120
|
+
const owner = origin === undefined
|
|
121
|
+
? LOCAL_THEME_OWNER
|
|
122
|
+
: compositionThemeOwner(origin, "descendants");
|
|
123
|
+
const frame = createThemeFrame({
|
|
124
|
+
owner,
|
|
125
|
+
scope,
|
|
126
|
+
definition: { base: name, tokens }
|
|
127
|
+
});
|
|
128
|
+
return freezeOwned({
|
|
129
|
+
frames: freezeOwned([frame]),
|
|
130
|
+
...(composition && scope === "descendants"
|
|
131
|
+
? { descendantFrames: freezeOwned([frame]) }
|
|
132
|
+
: composition
|
|
133
|
+
? { descendantFrames: freezeOwned([]) }
|
|
134
|
+
: {}),
|
|
135
|
+
localOrder: origin === undefined ? freezeOwned([scope]) : freezeOwned([]),
|
|
136
|
+
overrides: cloneAndFreeze(state.overrides ?? [])
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function resolveEffectiveThemeFrame(state) {
|
|
141
|
+
const frames = state?.frames;
|
|
142
|
+
return Array.isArray(frames) ? frames.at(-1) : undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function resolveEffectiveThemeTokens(state) {
|
|
146
|
+
const frame = resolveEffectiveThemeFrame(state);
|
|
147
|
+
return frame === undefined
|
|
148
|
+
? resolveThemeTokens("light")
|
|
149
|
+
: resolveThemeTokens({ base: frame.name, tokens: frame.tokens });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function setThemeStateOverrides(state, overrides) {
|
|
153
|
+
if (!Array.isArray(overrides)) {
|
|
154
|
+
throw new TypeError("Theme overrides must be an array.");
|
|
155
|
+
}
|
|
156
|
+
return freezeOwned({
|
|
157
|
+
...state,
|
|
158
|
+
overrides: cloneAndFreeze([...overrides].sort())
|
|
159
|
+
});
|
|
160
|
+
}
|
|
@@ -115,12 +115,12 @@ function titleTextAction(kind, create) {
|
|
|
115
115
|
}));
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
export const editTitleText = titleTextAction("title", false);
|
|
119
|
-
export const createTitleText = titleTextAction("title", true);
|
|
120
|
-
export const editSubtitleText = titleTextAction("subtitle", false);
|
|
121
|
-
export const createSubtitleText = titleTextAction("subtitle", true);
|
|
118
|
+
export const editTitleText = /* @__PURE__ */ titleTextAction("title", false);
|
|
119
|
+
export const createTitleText = /* @__PURE__ */ titleTextAction("title", true);
|
|
120
|
+
export const editSubtitleText = /* @__PURE__ */ titleTextAction("subtitle", false);
|
|
121
|
+
export const createSubtitleText = /* @__PURE__ */ titleTextAction("subtitle", true);
|
|
122
122
|
|
|
123
|
-
export const rematerializeTitle = action(
|
|
123
|
+
export const rematerializeTitle = /* @__PURE__ */ action(
|
|
124
124
|
{ op: "rematerializeTitle", description: "Rematerialize chart title graphics." },
|
|
125
125
|
withGuideLayoutValidation(function (args = {}) {
|
|
126
126
|
noOptions(args, "rematerializeTitle");
|
|
@@ -142,7 +142,7 @@ export const rematerializeTitle = action(
|
|
|
142
142
|
})
|
|
143
143
|
);
|
|
144
144
|
|
|
145
|
-
export const createTitle = action(
|
|
145
|
+
export const createTitle = /* @__PURE__ */ action(
|
|
146
146
|
{
|
|
147
147
|
op: "createTitle",
|
|
148
148
|
description: "Create a chart title and optional subtitle.",
|
|
@@ -176,7 +176,7 @@ export const createTitle = action(
|
|
|
176
176
|
})
|
|
177
177
|
);
|
|
178
178
|
|
|
179
|
-
export const editTitle = action(
|
|
179
|
+
export const editTitle = /* @__PURE__ */ action(
|
|
180
180
|
{
|
|
181
181
|
op: "editTitle",
|
|
182
182
|
description: "Edit one stable chart title resource.",
|
|
@@ -214,7 +214,7 @@ export const editTitle = action(
|
|
|
214
214
|
})
|
|
215
215
|
);
|
|
216
216
|
|
|
217
|
-
export const removeTitle = action(
|
|
217
|
+
export const removeTitle = /* @__PURE__ */ action(
|
|
218
218
|
{
|
|
219
219
|
op: "removeTitle",
|
|
220
220
|
description: "Remove the complete chart title resource.",
|
|
@@ -212,7 +212,7 @@ export function resolveTitleLayout(program, config) {
|
|
|
212
212
|
program.semanticSpec.title.subtitle,
|
|
213
213
|
"Chart subtitle"
|
|
214
214
|
)
|
|
215
|
-
}, config);
|
|
215
|
+
}, config, program.materializationConfigs.textMetrics);
|
|
216
216
|
const horizontal = ["top", "bottom"].includes(config.position);
|
|
217
217
|
let component;
|
|
218
218
|
if (horizontal) {
|
|
@@ -249,10 +249,10 @@ export function resolveTitleLayout(program, config) {
|
|
|
249
249
|
? undefined
|
|
250
250
|
: component(block.subtitleLines, block.subtitleCenters);
|
|
251
251
|
const titleBounds = unionTitleBounds([
|
|
252
|
-
resolveTitleComponentBounds(title, config.titleStyle),
|
|
252
|
+
resolveTitleComponentBounds(title, config.titleStyle, program.materializationConfigs.textMetrics),
|
|
253
253
|
...(subtitle === undefined
|
|
254
254
|
? []
|
|
255
|
-
: [resolveTitleComponentBounds(subtitle, config.subtitleStyle)])
|
|
255
|
+
: [resolveTitleComponentBounds(subtitle, config.subtitleStyle, program.materializationConfigs.textMetrics)])
|
|
256
256
|
]);
|
|
257
257
|
validateLayout(
|
|
258
258
|
config.position,
|
|
@@ -2,6 +2,7 @@ import { action } from "../../core/action.js";
|
|
|
2
2
|
import { validateOptionObject } from "../../core/validation.js";
|
|
3
3
|
import { findDataset } from "../../selectors/datasets.js";
|
|
4
4
|
import { findLayer } from "../../selectors/layers.js";
|
|
5
|
+
import { STROKE_STYLE_PROPERTIES } from "../../grammar/strokeStyle.js";
|
|
5
6
|
import {
|
|
6
7
|
applyFacadeGuides,
|
|
7
8
|
normalizeAppearance,
|
|
@@ -20,12 +21,13 @@ const OPTIONS = Object.freeze([
|
|
|
20
21
|
]);
|
|
21
22
|
const POSITION_OPTIONS = Object.freeze(["field", "fieldType", "scale"]);
|
|
22
23
|
const DENSITY_OPTIONS = Object.freeze([
|
|
23
|
-
"bandwidth", "extent", "steps", "kernel", "normalization", "width", "side"
|
|
24
|
+
"bandwidth", "extent", "steps", "kernel", "normalization", "weight", "width", "side"
|
|
24
25
|
]);
|
|
25
26
|
const WIDTH_OPTIONS = Object.freeze(["band", "resolve"]);
|
|
26
27
|
const SPLIT_OPTIONS = Object.freeze(["field", "domain"]);
|
|
27
28
|
const AREA_OPTIONS = Object.freeze([
|
|
28
|
-
"fill", "opacity", "stroke", "strokeWidth", "curve"
|
|
29
|
+
"fill", "opacity", "stroke", "strokeWidth", "curve",
|
|
30
|
+
...STROKE_STYLE_PROPERTIES
|
|
29
31
|
]);
|
|
30
32
|
const CATEGORICAL_TYPES = Object.freeze(["nominal", "ordinal"]);
|
|
31
33
|
const OPERATION = "createViolinPlot";
|
|
@@ -130,7 +132,7 @@ function guideOptions(guides, x, y, category, color) {
|
|
|
130
132
|
};
|
|
131
133
|
}
|
|
132
134
|
|
|
133
|
-
export const createViolinPlot = action(
|
|
135
|
+
export const createViolinPlot = /* @__PURE__ */ action(
|
|
134
136
|
{
|
|
135
137
|
op: "createViolinPlot",
|
|
136
138
|
description: "Create a categorical kernel-density violin plot."
|
|
@@ -221,6 +223,7 @@ export const createViolinPlot = action(
|
|
|
221
223
|
steps: transform.steps,
|
|
222
224
|
kernel: transform.kernel,
|
|
223
225
|
normalization: transform.normalization,
|
|
226
|
+
...(transform.weight === undefined ? {} : { weight: transform.weight }),
|
|
224
227
|
width: transform.placement.width,
|
|
225
228
|
side: transform.placement.side
|
|
226
229
|
},
|
|
@@ -74,7 +74,7 @@ function assertSplitSelectionCompatibility(program, owner, changesSplit) {
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
export const editViolinPlot = action(
|
|
77
|
+
export const editViolinPlot = /* @__PURE__ */ action(
|
|
78
78
|
{
|
|
79
79
|
op: OPERATION,
|
|
80
80
|
description: "Revise one violin plot's data and statistical roles."
|
|
@@ -181,6 +181,7 @@ export const editViolinPlot = action(
|
|
|
181
181
|
steps: transform.steps,
|
|
182
182
|
kernel: transform.kernel,
|
|
183
183
|
normalization: transform.normalization,
|
|
184
|
+
...(transform.weight === undefined ? {} : { weight: transform.weight }),
|
|
184
185
|
width: transform.placement.width,
|
|
185
186
|
side: transform.placement.side
|
|
186
187
|
}
|
package/src/core/action.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { validateKeys } from "./validation.js";
|
|
2
|
+
import { annotateError } from "./diagnostics.js";
|
|
3
|
+
import { cloneAndFreeze, freezeOwned, isOwned, isPlainObject } from "./immutable.js";
|
|
2
4
|
|
|
3
5
|
const metadataByWrappedAction = new WeakMap();
|
|
6
|
+
const implementationByWrappedAction = new WeakMap();
|
|
4
7
|
let actionCompletionHook;
|
|
5
8
|
|
|
6
9
|
export function setActionCompletionHook(hook) {
|
|
@@ -14,6 +17,14 @@ export function getWrappedActionMetadata(value) {
|
|
|
14
17
|
return metadataByWrappedAction.get(value);
|
|
15
18
|
}
|
|
16
19
|
|
|
20
|
+
export function invokeWrappedActionImplementation(value, program, args = {}) {
|
|
21
|
+
const implementation = implementationByWrappedAction.get(value);
|
|
22
|
+
if (implementation === undefined) {
|
|
23
|
+
throw new TypeError("Expected an action created by action().");
|
|
24
|
+
}
|
|
25
|
+
return implementation.call(program, args);
|
|
26
|
+
}
|
|
27
|
+
|
|
17
28
|
function summarizeObject(value, ancestors = new WeakSet()) {
|
|
18
29
|
if (ancestors.has(value)) {
|
|
19
30
|
throw new TypeError("Action arguments must not contain circular references.");
|
|
@@ -26,6 +37,8 @@ function summarizeObject(value, ancestors = new WeakSet()) {
|
|
|
26
37
|
summary[`${key}Count`] = item.length;
|
|
27
38
|
} else if (isPlainObject(item)) {
|
|
28
39
|
summary[key] = summarizeObject(item, ancestors);
|
|
40
|
+
} else if (typeof item === "function") {
|
|
41
|
+
summary[`${key}Type`] = "function";
|
|
29
42
|
} else if (item !== null && typeof item === "object") {
|
|
30
43
|
summary[`${key}Type`] = item.constructor?.name ?? "object";
|
|
31
44
|
} else {
|
|
@@ -62,13 +75,59 @@ export function createActionNode({ id, op, description, args }) {
|
|
|
62
75
|
});
|
|
63
76
|
}
|
|
64
77
|
|
|
78
|
+
// Persistent child tails make appending the active (last) branch independent
|
|
79
|
+
// of sibling count. The public children property remains a stable frozen Array.
|
|
80
|
+
const childLists = new WeakMap();
|
|
81
|
+
|
|
82
|
+
function childList(node) {
|
|
83
|
+
let list = childLists.get(node);
|
|
84
|
+
if (list !== undefined) return list;
|
|
85
|
+
let tail;
|
|
86
|
+
for (const child of node.children) tail = { value: child, previous: tail };
|
|
87
|
+
list = { tail, length: node.children.length };
|
|
88
|
+
if (isOwned(node)) childLists.set(node, list);
|
|
89
|
+
return list;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function withChildList(node, list) {
|
|
93
|
+
let children;
|
|
94
|
+
const next = {};
|
|
95
|
+
for (const key of Object.keys(node)) {
|
|
96
|
+
if (key !== "children") Object.defineProperty(next, key, {
|
|
97
|
+
value: node[key], enumerable: true
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
Object.defineProperty(next, "children", {
|
|
101
|
+
enumerable: true,
|
|
102
|
+
get() {
|
|
103
|
+
if (children === undefined) {
|
|
104
|
+
const values = new Array(list.length);
|
|
105
|
+
let item = list.tail;
|
|
106
|
+
for (let index = list.length - 1; index >= 0; index -= 1) {
|
|
107
|
+
values[index] = item.value;
|
|
108
|
+
item = item.previous;
|
|
109
|
+
}
|
|
110
|
+
children = freezeOwned(values);
|
|
111
|
+
}
|
|
112
|
+
return children;
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
childLists.set(next, list);
|
|
116
|
+
return freezeOwned(next);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function childAt(node, index) {
|
|
120
|
+
const list = childList(node);
|
|
121
|
+
return index === list.length - 1 ? list.tail.value : node.children[index];
|
|
122
|
+
}
|
|
123
|
+
|
|
65
124
|
function nodeAtPath(root, path) {
|
|
66
125
|
let node = root;
|
|
67
126
|
for (const index of path) {
|
|
68
|
-
if (!Number.isInteger(index) || index < 0 || index >= node.
|
|
127
|
+
if (!Number.isInteger(index) || index < 0 || index >= childList(node).length) {
|
|
69
128
|
throw new Error(`Unknown parent action path "${path.join(".")}".`);
|
|
70
129
|
}
|
|
71
|
-
node = node
|
|
130
|
+
node = childAt(node, index);
|
|
72
131
|
}
|
|
73
132
|
return node;
|
|
74
133
|
}
|
|
@@ -78,19 +137,28 @@ export function appendActionNodeAtPath(root, parentPath, actionNode) {
|
|
|
78
137
|
throw new TypeError("Parent action path must be an array.");
|
|
79
138
|
}
|
|
80
139
|
const parent = nodeAtPath(root, parentPath);
|
|
81
|
-
const path = [...parentPath, parent.
|
|
140
|
+
const path = [...parentPath, childList(parent).length];
|
|
82
141
|
|
|
83
142
|
function append(node, depth) {
|
|
143
|
+
const list = childList(node);
|
|
84
144
|
if (depth === parentPath.length) {
|
|
85
|
-
return
|
|
86
|
-
|
|
87
|
-
children: freezeOwned([...node.children, actionNode])
|
|
145
|
+
return withChildList(node, {
|
|
146
|
+
tail: { value: actionNode, previous: list.tail }, length: list.length + 1
|
|
88
147
|
});
|
|
89
148
|
}
|
|
90
149
|
const index = parentPath[depth];
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
150
|
+
const child = append(childAt(node, index), depth + 1);
|
|
151
|
+
if (index === list.length - 1) {
|
|
152
|
+
return withChildList(node, {
|
|
153
|
+
tail: { value: child, previous: list.tail.previous }, length: list.length
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
// Restored/extension traces may address an older sibling explicitly.
|
|
157
|
+
let tail;
|
|
158
|
+
for (let position = 0; position < list.length; position += 1) {
|
|
159
|
+
tail = { value: position === index ? child : node.children[position], previous: tail };
|
|
160
|
+
}
|
|
161
|
+
return withChildList(node, { tail, length: list.length });
|
|
94
162
|
}
|
|
95
163
|
|
|
96
164
|
return { root: append(root, 0), path };
|
|
@@ -128,38 +196,52 @@ export function action(metadata, implementation) {
|
|
|
128
196
|
});
|
|
129
197
|
|
|
130
198
|
const wrappedAction = function wrappedAction(args = {}) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
});
|
|
143
|
-
let result = implementation.call(entered, args);
|
|
144
|
-
|
|
145
|
-
if (!(result instanceof this.constructor)) {
|
|
146
|
-
throw new TypeError(`${ownedMetadata.op} must return a ChartProgram.`);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (this.actionStack.length === 0 && actionCompletionHook !== undefined) {
|
|
150
|
-
result = actionCompletionHook(result, {
|
|
151
|
-
source: this,
|
|
152
|
-
metadata: ownedMetadata,
|
|
199
|
+
try {
|
|
200
|
+
if (!isPlainObject(args)) {
|
|
201
|
+
throw new TypeError("Action arguments must be a plain object.");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (scope === "unit") this._assertUnitProgram(ownedMetadata.op);
|
|
205
|
+
if (scope === "composition") this._assertCompositionProgram(ownedMetadata.op);
|
|
206
|
+
|
|
207
|
+
const summarizedArgs = summarizeArgs(args);
|
|
208
|
+
const entered = this._enterAction({
|
|
209
|
+
...ownedMetadata,
|
|
153
210
|
args: summarizedArgs
|
|
154
211
|
});
|
|
212
|
+
let result = implementation.call(entered, args);
|
|
213
|
+
|
|
155
214
|
if (!(result instanceof this.constructor)) {
|
|
156
|
-
throw new TypeError(
|
|
215
|
+
throw new TypeError(`${ownedMetadata.op} must return a ChartProgram.`);
|
|
157
216
|
}
|
|
217
|
+
|
|
218
|
+
if (this.actionStack.length === 0 && actionCompletionHook !== undefined) {
|
|
219
|
+
result = actionCompletionHook(result, {
|
|
220
|
+
source: this,
|
|
221
|
+
metadata: ownedMetadata,
|
|
222
|
+
args: summarizedArgs
|
|
223
|
+
});
|
|
224
|
+
if (!(result instanceof this.constructor)) {
|
|
225
|
+
throw new TypeError("Action completion hook must return a ChartProgram.");
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return result._exitAction();
|
|
230
|
+
} catch (error) {
|
|
231
|
+
throw annotateError(error, { code: "action-failed", operation: ownedMetadata.op });
|
|
158
232
|
}
|
|
159
|
-
|
|
160
|
-
return result._exitAction();
|
|
161
233
|
};
|
|
162
234
|
|
|
163
235
|
metadataByWrappedAction.set(wrappedAction, ownedMetadata);
|
|
236
|
+
implementationByWrappedAction.set(wrappedAction, implementation);
|
|
164
237
|
return wrappedAction;
|
|
165
238
|
}
|
|
239
|
+
|
|
240
|
+
// Private built-in convenience: action() owns the object boundary and trace,
|
|
241
|
+
// while each definition supplies its closed option vocabulary exactly once.
|
|
242
|
+
export function closedAction(metadata, options, implementation) {
|
|
243
|
+
return action(metadata, function (args = {}) {
|
|
244
|
+
if (options !== undefined) validateKeys(args, options, metadata.op);
|
|
245
|
+
return implementation.call(this, args);
|
|
246
|
+
});
|
|
247
|
+
}
|
|
@@ -150,8 +150,13 @@ function validateFacetSpec(compositionSpec) {
|
|
|
150
150
|
"compositionSpec.facet.repeat requires target, channel, and fields."
|
|
151
151
|
);
|
|
152
152
|
}
|
|
153
|
+
const repeatChannel = ["x", "y", "theta", "r"].includes(repeat.channel) ||
|
|
154
|
+
(isPlainObject(repeat.channel) &&
|
|
155
|
+
Object.keys(repeat.channel).length === 1 &&
|
|
156
|
+
typeof repeat.channel.parallelDimension === "string" &&
|
|
157
|
+
repeat.channel.parallelDimension.length > 0);
|
|
153
158
|
if (typeof repeat.target !== "string" || repeat.target.length === 0 ||
|
|
154
|
-
!
|
|
159
|
+
!repeatChannel ||
|
|
155
160
|
!Array.isArray(repeat.fields) || repeat.fields.length === 0 ||
|
|
156
161
|
repeat.fields.some(field => typeof field !== "string" || field.length === 0) ||
|
|
157
162
|
new Set(repeat.fields).size !== repeat.fields.length) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const detailsByError = new WeakMap();
|
|
2
|
+
|
|
3
|
+
export function getErrorDetails(error) {
|
|
4
|
+
return error !== null && (typeof error === "object" || typeof error === "function")
|
|
5
|
+
? detailsByError.get(error) : undefined;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// Attach metadata without modifying the thrown value or its existing class.
|
|
9
|
+
export function annotateError(error, details) {
|
|
10
|
+
if (!(error instanceof Error)) return error;
|
|
11
|
+
const previous = getErrorDetails(error);
|
|
12
|
+
const merged = { ...details, ...previous };
|
|
13
|
+
if (merged.candidates !== undefined) merged.candidates = Object.freeze([...merged.candidates]);
|
|
14
|
+
detailsByError.set(error, Object.freeze(merged));
|
|
15
|
+
return error;
|
|
16
|
+
}
|
|
@@ -4,6 +4,32 @@ import { isPlainObject } from "./immutable.js";
|
|
|
4
4
|
const ACTION_NAME_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
5
5
|
const DEFINITION_KEYS = new Set(["name", "actions"]);
|
|
6
6
|
const registeredExtensionNames = new Set();
|
|
7
|
+
const extensionByAction = new Map();
|
|
8
|
+
const builtinOpsByClass = new WeakMap();
|
|
9
|
+
|
|
10
|
+
export function recordBuiltinActions(ProgramClass, additional = []) {
|
|
11
|
+
builtinOpsByClass.set(ProgramClass, new Set([
|
|
12
|
+
...Object.getOwnPropertyNames(ProgramClass.prototype).flatMap(name => {
|
|
13
|
+
const metadata = getWrappedActionMetadata(ProgramClass.prototype[name]);
|
|
14
|
+
return metadata ? [metadata.op] : [];
|
|
15
|
+
}), ...additional
|
|
16
|
+
]));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function isBuiltinProgramClass(ProgramClass) {
|
|
20
|
+
return builtinOpsByClass.has(ProgramClass);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function requiredTraceExtension(ProgramClass, op) {
|
|
24
|
+
if (op === "program" || builtinOpsByClass.get(ProgramClass)?.has(op)) return undefined;
|
|
25
|
+
const extension = extensionByAction.get(op);
|
|
26
|
+
if (extension !== undefined) return extension;
|
|
27
|
+
throw new TypeError(`Cannot persist unregistered action "${op}".`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function hasRegisteredExtension(name) {
|
|
31
|
+
return registeredExtensionNames.has(name);
|
|
32
|
+
}
|
|
7
33
|
|
|
8
34
|
function readDataProperty(object, key, owner) {
|
|
9
35
|
const descriptor = Object.getOwnPropertyDescriptor(object, key);
|
|
@@ -101,4 +127,5 @@ export function registerProgramExtension(ProgramClass, definition) {
|
|
|
101
127
|
|
|
102
128
|
Object.defineProperties(ProgramClass.prototype, descriptors);
|
|
103
129
|
registeredExtensionNames.add(name);
|
|
130
|
+
for (const actionName of actionNames) extensionByAction.set(actionName, name);
|
|
104
131
|
}
|
package/src/core/font.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const DEFAULT_FONT_FAMILY = "sans-serif";
|
|
2
|
+
|
|
3
|
+
export function normalizeRendererFontWeight(fontWeight) {
|
|
4
|
+
if (typeof fontWeight !== "number") return fontWeight;
|
|
5
|
+
return Math.min(900, Math.max(100, Math.round(fontWeight / 100) * 100));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function textMetricKey({ text, fontFamily, fontSize, fontWeight }) {
|
|
9
|
+
return JSON.stringify([text, fontFamily, fontSize, fontWeight]);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function textMetricFontWeight(value = "normal") {
|
|
13
|
+
if (typeof value === "number") return normalizeRendererFontWeight(value);
|
|
14
|
+
if (value === "normal") return 400;
|
|
15
|
+
if (value === "bold") return 700;
|
|
16
|
+
if (/^[1-9]00$/u.test(value)) return Number(value);
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
package/src/core/identifiers.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import { annotateError } from "./diagnostics.js";
|
|
1
2
|
export const USER_ID_SOURCE = "[A-Za-z0-9_-]+";
|
|
2
3
|
|
|
3
4
|
const USER_ID_PATTERN = new RegExp(`^${USER_ID_SOURCE}$`);
|
|
4
5
|
|
|
5
6
|
export function validateUserId(id, label = "ID") {
|
|
6
7
|
if (typeof id !== "string" || !USER_ID_PATTERN.test(id)) {
|
|
7
|
-
throw new TypeError(
|
|
8
|
+
throw annotateError(new TypeError(
|
|
8
9
|
`${label} must contain only letters, numbers, _ or - and must not be empty.`
|
|
9
|
-
);
|
|
10
|
+
), { code: "invalid-value", optionPath: label });
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
return id;
|
|
@@ -20,9 +21,9 @@ export function resolveOptionalUserId(id, {
|
|
|
20
21
|
}) {
|
|
21
22
|
if (id !== undefined) return validateUserId(id, label);
|
|
22
23
|
if (ambiguous) {
|
|
23
|
-
throw new Error(
|
|
24
|
+
throw annotateError(new Error(
|
|
24
25
|
`${operation} requires an explicit ${label.toLowerCase()} because its default is ambiguous.`
|
|
25
|
-
);
|
|
26
|
+
), { code: "ambiguous-resource", operation, optionPath: label });
|
|
26
27
|
}
|
|
27
28
|
return validateUserId(defaultId, label);
|
|
28
29
|
}
|
package/src/core/immutable.js
CHANGED
|
@@ -22,11 +22,18 @@ export function isOwned(value) {
|
|
|
22
22
|
return value !== null && typeof value === "object" && ownedValues.has(value);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export function cloneAndFreeze(value, ancestors = new WeakSet()) {
|
|
25
|
+
export function cloneAndFreeze(value, ancestors = new WeakSet(), path = "state") {
|
|
26
|
+
if (typeof value === "function") {
|
|
27
|
+
throw new TypeError(`Cannot store a function at ${path} in a ChartProgram.`);
|
|
28
|
+
}
|
|
26
29
|
if (value === null || typeof value !== "object") {
|
|
27
30
|
return value;
|
|
28
31
|
}
|
|
29
32
|
|
|
33
|
+
// Only library-owned values are recursively immutable; Object.freeze alone
|
|
34
|
+
// does not prove ownership of a caller's descendants.
|
|
35
|
+
if (isOwned(value)) return value;
|
|
36
|
+
|
|
30
37
|
if (ancestors.has(value)) {
|
|
31
38
|
throw new TypeError("Cannot store cyclic values in a ChartProgram.");
|
|
32
39
|
}
|
|
@@ -36,12 +43,12 @@ export function cloneAndFreeze(value, ancestors = new WeakSet()) {
|
|
|
36
43
|
let clone;
|
|
37
44
|
|
|
38
45
|
if (Array.isArray(value)) {
|
|
39
|
-
clone = value.map(item => cloneAndFreeze(item, ancestors));
|
|
46
|
+
clone = value.map((item, index) => cloneAndFreeze(item, ancestors, `${path}[${index}]`));
|
|
40
47
|
} else if (isPlainObject(value)) {
|
|
41
48
|
clone = Object.fromEntries(
|
|
42
49
|
Object.entries(value).map(([key, item]) => [
|
|
43
50
|
key,
|
|
44
|
-
cloneAndFreeze(item, ancestors)
|
|
51
|
+
cloneAndFreeze(item, ancestors, `${path}.${key}`)
|
|
45
52
|
])
|
|
46
53
|
);
|
|
47
54
|
} else {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// The method's value-producing contract is shared by runtime validation and
|
|
2
|
+
// authoring knowledge; editing can inherit these values from stored state.
|
|
3
|
+
export const IMPUTE_REQUIRED_OPTIONS = Object.freeze({
|
|
4
|
+
constant: Object.freeze(["value"]),
|
|
5
|
+
forward: Object.freeze(["sortBy"]),
|
|
6
|
+
backward: Object.freeze(["sortBy"]),
|
|
7
|
+
linear: Object.freeze(["sortBy"])
|
|
8
|
+
});
|