ggaction 0.0.22 → 0.0.24
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 +35 -0
- package/README.md +82 -90
- package/knowledge/action-card.schema.json +1 -1
- package/knowledge/action-cards.json +1 -1
- package/knowledge/intent-taxonomy.json +1 -1
- package/knowledge/mcp-resources.json +1 -1
- package/package.json +6 -3
- package/src/actions/charts/heatmap.js +1 -1
- package/src/actions/charts/interval-regression.js +2 -2
- package/src/actions/data/basicBin2d.js +1 -1
- package/src/actions/data/bin2d.js +2 -2
- package/src/actions/data/edit.js +1 -1
- package/src/actions/data/timeUnit.js +1 -1
- package/src/actions/encodings/ranged.js +2 -2
- package/src/actions/facets/actions.js +1 -1
- package/src/actions/guides/axes/components.js +5 -0
- package/src/actions/guides/axes/edit.js +1 -188
- package/src/actions/guides/axes/labels.js +1 -537
- package/src/actions/guides/axes/tickGroups.js +16 -1
- package/src/actions/guides/axes/ticks.js +33 -24
- package/src/actions/guides/axes/titles.js +1 -389
- package/src/actions/guides/legends/categorical/components.js +1 -4
- package/src/actions/regression/create.js +6 -1
- package/src/actions/regression/edit.js +10 -1
- package/src/actions/titles/resolve.js +11 -1
- package/src/core/compositionState.js +9 -4
- package/src/core/facetAxisPolicy.js +30 -0
- package/src/core/facetGap.js +44 -0
- package/src/grammar/axisTextPlacement.js +1 -1
- package/src/grammar/bin2d.js +34 -4
- package/src/grammar/curveCommands.js +35 -2
- package/src/grammar/datasetSchema.js +1 -1
- package/src/grammar/facetHeaders.js +1 -1
- package/src/grammar/facets/guides.js +7 -5
- package/src/grammar/regression/parameters.js +7 -0
- package/src/grammar/timeUnit.js +9 -3
- package/src/inspectionDescriptors.js +1 -1
- package/src/layout/composition.js +2 -6
- package/src/layout/facetContentFlow.js +15 -7
- package/src/layout/facets.js +1 -1
- package/src/layout/guideCollisions.js +9 -3
- package/src/layout/title.js +1 -1
- package/src/materialization/facetContentBounds.js +96 -0
- package/src/materialization/facetGuides/layout.js +1 -1
- package/src/materialization/facetGuides/placement.js +16 -0
- package/src/materialization/facetHeaders.js +1 -1
- package/src/materialization/facets.js +1 -1
- package/src/materialization/graphicHierarchy.js +25 -1
- package/src/materialization/guides/layout.js +1 -1
- package/src/materialization/guides/resources.js +1 -1
- package/src/materialization/planner.js +19 -4
- package/src/persistence/validation.js +25 -1
- package/src/version.js +1 -1
- package/types/basic.d.ts +2 -0
- package/types/index.d.ts +3 -0
- package/types/program.d.ts +36 -18
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { axisGraphicIds } from "../../../materialization/guides/resources.js";
|
|
2
|
+
import { isLabelRelativeTitleOffset } from "../../../grammar/axisTextPlacement.js";
|
|
2
3
|
|
|
3
4
|
export const AXIS_COMPONENTS = Object.freeze(["line", "ticks", "labels", "title"]);
|
|
4
5
|
|
|
@@ -31,6 +32,10 @@ export function assertRemovableAxisComponent(program, channel, component, operat
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
export function removeAxisComponent(program, channel, component, axisId = channel) {
|
|
35
|
+
if (component === "labels" && isLabelRelativeTitleOffset(program.guideConfigs.axis?.[axisId]?.title?.offset) &&
|
|
36
|
+
program.context.pendingRelativeTitleAxis !== axisId) {
|
|
37
|
+
throw new Error("Remove the label-relative axis title together with its labels.");
|
|
38
|
+
}
|
|
34
39
|
const id = axisComponentId(channel, component, axisId);
|
|
35
40
|
let next = program;
|
|
36
41
|
if (component === "title" && next.semanticSpec.guides.axis?.[axisId]?.title !== undefined) {
|
|
@@ -1,188 +1 @@
|
|
|
1
|
-
import { selectCartesianAxis,
|
|
2
|
-
import { validateAxisPosition, resolveAxisFrameConfig } from "./policy.js";
|
|
3
|
-
import { withGuideLayoutValidation } from "../../../materialization/guides/layout.js";
|
|
4
|
-
import { action } from "../../../core/action.js";
|
|
5
|
-
import { isPlainObject } from "../../../core/immutable.js";
|
|
6
|
-
import { validateKeys, validateOptionObject, validateNonNegativeFinite } from "../../../core/validation.js";
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
AXIS_COMPONENTS,
|
|
10
|
-
axisComponentId as graphicId,
|
|
11
|
-
hasAxisComponent as hasComponent,
|
|
12
|
-
assertRemovableAxisComponent as assertRemovable,
|
|
13
|
-
removeAxisComponent as removeComponent
|
|
14
|
-
} from "./components.js";
|
|
15
|
-
|
|
16
|
-
const OPTIONS = Object.freeze([
|
|
17
|
-
"target", "position", "line", "ticks", "labels", "ticksAndLabels", "title", "offset", "underlay"
|
|
18
|
-
]);
|
|
19
|
-
const LINE_OPTIONS = Object.freeze(["color", "lineWidth"]);
|
|
20
|
-
const TICK_OPTIONS = Object.freeze([
|
|
21
|
-
"count", "values", "rounding", "length", "color", "lineWidth", "alongOffset", "strokeDash", "strokeDashMap"
|
|
22
|
-
]);
|
|
23
|
-
const LABEL_OPTIONS = Object.freeze([
|
|
24
|
-
"count", "values", "rounding", "offset", "format", "color", "fontSize",
|
|
25
|
-
"fontFamily", "fontWeight", "rotation", "maxWidth", "wrap",
|
|
26
|
-
"lineHeight", "overlap", "alongOffset", "labelMap", "flush", "colorMap", "align"
|
|
27
|
-
]);
|
|
28
|
-
const GROUP_OPTIONS = Object.freeze(["count", "values", "rounding", "ticks", "labels"]);
|
|
29
|
-
const TITLE_OPTIONS = Object.freeze([
|
|
30
|
-
"text", "at", "offset", "rotation", "color", "fontSize",
|
|
31
|
-
"fontFamily", "fontWeight", "alongOffset", "align", "baseline", "overlap"
|
|
32
|
-
]);
|
|
33
|
-
|
|
34
|
-
function validateNested(args, key, options, operation, { allowFalse = false } = {}) {
|
|
35
|
-
if (!Object.hasOwn(args, key)) return;
|
|
36
|
-
if (allowFalse && args[key] === false) return;
|
|
37
|
-
if (!isPlainObject(args[key])) {
|
|
38
|
-
const accepted = allowFalse ? "false or a plain object" : "a plain object";
|
|
39
|
-
throw new TypeError(`${operation}.${key} must be ${accepted}.`);
|
|
40
|
-
}
|
|
41
|
-
validateKeys(args[key], options, `${operation}.${key}`);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function validateArgs(args, operation) {
|
|
45
|
-
if (Object.hasOwn(args, "offset")) validateNonNegativeFinite(args.offset, "Axis frame offset");
|
|
46
|
-
resolveAxisFrameConfig(undefined, args);
|
|
47
|
-
validateOptionObject(args, OPTIONS, operation, {
|
|
48
|
-
allowEmpty: false,
|
|
49
|
-
emptyMessage: `${operation} requires at least one axis change.`,
|
|
50
|
-
emptyError: Error
|
|
51
|
-
});
|
|
52
|
-
const removable = { allowFalse: true };
|
|
53
|
-
validateNested(args, "line", LINE_OPTIONS, operation, removable);
|
|
54
|
-
validateNested(args, "ticks", TICK_OPTIONS, operation, removable);
|
|
55
|
-
validateNested(args, "labels", LABEL_OPTIONS, operation, removable);
|
|
56
|
-
validateNested(args, "ticksAndLabels", GROUP_OPTIONS, operation, removable);
|
|
57
|
-
validateNested(args, "title", TITLE_OPTIONS, operation, removable);
|
|
58
|
-
if (args.ticksAndLabels !== undefined && (
|
|
59
|
-
args.ticks !== undefined || args.labels !== undefined
|
|
60
|
-
)) {
|
|
61
|
-
throw new Error(
|
|
62
|
-
`${operation} cannot combine ticksAndLabels with ticks or labels.`
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
if (args.ticksAndLabels !== false && args.ticksAndLabels?.ticks !== undefined) {
|
|
66
|
-
validateNested(args.ticksAndLabels, "ticks", ["length", "color", "lineWidth", "alongOffset", "strokeDash", "strokeDashMap"], `${operation}.ticksAndLabels`);
|
|
67
|
-
}
|
|
68
|
-
if (args.ticksAndLabels !== false && args.ticksAndLabels?.labels !== undefined) {
|
|
69
|
-
validateNested(args.ticksAndLabels, "labels", [
|
|
70
|
-
"offset", "format", "color", "fontSize", "fontFamily", "fontWeight",
|
|
71
|
-
"rotation", "maxWidth", "wrap", "lineHeight", "overlap", "alongOffset", "labelMap", "flush", "colorMap", "align"
|
|
72
|
-
], `${operation}.ticksAndLabels`);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function names(channel) {
|
|
77
|
-
const prefix = channel === "x" ? "X" : "Y";
|
|
78
|
-
return {
|
|
79
|
-
operation: `edit${prefix}Axis`,
|
|
80
|
-
line: `edit${prefix}AxisLine`,
|
|
81
|
-
ticks: `edit${prefix}AxisTicks`,
|
|
82
|
-
labels: `edit${prefix}AxisLabels`,
|
|
83
|
-
group: `edit${prefix}AxisTicksAndLabels`,
|
|
84
|
-
title: `edit${prefix}AxisTitle`,
|
|
85
|
-
remove: `remove${prefix}Axis`
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function buildPlan(program, channel, args, operation, axisId = channel) {
|
|
90
|
-
const shared = { ...axisTargetArgs(axisId, channel),
|
|
91
|
-
...(Object.hasOwn(args, "position") ? { position: args.position } : {}) };
|
|
92
|
-
const plan = [];
|
|
93
|
-
const addLeaf = (component, edit) => {
|
|
94
|
-
const value = args[component];
|
|
95
|
-
if (value === false) {
|
|
96
|
-
assertRemovable(program, channel, component, operation.operation, axisId);
|
|
97
|
-
plan.push({ kind: "remove", component });
|
|
98
|
-
} else if (value !== undefined) {
|
|
99
|
-
plan.push({ kind: "edit", operation: edit, args: { ...shared, ...value } });
|
|
100
|
-
} else if ((args.position !== undefined || Object.hasOwn(args, "offset") || Object.hasOwn(args, "underlay")) && hasComponent(program, channel, component, axisId)) {
|
|
101
|
-
plan.push({ kind: "edit", operation: edit, args: shared });
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
addLeaf("line", operation.line);
|
|
106
|
-
if (args.ticksAndLabels === false) {
|
|
107
|
-
assertRemovable(program, channel, "ticks", operation.operation, axisId);
|
|
108
|
-
assertRemovable(program, channel, "labels", operation.operation, axisId);
|
|
109
|
-
plan.push(
|
|
110
|
-
{ kind: "remove", component: "ticks" },
|
|
111
|
-
{ kind: "remove", component: "labels" }
|
|
112
|
-
);
|
|
113
|
-
} else if (args.ticksAndLabels !== undefined) {
|
|
114
|
-
plan.push({
|
|
115
|
-
kind: "edit",
|
|
116
|
-
operation: operation.group,
|
|
117
|
-
args: { ...shared, ...args.ticksAndLabels }
|
|
118
|
-
});
|
|
119
|
-
} else {
|
|
120
|
-
addLeaf("ticks", operation.ticks);
|
|
121
|
-
addLeaf("labels", operation.labels);
|
|
122
|
-
}
|
|
123
|
-
addLeaf("title", operation.title);
|
|
124
|
-
|
|
125
|
-
if (plan.length === 0) {
|
|
126
|
-
throw new Error(`${operation.operation} requires an existing ${channel}-axis component.`);
|
|
127
|
-
}
|
|
128
|
-
return plan;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function applyPlan(program, channel, plan, operation, axisId = channel) {
|
|
132
|
-
let next = program;
|
|
133
|
-
for (const step of plan) {
|
|
134
|
-
next = step.kind === "remove"
|
|
135
|
-
? removeComponent(next, channel, step.component, axisId)
|
|
136
|
-
: next[step.operation](step.args);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const hasRetainedGraphic = AXIS_COMPONENTS.some(
|
|
140
|
-
component => next.graphicSpec.objects[graphicId(channel, component, axisId)] !== undefined
|
|
141
|
-
);
|
|
142
|
-
if (!hasRetainedGraphic && (
|
|
143
|
-
next.semanticSpec.guides.axis?.[axisId] !== undefined ||
|
|
144
|
-
next.guideConfigs.axis?.[axisId] !== undefined
|
|
145
|
-
)) {
|
|
146
|
-
next = next[operation.remove](axisId === channel ? undefined : axisTargetArgs(axisId, channel));
|
|
147
|
-
}
|
|
148
|
-
return next;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function makeEditAxis(channel) {
|
|
152
|
-
const operation = names(channel);
|
|
153
|
-
return action(
|
|
154
|
-
{
|
|
155
|
-
op: operation.operation,
|
|
156
|
-
description: `Edit selected existing ${channel}-axis components.`
|
|
157
|
-
},
|
|
158
|
-
withGuideLayoutValidation(function (args = {}) {
|
|
159
|
-
validateArgs(args, operation.operation);
|
|
160
|
-
const selected = selectCartesianAxis(this, channel, args, { whole: true });
|
|
161
|
-
const axisId = selected.id; args = selected.options;
|
|
162
|
-
let source = this;
|
|
163
|
-
if (selected.owner?.named && args.position !== undefined) {
|
|
164
|
-
const position = validateAxisPosition(channel, args.position);
|
|
165
|
-
assertCartesianAxisSideAvailable(this, { id: axisId, channel,
|
|
166
|
-
coordinate: selected.owner.coordinate, position });
|
|
167
|
-
source = source.editSemantic({ property: `guide.axis.${axisId}.position`, value: position });
|
|
168
|
-
}
|
|
169
|
-
const frame = resolveAxisFrameConfig(source.guideConfigs.axis?.[axisId]?.frame, args);
|
|
170
|
-
source = frame === undefined ? source._withoutMaterializationConfig(["guides", "axis", axisId, "frame"])
|
|
171
|
-
: source._withGuideConfig(axisId, "frame", frame);
|
|
172
|
-
const plan = buildPlan(source, channel, args, operation, axisId);
|
|
173
|
-
|
|
174
|
-
// Run the complete proposal against an immutable speculative branch so a
|
|
175
|
-
// later leaf failure cannot begin the returned action trace or state.
|
|
176
|
-
applyPlan(source, channel, plan, operation, axisId);
|
|
177
|
-
return applyPlan(source, channel, plan, operation, axisId);
|
|
178
|
-
})
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const editXAxis = /* @__PURE__ */ makeEditAxis("x");
|
|
183
|
-
const editYAxis = /* @__PURE__ */ makeEditAxis("y");
|
|
184
|
-
|
|
185
|
-
export function registerCompleteAxisEditActions(ProgramClass) {
|
|
186
|
-
ProgramClass.prototype.editXAxis = editXAxis;
|
|
187
|
-
ProgramClass.prototype.editYAxis = editYAxis;
|
|
188
|
-
}
|
|
1
|
+
import{resolveAxisTickEditConfig as x}from"./ticks.js";import{resolveAxisLabelEditConfig as L}from"./labels.js";import{validateAxisTickGroupEditConfig as T}from"./tickGroups.js";import{selectCartesianAxis as w,axisTargetArgs as k,assertCartesianAxisSideAvailable as C}from"../../../selectors/axes.js";import{validateAxisPosition as $,resolveAxisFrameConfig as h}from"./policy.js";import{withGuideLayoutValidation as E,withPendingRelativeAxisTitle as j}from"../../../materialization/guides/layout.js";import{action as S}from"../../../core/action.js";import{applyMaterializationPlan as N}from"../../../materialization/planner.js";import{isPlainObject as u}from"../../../core/immutable.js";import{isLabelRelativeTitleOffset as O}from"../../../grammar/axisTextPlacement.js";import{validateKeys as P,validateOptionObject as g,validateNonNegativeFinite as y}from"../../../core/validation.js";import{AXIS_COMPONENTS as z,axisComponentId as M,hasAxisComponent as R,assertRemovableAxisComponent as m,removeAxisComponent as W}from"./components.js";const _=Object.freeze(["target","position","line","ticks","labels","ticksAndLabels","title","offset","underlay"]),G=Object.freeze(["color","lineWidth"]),F=Object.freeze(["count","values","rounding","length","color","lineWidth","alongOffset","strokeDash","strokeDashMap"]),D=Object.freeze(["count","values","rounding","offset","format","color","fontSize","fontFamily","fontWeight","rotation","maxWidth","wrap","lineHeight","overlap","alongOffset","labelMap","flush","flushThreshold","colorMap","align"]),X=Object.freeze(["count","values","rounding","ticks","labels"]),Y=Object.freeze(["text","at","offset","rotation","color","fontSize","fontFamily","fontWeight","alongOffset","align","baseline","overlap"]);function a(e,i,t,n,{allowFalse:o=!1}={}){if(Object.hasOwn(e,i)&&!(o&&e[i]===!1)){if(!u(e[i])){const f=o?"false or a plain object":"a plain object";throw new TypeError(`${n}.${i} must be ${f}.`)}P(e[i],t,`${n}.${i}`)}}function q(e,i){Object.hasOwn(e,"offset")&&y(e.offset,"Axis frame offset"),h(void 0,e),g(e,_,i,{allowEmpty:!1,emptyMessage:`${i} requires at least one axis change.`,emptyError:Error});const t={allowFalse:!0};if(a(e,"line",G,i,t),a(e,"ticks",F,i,t),a(e,"labels",D,i,t),a(e,"ticksAndLabels",X,i,t),a(e,"title",Y,i,t),e.ticksAndLabels!==void 0&&(e.ticks!==void 0||e.labels!==void 0))throw new Error(`${i} cannot combine ticksAndLabels with ticks or labels.`);e.ticksAndLabels!==!1&&e.ticksAndLabels?.ticks!==void 0&&a(e.ticksAndLabels,"ticks",["length","color","lineWidth","alongOffset","strokeDash","strokeDashMap"],`${i}.ticksAndLabels`),e.ticksAndLabels!==!1&&e.ticksAndLabels?.labels!==void 0&&a(e.ticksAndLabels,"labels",["offset","format","color","fontSize","fontFamily","fontWeight","rotation","maxWidth","wrap","lineHeight","overlap","alongOffset","labelMap","flush","flushThreshold","colorMap","align"],`${i}.ticksAndLabels`)}function H(e){const i=e==="x"?"X":"Y";return{operation:`edit${i}Axis`,line:`edit${i}AxisLine`,ticks:`edit${i}AxisTicks`,labels:`edit${i}AxisLabels`,group:`edit${i}AxisTicksAndLabels`,title:`edit${i}AxisTitle`,remove:`remove${i}Axis`}}function K(e,i,t,n){u(t.ticks)&&x(e,i,t.ticks,n),u(t.labels)&&L(e,i,t.labels,n),u(t.ticksAndLabels)&&T(e,i,t.ticksAndLabels,n)}function B(e,i,t,n,o=i){const f={...k(o,i),...Object.hasOwn(t,"position")?{position:t.position}:{}},l=[],d=(c,s)=>{const r=t[c];r===!1?(m(e,i,c,n.operation,o),l.push({kind:"remove",component:c})):r!==void 0?l.push({kind:"edit",operation:s,args:{...f,...r}}):(t.position!==void 0||Object.hasOwn(t,"offset")||Object.hasOwn(t,"underlay"))&&R(e,i,c,o)&&l.push({kind:"edit",operation:s,args:f})};if(d("line",n.line),t.ticksAndLabels===!1?(m(e,i,"ticks",n.operation,o),m(e,i,"labels",n.operation,o),l.push({kind:"remove",component:"ticks"},{kind:"remove",component:"labels"})):t.ticksAndLabels!==void 0?l.push({kind:"edit",operation:n.group,args:{...f,...t.ticksAndLabels}}):(d("ticks",n.ticks),d("labels",n.labels)),d("title",n.title),l.length===0)throw new Error(`${n.operation} requires an existing ${i}-axis component.`);return l}function p(e,i,t,n,o=i){if(O(e.guideConfigs.axis?.[o]?.title?.offset)&&t.some(s=>s.kind==="edit"&&s.operation===n.title)&&e.context.pendingRelativeTitleAxis!==o)return j(e,o,s=>p(s,i,t,n,o));let l=e;const d=O(e.guideConfigs.axis?.[o]?.title?.offset)&&t.some(s=>s.kind==="remove"&&s.component==="title")?[...t.filter(s=>s.kind==="remove"&&s.component==="title"),...t.filter(s=>!(s.kind==="remove"&&s.component==="title"))]:t;for(let s=0;s<d.length;){const r=d[s];if(r.kind==="remove"){l=W(l,i,r.component,o),s+=1;continue}const b=[];for(;s<d.length&&d[s].kind==="edit";){const A=d[s++];b.push({op:A.operation,args:A.args})}l=N(l,b)}return!z.some(s=>l.graphicSpec.objects[M(i,s,o)]!==void 0)&&(l.semanticSpec.guides.axis?.[o]!==void 0||l.guideConfigs.axis?.[o]!==void 0)&&(l=l[n.remove](o===i?void 0:k(o,i))),l}function v(e){const i=H(e);return S({op:i.operation,description:`Edit selected existing ${e}-axis components.`},E(function(t={}){q(t,i.operation);const n=w(this,e,t,{whole:!0}),o=n.id;t=n.options,K(this,e,t,o);let f=this;if(n.owner?.named&&t.position!==void 0){const c=$(e,t.position);C(this,{id:o,channel:e,coordinate:n.owner.coordinate,position:c}),f=f.editSemantic({property:`guide.axis.${o}.position`,value:c})}const l=h(f.guideConfigs.axis?.[o]?.frame,t);f=l===void 0?f._withoutMaterializationConfig(["guides","axis",o,"frame"]):f._withGuideConfig(o,"frame",l);const d=B(f,e,t,i,o);return p(f,e,d,i,o),p(f,e,d,i,o)}))}const U=v("x"),V=v("y");function de(e){e.prototype.editXAxis=U,e.prototype.editYAxis=V}export{de as registerCompleteAxisEditActions};
|