ggaction 0.0.11 → 0.0.13

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.
Files changed (289) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +3 -2
  3. package/knowledge/action-card.schema.json +1 -1
  4. package/knowledge/action-cards.json +1 -1
  5. package/knowledge/action-cards.schema.json +1 -1
  6. package/knowledge/extension-authoring.md +2 -1
  7. package/knowledge/intent-taxonomy.json +1 -1
  8. package/knowledge/mcp-resources.json +1 -1
  9. package/knowledge/task-resolver.js +1 -1
  10. package/package.json +4 -2
  11. package/src/actions/basic.js +2 -0
  12. package/src/actions/boxPlots/materialize.js +2 -1
  13. package/src/actions/canvas/actions.js +3 -1
  14. package/src/actions/canvas/fitting.js +211 -0
  15. package/src/actions/canvas/index.js +2 -0
  16. package/src/actions/categoryOrder/index.js +3 -2
  17. package/src/actions/charts/area.js +47 -0
  18. package/src/actions/charts/bar.js +11 -5
  19. package/src/actions/charts/beeswarm.js +70 -0
  20. package/src/actions/charts/density.js +44 -0
  21. package/src/actions/charts/ecdf.js +183 -0
  22. package/src/actions/charts/endpoints.js +451 -0
  23. package/src/actions/charts/heatmap.js +2 -2
  24. package/src/actions/charts/histogram.js +1 -1
  25. package/src/actions/charts/horizon.js +71 -0
  26. package/src/actions/charts/index.js +37 -0
  27. package/src/actions/charts/interval-regression.js +169 -0
  28. package/src/actions/charts/line.js +5 -5
  29. package/src/actions/charts/parallel.js +1 -1
  30. package/src/actions/charts/pie.js +44 -0
  31. package/src/actions/charts/polar.js +123 -0
  32. package/src/actions/charts/radar.js +299 -0
  33. package/src/actions/charts/radial.js +42 -0
  34. package/src/actions/charts/raincloud.js +422 -0
  35. package/src/actions/charts/rug-strip.js +279 -0
  36. package/src/actions/charts/scatter.js +9 -3
  37. package/src/actions/charts/shared.js +69 -10
  38. package/src/actions/composition/actions.js +118 -1
  39. package/src/actions/data/bin.js +29 -0
  40. package/src/actions/data/computed.js +24 -0
  41. package/src/actions/data/derived.js +66 -0
  42. package/src/actions/data/ecdf.js +38 -0
  43. package/src/actions/data/filter.js +212 -28
  44. package/src/actions/data/fold.js +24 -0
  45. package/src/actions/data/index.js +24 -0
  46. package/src/actions/data/interval.js +2 -0
  47. package/src/actions/data/intervalEdit.js +86 -4
  48. package/src/actions/data/regression.js +5 -2
  49. package/src/actions/data/stack.js +26 -0
  50. package/src/actions/data/summary.js +26 -0
  51. package/src/actions/data/timeUnit.js +1 -1
  52. package/src/actions/encodings/appearance.js +32 -13
  53. package/src/actions/encodings/barWidth.js +8 -0
  54. package/src/actions/encodings/basic.js +2 -0
  55. package/src/actions/encodings/color/continuous.js +19 -59
  56. package/src/actions/encodings/color/index.js +28 -22
  57. package/src/actions/encodings/color/layout.js +3 -97
  58. package/src/actions/encodings/color/policy.js +5 -8
  59. package/src/actions/encodings/density/resolve.js +37 -5
  60. package/src/actions/encodings/density.js +120 -33
  61. package/src/actions/encodings/horizon/resolve.js +8 -4
  62. package/src/actions/encodings/horizon.js +18 -8
  63. package/src/actions/encodings/index.js +2 -0
  64. package/src/actions/encodings/offset.js +33 -27
  65. package/src/actions/encodings/parallel.js +2 -8
  66. package/src/actions/encodings/position/apply.js +40 -4
  67. package/src/actions/encodings/position/index.js +43 -5
  68. package/src/actions/encodings/position/policies/arc.js +37 -2
  69. package/src/actions/encodings/position/policies/area.js +4 -42
  70. package/src/actions/encodings/position/policies/bar.js +48 -13
  71. package/src/actions/encodings/position/resolve.js +79 -24
  72. package/src/actions/encodings/ranged.js +123 -97
  73. package/src/actions/encodings/remove.js +37 -32
  74. package/src/actions/encodings/ruleAppearance.js +23 -9
  75. package/src/actions/encodings/seriesLayout.js +104 -0
  76. package/src/actions/encodings/shared.js +25 -23
  77. package/src/actions/encodings/strokeDash.js +9 -6
  78. package/src/actions/encodings/temporal.js +7 -0
  79. package/src/actions/encodings/text.js +30 -15
  80. package/src/actions/errorBands/create.js +18 -5
  81. package/src/actions/errorBands/edit.js +341 -21
  82. package/src/actions/errorBars/create.js +153 -11
  83. package/src/actions/errorBars/edit.js +365 -19
  84. package/src/actions/facets/actions.js +410 -12
  85. package/src/actions/facets/derive.js +57 -8
  86. package/src/actions/gradientPlots/components.js +33 -11
  87. package/src/actions/gradientPlots/materialize.js +5 -7
  88. package/src/actions/guides/applicability.js +20 -17
  89. package/src/actions/guides/axes/axes.js +4 -3
  90. package/src/actions/guides/axes/axis.js +18 -18
  91. package/src/actions/guides/axes/components.js +43 -0
  92. package/src/actions/guides/axes/edit.js +16 -51
  93. package/src/actions/guides/axes/labels.js +129 -19
  94. package/src/actions/guides/axes/lines.js +7 -5
  95. package/src/actions/guides/axes/parallel/lifecycle.js +117 -0
  96. package/src/actions/guides/axes/parallel/policy.js +137 -0
  97. package/src/actions/guides/axes/parallel/resolve.js +44 -23
  98. package/src/actions/guides/axes/parallel.js +93 -217
  99. package/src/actions/guides/axes/policy.js +21 -67
  100. package/src/actions/guides/axes/tickGroups.js +14 -8
  101. package/src/actions/guides/axes/ticks.js +5 -4
  102. package/src/actions/guides/axes/titles.js +21 -8
  103. package/src/actions/guides/basic.js +2 -0
  104. package/src/actions/guides/facade.js +106 -0
  105. package/src/actions/guides/facadeAxes.js +200 -0
  106. package/src/actions/guides/facadeLegend.js +63 -0
  107. package/src/actions/guides/grids/grid.js +37 -33
  108. package/src/actions/guides/grids/resolve.js +10 -2
  109. package/src/actions/guides/guides.js +35 -28
  110. package/src/actions/guides/legends/categorical/actions.js +193 -249
  111. package/src/actions/guides/legends/categorical/components.js +4 -5
  112. package/src/actions/guides/legends/categorical/layout.js +36 -349
  113. package/src/actions/guides/legends/categorical/options.js +30 -14
  114. package/src/actions/guides/legends/categorical/recipes.js +14 -0
  115. package/src/actions/guides/legends/categorical/resolve.js +21 -10
  116. package/src/actions/guides/legends/categorical/symbols.js +2 -3
  117. package/src/actions/guides/legends/continuous/common.js +69 -14
  118. package/src/actions/guides/legends/continuous/gradient.js +111 -83
  119. package/src/actions/guides/legends/continuous/index.js +6 -2
  120. package/src/actions/guides/legends/continuous/interval.js +71 -92
  121. package/src/actions/guides/legends/continuous/opacity.js +105 -84
  122. package/src/actions/guides/legends/creation.js +27 -0
  123. package/src/actions/guides/legends/edit.js +198 -66
  124. package/src/actions/guides/legends/focused.js +2 -2
  125. package/src/actions/guides/legends/lane.js +98 -86
  126. package/src/actions/guides/legends/lifecycle.js +132 -0
  127. package/src/actions/guides/legends/remove.js +31 -76
  128. package/src/actions/guides/legends/size.js +149 -105
  129. package/src/actions/guides/legends/strokeWidth.js +106 -64
  130. package/src/actions/guides/legends/target.js +12 -0
  131. package/src/actions/guides/legends/transition.js +43 -0
  132. package/src/actions/guides/polar/axes/facade.js +77 -62
  133. package/src/actions/guides/polar/axes/labels.js +5 -4
  134. package/src/actions/guides/polar/axes/lines.js +2 -1
  135. package/src/actions/guides/polar/axes/shared.js +6 -0
  136. package/src/actions/guides/polar/axes/ticks.js +5 -4
  137. package/src/actions/guides/polar/axes/titles.js +5 -4
  138. package/src/actions/guides/polar/grids.js +7 -7
  139. package/src/actions/guides/reuse.js +44 -0
  140. package/src/actions/guides/tickValues.js +19 -21
  141. package/src/actions/index.js +2 -0
  142. package/src/actions/intervals/resolve.js +35 -15
  143. package/src/actions/marks/arc/actions.js +1 -1
  144. package/src/actions/marks/area/actions.js +18 -4
  145. package/src/actions/marks/area/materialize.js +5 -2
  146. package/src/actions/marks/bar/materialize.js +11 -7
  147. package/src/actions/marks/index.js +5 -0
  148. package/src/actions/marks/line/actions.js +43 -108
  149. package/src/actions/marks/line/materialize.js +34 -52
  150. package/src/actions/marks/point/edit.js +11 -15
  151. package/src/actions/marks/point/index.js +1 -0
  152. package/src/actions/marks/point/jitter.js +3 -0
  153. package/src/actions/marks/point/materialize.js +138 -14
  154. package/src/actions/marks/point/packing.js +68 -0
  155. package/src/actions/marks/references.js +87 -0
  156. package/src/actions/marks/remove.js +17 -4
  157. package/src/actions/marks/rule/actions.js +60 -4
  158. package/src/actions/marks/rule/appearance.js +26 -0
  159. package/src/actions/marks/shared.js +12 -12
  160. package/src/actions/marks/text/actions.js +224 -6
  161. package/src/actions/marks/text/layout.js +2 -2
  162. package/src/actions/primitives/createGraphics.js +3 -1
  163. package/src/actions/primitives/semantic.js +1 -1
  164. package/src/actions/primitives/semanticAction.js +39 -2
  165. package/src/actions/primitives/semanticValidation/guide.js +10 -0
  166. package/src/actions/primitives/semanticValidation/layer.js +18 -1
  167. package/src/actions/primitives/semanticValidation/scale.js +10 -1
  168. package/src/actions/regression/create.js +1 -1
  169. package/src/actions/regression/edit.js +19 -6
  170. package/src/actions/regression/resolve.js +1 -1
  171. package/src/actions/scales/consumers/common.js +20 -53
  172. package/src/actions/scales/consumers/families.js +27 -14
  173. package/src/actions/scales/consumers/index.js +23 -2
  174. package/src/actions/scales/consumers/seriesLayout.js +33 -2
  175. package/src/actions/scales/create.js +4 -4
  176. package/src/actions/scales/definitions.js +19 -3
  177. package/src/actions/scales/edit.js +46 -34
  178. package/src/actions/scales/editPolicy.js +21 -31
  179. package/src/actions/scales/materialize.js +10 -46
  180. package/src/actions/scales/patch.js +1 -1
  181. package/src/actions/scales/preview.js +39 -0
  182. package/src/actions/scales/quantitativeColor.js +9 -5
  183. package/src/actions/theme/actions.js +43 -0
  184. package/src/actions/theme/index.js +9 -0
  185. package/src/actions/theme/reconcile.js +882 -0
  186. package/src/actions/titles/actions.js +11 -10
  187. package/src/actions/titles/resolve.js +1 -40
  188. package/src/actions/violinPlots/create.js +86 -42
  189. package/src/actions/violinPlots/edit.js +191 -0
  190. package/src/actions/violinPlots/index.js +2 -0
  191. package/src/core/ChartProgram.js +7 -0
  192. package/src/core/action.js +22 -2
  193. package/src/core/compositionState.js +136 -8
  194. package/src/core/vocabulary.js +2 -2
  195. package/src/grammar/aggregate.js +66 -23
  196. package/src/grammar/arcs.js +213 -65
  197. package/src/grammar/areaEndpoints.js +46 -0
  198. package/src/grammar/areaSeries.js +87 -41
  199. package/src/grammar/bars/aggregate.js +33 -12
  200. package/src/grammar/bars/policy.js +9 -7
  201. package/src/grammar/bin.js +181 -0
  202. package/src/grammar/categoryOrder.js +34 -0
  203. package/src/grammar/computed.js +150 -0
  204. package/src/grammar/ecdf.js +198 -0
  205. package/src/grammar/facets/index.js +117 -6
  206. package/src/grammar/fold.js +123 -0
  207. package/src/grammar/horizon.js +6 -4
  208. package/src/grammar/interval.js +37 -20
  209. package/src/grammar/lineSeries.js +20 -63
  210. package/src/grammar/markFilter.js +22 -4
  211. package/src/grammar/markLabels.js +93 -0
  212. package/src/grammar/parallelAxisTitles.js +16 -0
  213. package/src/grammar/pathSeries.js +91 -0
  214. package/src/grammar/pointPacking.js +183 -0
  215. package/src/grammar/positionCompatibility.js +1 -1
  216. package/src/grammar/positionDatum.js +12 -0
  217. package/src/grammar/rects.js +9 -1
  218. package/src/grammar/regression/derive.js +4 -0
  219. package/src/grammar/regression/index.js +7 -2
  220. package/src/grammar/regression/models.js +22 -7
  221. package/src/grammar/regression/parameters.js +40 -13
  222. package/src/grammar/rotation.js +29 -0
  223. package/src/grammar/rules.js +9 -21
  224. package/src/grammar/scales/color.js +23 -1
  225. package/src/grammar/scales/colorConsumers.js +38 -0
  226. package/src/grammar/scales/definition.js +15 -1
  227. package/src/grammar/scales/discretized.js +11 -5
  228. package/src/grammar/scales/fields.js +38 -8
  229. package/src/grammar/scales/mapping.js +2 -0
  230. package/src/grammar/scales/ordinal.js +23 -0
  231. package/src/grammar/scales/radial.js +55 -0
  232. package/src/grammar/scales/types.js +1 -0
  233. package/src/grammar/schemas/semanticPath.js +17 -3
  234. package/src/grammar/stack.js +170 -0
  235. package/src/grammar/statistics/confidenceInterval.js +101 -0
  236. package/src/grammar/summary.js +183 -0
  237. package/src/grammar/text.js +22 -16
  238. package/src/grammar/timeUnit.js +21 -11
  239. package/src/grammar/transformTopology.js +6 -0
  240. package/src/grammar/transforms.js +36 -0
  241. package/src/grammar/valueFormat.js +112 -0
  242. package/src/grammar/window.js +13 -7
  243. package/src/layout/facets.js +86 -23
  244. package/src/layout/guideCollisions.js +12 -0
  245. package/src/layout/legend.js +2 -10
  246. package/src/layout/legendItems.js +102 -0
  247. package/src/layout/legendLane.js +100 -11
  248. package/src/materialization/bars/aggregate.js +38 -152
  249. package/src/materialization/bars/histogram.js +24 -0
  250. package/src/materialization/categorySlotOffset.js +40 -0
  251. package/src/materialization/compositionSnapshot.js +1 -1
  252. package/src/materialization/dependencies.js +57 -16
  253. package/src/materialization/encodings.js +1 -1
  254. package/src/materialization/facetGuides/legacyCategorical.js +7 -1
  255. package/src/materialization/facetGuides/placement.js +16 -1
  256. package/src/materialization/facetGuides/preparation.js +26 -1
  257. package/src/materialization/facets.js +11 -2
  258. package/src/materialization/guides/layout.js +57 -0
  259. package/src/materialization/guides/resources.js +2 -2
  260. package/src/materialization/legends.js +10 -1
  261. package/src/materialization/marks/capabilities.js +30 -7
  262. package/src/materialization/marks/grouping.js +52 -0
  263. package/src/materialization/marks/index.js +63 -25
  264. package/src/materialization/marks/policies.js +2 -0
  265. package/src/materialization/rect.js +33 -33
  266. package/src/materialization/rowEncoding.js +70 -14
  267. package/src/materialization/scaleGuideDependencies.js +8 -5
  268. package/src/materialization/scales/map.js +1 -0
  269. package/src/materialization/scales/policies/arc.js +42 -0
  270. package/src/materialization/scales/policies/bar.js +0 -43
  271. package/src/materialization/scales/policies/binnedPosition.js +7 -4
  272. package/src/materialization/scales/policies/offset.js +54 -0
  273. package/src/materialization/scales/policies/series.js +2 -1
  274. package/src/materialization/scales/resolve.js +48 -18
  275. package/src/materialization/selection/compatibility.js +11 -0
  276. package/src/materialization/selection/items/bar.js +12 -68
  277. package/src/materialization/selection/items/common.js +8 -2
  278. package/src/materialization/selection/items/path.js +3 -13
  279. package/src/materialization/selection/items/rect.js +7 -4
  280. package/src/materialization/text.js +64 -5
  281. package/src/mcp/adapter.js +2 -2
  282. package/src/selectors/datasets.js +13 -0
  283. package/src/selectors/layers.js +3 -2
  284. package/src/selectors/scales.js +6 -0
  285. package/src/theme/defaults.js +39 -0
  286. package/types/basic.d.ts +11 -2
  287. package/types/index.d.ts +126 -0
  288. package/types/program.d.ts +1327 -124
  289. package/src/materialization/bars/grouped.js +0 -144
@@ -1,228 +1,104 @@
1
1
  import { action } from "../../../core/action.js";
2
- import { validateOptionObject } from "../../../core/validation.js";
3
- import { resolvePlotGraphicPlacement } from
4
- "../../../materialization/graphicHierarchy.js";
5
- import { DEFAULT_COLORS, DEFAULT_FONT_FAMILY } from
6
- "../../../theme/defaults.js";
7
- import {
8
- requireParallelAxisLayer,
9
- resolveParallelAxisTarget,
10
- resolveParallelAxisValues
11
- } from "./parallel/resolve.js";
2
+ import { validateGeneratedItemLimit, validateOptionObject } from "../../../core/validation.js";
3
+ import { resolvePlotGraphicPlacement } from "../../../materialization/graphicHierarchy.js";
4
+ import { requireParallelAxisLayer, resolveParallelAxisTarget, resolveStyledParallelAxes } from "./parallel/resolve.js";
5
+ import { PARALLEL_AXIS_GRAPHICS, PARALLEL_AXIS_PARTS, hasParallelAxisParts } from "./parallel/policy.js";
6
+ import { registerParallelAxisLifecycleActions } from "./parallel/lifecycle.js";
12
7
 
13
- const CREATE_OPTIONS = Object.freeze(["target", "coordinate"]);
14
- const REMATERIALIZE_OPTIONS = Object.freeze(["target"]);
8
+ function uniform(values) {
9
+ return values.length > 0 && values.every(value => value === values[0]) ? values[0] : values;
10
+ }
15
11
 
16
- export const rematerializeParallelAxes = action(
17
- {
18
- op: "rematerializeParallelAxes",
19
- description: "Recompute concrete Parallel dimension axes."
20
- },
21
- function (args = {}) {
22
- validateOptionObject(
23
- args,
24
- REMATERIALIZE_OPTIONS,
25
- "rematerializeParallelAxes"
26
- );
27
- const target = resolveParallelAxisTarget(
28
- this,
29
- args.target ?? this.guideConfigs.axis?.parallel?.axes?.target
30
- );
31
- const { dimensions } = requireParallelAxisLayer(this, target);
32
- const { axes, bounds } = resolveParallelAxisValues(this, dimensions);
33
- const ticks = axes.flatMap(axis => axis.values.map((value, index) => ({
34
- x: axis.x,
35
- y: axis.y[index],
36
- value,
37
- text: axis.labels[index]
38
- })));
39
- let next = this
40
- .editGraphics({
41
- target: "parallelAxisLines",
42
- property: "length",
43
- value: axes.length
44
- })
45
- .editGraphics({
46
- target: "parallelAxisLines",
47
- property: "x1",
48
- value: axes.map(axis => axis.x)
49
- })
50
- .editGraphics({ target: "parallelAxisLines", property: "y1", value: bounds.y })
51
- .editGraphics({
52
- target: "parallelAxisLines",
53
- property: "x2",
54
- value: axes.map(axis => axis.x)
55
- })
56
- .editGraphics({
57
- target: "parallelAxisLines",
58
- property: "y2",
59
- value: bounds.y + bounds.height
60
- })
61
- .editGraphics({
62
- target: "parallelAxisLines",
63
- property: "stroke",
64
- value: DEFAULT_COLORS.axis
65
- })
66
- .editGraphics({
67
- target: "parallelAxisLines",
68
- property: "strokeWidth",
69
- value: 1.25
70
- })
71
- .editGraphics({
72
- target: "parallelAxisTicks",
73
- property: "length",
74
- value: ticks.length
75
- })
76
- .editGraphics({
77
- target: "parallelAxisTicks",
78
- property: "x1",
79
- value: ticks.map(tick => tick.x - 4)
80
- })
81
- .editGraphics({
82
- target: "parallelAxisTicks",
83
- property: "y1",
84
- value: ticks.map(tick => tick.y)
85
- })
86
- .editGraphics({
87
- target: "parallelAxisTicks",
88
- property: "x2",
89
- value: ticks.map(tick => tick.x + 4)
90
- })
91
- .editGraphics({
92
- target: "parallelAxisTicks",
93
- property: "y2",
94
- value: ticks.map(tick => tick.y)
95
- })
96
- .editGraphics({
97
- target: "parallelAxisTicks",
98
- property: "stroke",
99
- value: DEFAULT_COLORS.mutedText
100
- })
101
- .editGraphics({
102
- target: "parallelAxisTicks",
103
- property: "strokeWidth",
104
- value: 1
105
- })
106
- .editGraphics({
107
- target: "parallelAxisLabels",
108
- property: "length",
109
- value: ticks.length
110
- })
111
- .editGraphics({
112
- target: "parallelAxisLabels",
113
- property: "x",
114
- value: ticks.map(tick => tick.x - 9)
115
- })
116
- .editGraphics({
117
- target: "parallelAxisLabels",
118
- property: "y",
119
- value: ticks.map(tick => tick.y)
120
- })
121
- .editGraphics({
122
- target: "parallelAxisLabels",
123
- property: "text",
124
- value: ticks.map(tick => tick.text)
125
- })
126
- .editGraphics({ target: "parallelAxisLabels", property: "fill", value: DEFAULT_COLORS.axis })
127
- .editGraphics({ target: "parallelAxisLabels", property: "fontSize", value: 11 })
128
- .editGraphics({ target: "parallelAxisLabels", property: "fontFamily", value: DEFAULT_FONT_FAMILY })
129
- .editGraphics({ target: "parallelAxisLabels", property: "fontWeight", value: "normal" })
130
- .editGraphics({ target: "parallelAxisLabels", property: "textAlign", value: "right" })
131
- .editGraphics({ target: "parallelAxisLabels", property: "textBaseline", value: "middle" })
132
- .editGraphics({
133
- target: "parallelAxisTitles",
134
- property: "length",
135
- value: axes.length
136
- })
137
- .editGraphics({
138
- target: "parallelAxisTitles",
139
- property: "x",
140
- value: axes.map(axis => axis.x)
141
- })
142
- .editGraphics({
143
- target: "parallelAxisTitles",
144
- property: "y",
145
- value: bounds.y - 20
146
- })
147
- .editGraphics({
148
- target: "parallelAxisTitles",
149
- property: "text",
150
- value: axes.map(axis => axis.title)
151
- })
152
- .editGraphics({ target: "parallelAxisTitles", property: "fill", value: DEFAULT_COLORS.axisTitle })
153
- .editGraphics({ target: "parallelAxisTitles", property: "fontSize", value: 13 })
154
- .editGraphics({ target: "parallelAxisTitles", property: "fontFamily", value: DEFAULT_FONT_FAMILY })
155
- .editGraphics({ target: "parallelAxisTitles", property: "fontWeight", value: 600 })
156
- .editGraphics({ target: "parallelAxisTitles", property: "textAlign", value: "center" })
157
- .editGraphics({ target: "parallelAxisTitles", property: "textBaseline", value: "middle" });
158
- return next._withGuideConfig("parallel", "axes", {
159
- target,
160
- scales: dimensions.map(dimension => dimension.scale)
161
- });
162
- }
163
- );
12
+ function collectionProperties(part, axes, bounds) {
13
+ if (part === "line") return {
14
+ length: axes.length,
15
+ x1: axes.map(axis => axis.x), y1: bounds.y,
16
+ x2: axes.map(axis => axis.x), y2: bounds.y + bounds.height,
17
+ stroke: uniform(axes.map(axis => axis.config.line.color)),
18
+ strokeWidth: uniform(axes.map(axis => axis.config.line.lineWidth))
19
+ };
20
+ if (part === "title") return {
21
+ length: axes.length, x: axes.map(axis => axis.x),
22
+ y: uniform(axes.map(axis => bounds.y - axis.config.title.offset)),
23
+ text: axes.map(axis => axis.title),
24
+ fill: uniform(axes.map(axis => axis.config.title.color)),
25
+ fontSize: uniform(axes.map(axis => axis.config.title.fontSize)),
26
+ fontFamily: uniform(axes.map(axis => axis.config.title.fontFamily)),
27
+ fontWeight: uniform(axes.map(axis => axis.config.title.fontWeight)),
28
+ textAlign: "center", textBaseline: "middle"
29
+ };
30
+ const rows = axes.flatMap(axis => axis[part].values.map((value, index) => ({
31
+ x: axis.x, y: axis[part].y[index], text: axis[part].text?.[index], config: axis.config[part]
32
+ })));
33
+ if (part === "ticks") return {
34
+ length: rows.length, x1: rows.map(row => row.x - row.config.length / 2),
35
+ y1: rows.map(row => row.y), x2: rows.map(row => row.x + row.config.length / 2),
36
+ y2: rows.map(row => row.y), stroke: uniform(rows.map(row => row.config.color)),
37
+ strokeWidth: uniform(rows.map(row => row.config.lineWidth))
38
+ };
39
+ return {
40
+ length: rows.length, x: rows.map(row => row.x - row.config.offset),
41
+ y: rows.map(row => row.y), text: rows.map(row => row.text),
42
+ fill: uniform(rows.map(row => row.config.color)),
43
+ fontSize: uniform(rows.map(row => row.config.fontSize)),
44
+ fontFamily: uniform(rows.map(row => row.config.fontFamily)),
45
+ fontWeight: uniform(rows.map(row => row.config.fontWeight)),
46
+ textAlign: "right", textBaseline: "middle"
47
+ };
48
+ }
49
+
50
+ export const rematerializeParallelAxes = action({
51
+ op: "rematerializeParallelAxes",
52
+ description: "Recompute concrete Parallel dimension axes."
53
+ }, function (args = {}) {
54
+ validateOptionObject(args, ["target"], "rematerializeParallelAxes");
55
+ const stored = this.semanticSpec.guides.axis?.parallel;
56
+ if (stored === undefined) throw new Error("rematerializeParallelAxes requires existing Parallel axes.");
57
+ const target = resolveParallelAxisTarget(this, args.target ?? stored.target);
58
+ if (target !== stored.target) throw new Error("Parallel axes belong to another target.");
59
+ const { dimensions } = requireParallelAxisLayer(this, target);
60
+ const { axes, bounds, configs } = resolveStyledParallelAxes(this, dimensions);
61
+ if (!configs.dimensions.some(hasParallelAxisParts)) return this.removeParallelAxes({ target });
164
62
 
165
- export const createParallelAxes = action(
166
- {
167
- op: "createParallelAxes",
168
- description: "Create ordinary line and text graphics for Parallel dimensions."
169
- },
170
- function (args = {}) {
171
- validateOptionObject(args, CREATE_OPTIONS, "createParallelAxes");
172
- const target = resolveParallelAxisTarget(this, args.target);
173
- const { coordinate, dimensions } = requireParallelAxisLayer(this, target);
174
- if (args.coordinate !== undefined && args.coordinate !== coordinate.id) {
175
- throw new Error(
176
- `Parallel layer "${target}" uses coordinate "${coordinate.id}".`
177
- );
63
+ // Preflight every collection before authoring any concrete graphic.
64
+ const properties = Object.fromEntries(PARALLEL_AXIS_PARTS.map(part => {
65
+ const selected = axes.filter(axis => axis.config[part] !== undefined);
66
+ const value = collectionProperties(part, selected, bounds);
67
+ validateGeneratedItemLimit(value.length, "Parallel axis item count");
68
+ return [part, { enabled: selected.length > 0, value }];
69
+ }));
70
+ let next = this;
71
+ const scales = dimensions.map(dimension => dimension.scale);
72
+ if (stored.scales?.length !== scales.length || scales.some((scale, index) => scale !== stored.scales[index])) {
73
+ next = next.editSemantic({ property: "guide.axis.parallel.scales", value: scales });
74
+ }
75
+ const retainedTitles = stored.titles?.filter(title =>
76
+ configs.dimensions.some(config => config.field === title.field && config.title !== undefined));
77
+ if (retainedTitles !== undefined && retainedTitles.length !== stored.titles.length) {
78
+ next = next.editSemantic({ property: "guide.axis.parallel.titles",
79
+ ...(retainedTitles.length === 0 ? { remove: true } : { value: retainedTitles }) });
80
+ }
81
+ for (const [index, part] of PARALLEL_AXIS_PARTS.entries()) {
82
+ const id = PARALLEL_AXIS_GRAPHICS[part];
83
+ const { enabled, value } = properties[part];
84
+ if (!enabled) {
85
+ if (next.graphicSpec.objects[id] !== undefined) next = next.editGraphics({ target: id, remove: true });
86
+ continue;
87
+ }
88
+ if (next.graphicSpec.objects[id] === undefined) {
89
+ const before = PARALLEL_AXIS_PARTS.slice(index + 1).map(key => PARALLEL_AXIS_GRAPHICS[key])
90
+ .find(key => next.graphicSpec.objects[key] !== undefined);
91
+ next = next.createGraphics({ id, type: part === "line" || part === "ticks" ? "line" : "text", length: 0,
92
+ ...resolvePlotGraphicPlacement(next, before === undefined ? {} : { before }) });
178
93
  }
179
- for (const id of [
180
- "parallelAxisLines", "parallelAxisTicks", "parallelAxisLabels",
181
- "parallelAxisTitles"
182
- ]) {
183
- if (this.graphicSpec.objects[id] !== undefined) {
184
- throw new Error("createParallelAxes requires missing Parallel axes.");
185
- }
94
+ for (const [property, item] of Object.entries(value)) {
95
+ next = next.editGraphics({ target: id, property, value: item });
186
96
  }
187
- const placement = resolvePlotGraphicPlacement(this);
188
- let next = this
189
- .editSemantic({
190
- property: "guide.axis.parallel.target",
191
- value: target
192
- })
193
- .editSemantic({
194
- property: "guide.axis.parallel.coordinate",
195
- value: coordinate.id
196
- })
197
- .editSemantic({
198
- property: "guide.axis.parallel.scales",
199
- value: dimensions.map(dimension => dimension.scale)
200
- })
201
- .createGraphics({ id: "parallelAxisLines", type: "line", length: 0, ...placement })
202
- .editGraphics({ target: "parallelAxisLines", property: "stroke", value: DEFAULT_COLORS.axis })
203
- .editGraphics({ target: "parallelAxisLines", property: "strokeWidth", value: 1.25 })
204
- .createGraphics({ id: "parallelAxisTicks", type: "line", length: 0, ...placement })
205
- .editGraphics({ target: "parallelAxisTicks", property: "stroke", value: DEFAULT_COLORS.mutedText })
206
- .editGraphics({ target: "parallelAxisTicks", property: "strokeWidth", value: 1 })
207
- .createGraphics({ id: "parallelAxisLabels", type: "text", length: 0, ...placement })
208
- .editGraphics({ target: "parallelAxisLabels", property: "fill", value: DEFAULT_COLORS.axis })
209
- .editGraphics({ target: "parallelAxisLabels", property: "fontSize", value: 11 })
210
- .editGraphics({ target: "parallelAxisLabels", property: "fontFamily", value: DEFAULT_FONT_FAMILY })
211
- .editGraphics({ target: "parallelAxisLabels", property: "fontWeight", value: "normal" })
212
- .editGraphics({ target: "parallelAxisLabels", property: "textAlign", value: "right" })
213
- .editGraphics({ target: "parallelAxisLabels", property: "textBaseline", value: "middle" })
214
- .createGraphics({ id: "parallelAxisTitles", type: "text", length: 0, ...placement })
215
- .editGraphics({ target: "parallelAxisTitles", property: "fill", value: DEFAULT_COLORS.axisTitle })
216
- .editGraphics({ target: "parallelAxisTitles", property: "fontSize", value: 13 })
217
- .editGraphics({ target: "parallelAxisTitles", property: "fontFamily", value: DEFAULT_FONT_FAMILY })
218
- .editGraphics({ target: "parallelAxisTitles", property: "fontWeight", value: 600 })
219
- .editGraphics({ target: "parallelAxisTitles", property: "textAlign", value: "center" })
220
- .editGraphics({ target: "parallelAxisTitles", property: "textBaseline", value: "middle" });
221
- return next.rematerializeParallelAxes({ target });
222
97
  }
223
- );
98
+ return next._withGuideConfig("parallel", "axes", { target, scales, ...configs });
99
+ });
224
100
 
225
101
  export function registerParallelAxisActions(ProgramClass) {
226
- ProgramClass.prototype.createParallelAxes = createParallelAxes;
227
102
  ProgramClass.prototype.rematerializeParallelAxes = rematerializeParallelAxes;
103
+ registerParallelAxisLifecycleActions(ProgramClass);
228
104
  }
@@ -1,32 +1,15 @@
1
1
  import { formatVisibleText } from "../../../core/textMetrics.js";
2
+ import {
3
+ formatValue,
4
+ isUtcValueFormat,
5
+ validateValueFormat
6
+ } from "../../../grammar/valueFormat.js";
2
7
 
3
8
  const AXIS_POSITIONS = Object.freeze({
4
9
  x: Object.freeze(["bottom", "top"]),
5
10
  y: Object.freeze(["left", "right"])
6
11
  });
7
12
 
8
- const NUMERIC_FORMATS = new Set([
9
- ".0f", ".1f", ".2f", ".0%", ".1%", ".2e"
10
- ]);
11
- const TIME_DIRECTIVES = new Set(["Y", "m", "d", "b", "%"]);
12
- const MONTH_NAMES = Object.freeze([
13
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
14
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
15
- ]);
16
-
17
- function isTimeFormat(format) {
18
- if (typeof format !== "string") return false;
19
- let directives = 0;
20
- for (let index = 0; index < format.length; index += 1) {
21
- if (format[index] !== "%") continue;
22
- const directive = format[index + 1];
23
- if (!TIME_DIRECTIVES.has(directive)) return false;
24
- if (directive !== "%") directives += 1;
25
- index += 1;
26
- }
27
- return directives > 0;
28
- }
29
-
30
13
  export function defaultAxisPosition(channel) {
31
14
  return channel === "x" ? "bottom" : "left";
32
15
  }
@@ -113,42 +96,9 @@ export function resolveAxisTitleGeometry({
113
96
  }
114
97
 
115
98
  export function validateAxisFormat(format) {
116
- if (format === "auto") return format;
117
- if (
118
- format !== null &&
119
- typeof format === "object" &&
120
- Number.isInteger(format.decimals) &&
121
- format.decimals >= 0 &&
122
- Object.keys(format).length === 1
123
- ) {
124
- return format;
125
- }
126
- if (typeof format === "string" && (
127
- NUMERIC_FORMATS.has(format) || isTimeFormat(format)
128
- )) {
129
- return format;
130
- }
131
- throw new TypeError(
132
- "Label format must be auto, { decimals }, or a supported format string."
133
- );
134
- }
135
-
136
- function pad(value, length = 2) {
137
- return String(value).padStart(length, "0");
138
- }
139
-
140
- function formatTime(value, format) {
141
- const date = new Date(value);
142
- if (!Number.isFinite(date.getTime())) {
143
- throw new TypeError("Time axis format requires valid timestamps.");
144
- }
145
- const year = pad(date.getUTCFullYear(), 4);
146
- const month = pad(date.getUTCMonth() + 1);
147
- const day = pad(date.getUTCDate());
148
- const values = { Y: year, m: month, d: day, b: MONTH_NAMES[date.getUTCMonth()] };
149
- return format.replace(/%([Ymdb%])/g, (_, directive) =>
150
- directive === "%" ? "%" : values[directive]
151
- );
99
+ return validateValueFormat(format, "Label format", {
100
+ allowDecimalsObject: true
101
+ });
152
102
  }
153
103
 
154
104
  export function formatAxisValue(value, scaleType, format, autoFormatter) {
@@ -158,18 +108,22 @@ export function formatAxisValue(value, scaleType, format, autoFormatter) {
158
108
  throw new Error('Discrete axis labels require format "auto".');
159
109
  }
160
110
  if (scaleType === "time") {
161
- if (!isTimeFormat(format)) {
111
+ if (!isUtcValueFormat(format)) {
162
112
  throw new Error("Time axis labels require a supported time format string.");
163
113
  }
164
- return formatTime(value, format);
114
+ return formatValue(value, {
115
+ format,
116
+ valueType: "temporal",
117
+ label: "Axis label format"
118
+ });
165
119
  }
166
- if (isTimeFormat(format)) {
120
+ if (isUtcValueFormat(format)) {
167
121
  throw new Error("Quantitative axis labels cannot use a time format string.");
168
122
  }
169
- if (typeof format === "object") return value.toFixed(format.decimals);
170
- if (format.endsWith("f")) return value.toFixed(Number(format[1]));
171
- if (format.endsWith("%")) {
172
- return `${(value * 100).toFixed(Number(format[1]))}%`;
173
- }
174
- return value.toExponential(2);
123
+ return formatValue(value, {
124
+ format,
125
+ valueType: "quantitative",
126
+ label: "Axis label format",
127
+ allowDecimalsObject: true
128
+ });
175
129
  }
@@ -1,3 +1,4 @@
1
+ import { withGuideLayoutValidation } from "../../../materialization/guides/layout.js";
1
2
  import { action } from "../../../core/action.js";
2
3
  import { isPlainObject } from "../../../core/immutable.js";
3
4
  import { validateKeys, validateOptionObject } from "../../../core/validation.js";
@@ -24,7 +25,12 @@ const LABEL_OPTIONS = Object.freeze([
24
25
  "color",
25
26
  "fontSize",
26
27
  "fontFamily",
27
- "fontWeight"
28
+ "fontWeight",
29
+ "rotation",
30
+ "maxWidth",
31
+ "wrap",
32
+ "lineHeight",
33
+ "overlap"
28
34
  ]);
29
35
  const SHARED_CREATE = Object.freeze(["scale", "position", "count", "values"]);
30
36
  const SHARED_EDIT = Object.freeze(["position", "count", "values"]);
@@ -37,7 +43,7 @@ function validateNested(value, supported, label) {
37
43
  validateKeys(value, supported, label);
38
44
  }
39
45
 
40
- function validateArgs(args, operation, create) {
46
+ export function validateAxisTickGroupArgs(args, operation, create) {
41
47
  validateOptionObject(
42
48
  args,
43
49
  create ? CREATE_OPTIONS : EDIT_OPTIONS,
@@ -89,8 +95,8 @@ function makeCreate(channel) {
89
95
  op: operation.create,
90
96
  description: `Create ${channel}-axis ticks and labels.`
91
97
  },
92
- function (args = {}) {
93
- validateArgs(args, operation.create, true);
98
+ withGuideLayoutValidation(function (args = {}) {
99
+ validateAxisTickGroupArgs(args, operation.create, true);
94
100
  const shared = select(args, SHARED_CREATE);
95
101
  const tickArgs = { ...shared, ...(args.ticks ?? {}) };
96
102
  const labelArgs = {
@@ -101,7 +107,7 @@ function makeCreate(channel) {
101
107
  return this[operation.createTicks](tickArgs)[operation.createLabels](
102
108
  labelArgs
103
109
  );
104
- }
110
+ })
105
111
  );
106
112
  }
107
113
 
@@ -113,8 +119,8 @@ function makeEdit(channel) {
113
119
  op: operation.edit,
114
120
  description: `Edit ${channel}-axis ticks and labels.`
115
121
  },
116
- function (args = {}) {
117
- validateArgs(args, operation.edit, false);
122
+ withGuideLayoutValidation(function (args = {}) {
123
+ validateAxisTickGroupArgs(args, operation.edit, false);
118
124
  const shared = select(args, SHARED_EDIT);
119
125
  const hasShared = Object.keys(shared).length > 0;
120
126
  let next = this;
@@ -134,7 +140,7 @@ function makeEdit(channel) {
134
140
  }
135
141
 
136
142
  return next;
137
- }
143
+ })
138
144
  );
139
145
  }
140
146
 
@@ -1,3 +1,4 @@
1
+ import { withGuideLayoutValidation } from "../../../materialization/guides/layout.js";
1
2
  import { action } from "../../../core/action.js";
2
3
  import { validateUserId } from "../../../core/identifiers.js";
3
4
  import {
@@ -126,7 +127,7 @@ function geometry(program, channel, config) {
126
127
 
127
128
  function makeEdit(channel) {
128
129
  const op = channel === "x" ? "editXAxisTicks" : "editYAxisTicks";
129
- return action({ op, description: `Edit concrete ${channel}-axis ticks.` }, function (args = {}) {
130
+ return action({ op, description: `Edit concrete ${channel}-axis ticks.` }, withGuideLayoutValidation(function (args = {}) {
130
131
  validateOptions(args, op, false);
131
132
  const id = `${channel}AxisTicks`;
132
133
  if (this.graphicSpec.objects[id]?.type !== "line") throw new Error(`${op} requires existing axis ticks.`);
@@ -157,7 +158,7 @@ function makeEdit(channel) {
157
158
  let next = this._withGuideConfig(channel, config).editGraphics({ target: id, property: "length", value: resolved.values.length });
158
159
  for (const property of ["x1", "y1", "x2", "y2"]) next = next.editGraphics({ target: id, property, value: resolved[property] });
159
160
  return next.editGraphics({ target: id, property: "stroke", value: config.color }).editGraphics({ target: id, property: "strokeWidth", value: config.lineWidth });
160
- });
161
+ }));
161
162
  }
162
163
 
163
164
  const editXAxisTicks = makeEdit("x"), editYAxisTicks = makeEdit("y");
@@ -165,7 +166,7 @@ const editXAxisTicks = makeEdit("x"), editYAxisTicks = makeEdit("y");
165
166
  function makeCreate(channel) {
166
167
  const op = channel === "x" ? "createXAxisTicks" : "createYAxisTicks";
167
168
  const edit = channel === "x" ? "editXAxisTicks" : "editYAxisTicks";
168
- return action({ op, description: `Create concrete ${channel}-axis ticks.` }, function (args = {}) {
169
+ return action({ op, description: `Create concrete ${channel}-axis ticks.` }, withGuideLayoutValidation(function (args = {}) {
169
170
  validateOptions(args, op, true);
170
171
  const scale = validateUserId(args.scale ?? channel, "Scale id");
171
172
  const existingGuide = this.semanticSpec.guides.axis?.[channel]?.scale;
@@ -192,7 +193,7 @@ function makeCreate(channel) {
192
193
  ...resolvePlotGraphicPlacement(this)
193
194
  })
194
195
  ._withGuideConfig(channel, config)[edit]();
195
- });
196
+ }));
196
197
  }
197
198
 
198
199
  const createXAxisTicks = makeCreate("x"), createYAxisTicks = makeCreate("y");
@@ -1,3 +1,5 @@
1
+ import { isSourceOwnedText } from "../../../grammar/text.js";
2
+ import { withGuideLayoutValidation } from "../../../materialization/guides/layout.js";
1
3
  import { action } from "../../../core/action.js";
2
4
  import { validateUserId } from "../../../core/identifiers.js";
3
5
  import {
@@ -32,6 +34,7 @@ import {
32
34
  import { resolveConcreteGraphicBounds } from
33
35
  "../../../grammar/schemas/graphicBounds.js";
34
36
  import { validateAxisTextStyle } from "./labels.js";
37
+ import { resolveRotation } from "../../../grammar/rotation.js";
35
38
 
36
39
  function titleBounds(geometry, config, text) {
37
40
  return resolveTextBounds({
@@ -102,7 +105,15 @@ export function inferAxisTitleText(program, channel, scaleId) {
102
105
  const titles = new Set();
103
106
  const primaryTitles = new Set();
104
107
  for (const layer of program.semanticSpec.layers) {
105
- const encoding = layer.encoding?.[channel];
108
+ if (isSourceOwnedText(layer)) continue;
109
+ const primary = layer.encoding?.[channel];
110
+ const encoding = layer.mark?.type === "area" && Object.hasOwn(primary ?? {}, "datum")
111
+ ? layer.encoding?.[`${channel}2`] : primary;
112
+ if (channel === "radius" && layer.mark?.type === "arc" &&
113
+ encoding?.scale === scaleId && encoding.aggregate === "count" && encoding.field === undefined) {
114
+ titles.add(encoding.title ?? "count");
115
+ continue;
116
+ }
106
117
  if (
107
118
  encoding?.scale === scaleId &&
108
119
  typeof encoding.field === "string" &&
@@ -193,12 +204,12 @@ function names(channel) {
193
204
 
194
205
  function makeEdit(channel) {
195
206
  const operation = names(channel);
196
- return action({ op: operation.edit, description: `Edit the ${channel}-axis title.` }, function (args = {}) {
207
+ return action({ op: operation.edit, description: `Edit the ${channel}-axis title.` }, withGuideLayoutValidation(function (args = {}) {
197
208
  validateKeys(args, EDIT_OPTIONS, operation.edit);
198
209
  if (this.graphicSpec.objects[operation.graphic]?.type !== "text") throw new Error(`${operation.edit} requires an existing axis title.`);
199
210
  const previous = this.guideConfigs.axis?.[channel]?.title;
200
211
  if (!previous) throw new Error(`${operation.edit} requires title configuration.`);
201
- const { text, ...appearance } = args;
212
+ const { text, rotation: requestedRotation, ...appearance } = args;
202
213
  const explicitText = Object.hasOwn(args, "text");
203
214
  const explicitRotation = Object.hasOwn(args, "rotation");
204
215
  const position = appearance.position ?? previous.position;
@@ -211,7 +222,9 @@ function makeEdit(channel) {
211
222
  position,
212
223
  rotation: inferredRotation
213
224
  ? defaultAxisTitleRotation(channel, position)
214
- : appearance.rotation ?? previous.rotation,
225
+ : explicitRotation
226
+ ? resolveRotation(requestedRotation, "Axis title rotation")
227
+ : previous.rotation,
215
228
  inferredRotation,
216
229
  inferredOffset: Object.hasOwn(args, "offset")
217
230
  ? false
@@ -245,7 +258,7 @@ function makeEdit(channel) {
245
258
  };
246
259
  for (const [property, value] of Object.entries(properties)) next = next.editGraphics({ target: operation.graphic, property, value });
247
260
  return next;
248
- });
261
+ }));
249
262
  }
250
263
 
251
264
  const editXAxisTitle = makeEdit("x");
@@ -253,7 +266,7 @@ const editYAxisTitle = makeEdit("y");
253
266
 
254
267
  function makeCreate(channel) {
255
268
  const operation = names(channel);
256
- return action({ op: operation.create, description: `Create the ${channel}-axis title.` }, function (args = {}) {
269
+ return action({ op: operation.create, description: `Create the ${channel}-axis title.` }, withGuideLayoutValidation(function (args = {}) {
257
270
  validateKeys(args, CREATE_OPTIONS, operation.create);
258
271
  const {
259
272
  text: requestedText,
@@ -281,7 +294,7 @@ function makeCreate(channel) {
281
294
  offset: channel === "x" ? 42 : 52,
282
295
  rotation: inferredRotation
283
296
  ? defaultAxisTitleRotation(channel, position)
284
- : requestedRotation,
297
+ : resolveRotation(requestedRotation, "Axis title rotation"),
285
298
  inferredRotation,
286
299
  inferredOffset,
287
300
  color: DEFAULTS.color,
@@ -303,7 +316,7 @@ function makeCreate(channel) {
303
316
  ...resolvePlotGraphicPlacement(this)
304
317
  })
305
318
  ._withGuideConfig(channel, "title", config)[operation.edit]();
306
- });
319
+ }));
307
320
  }
308
321
 
309
322
  const createXAxisTitle = makeCreate("x");
@@ -5,6 +5,7 @@ import {
5
5
  registerBasicCategoricalLegendActions
6
6
  } from "./legends/categorical/index.js";
7
7
  import {
8
+ registerIntervalLegendActions,
8
9
  registerGradientLegendActions
9
10
  } from "./legends/continuous/index.js";
10
11
  import { registerSizeLegendActions } from "./legends/size.js";
@@ -13,6 +14,7 @@ export function registerBasicGuideActions(ProgramClass) {
13
14
  registerBasicCartesianAxisActions(ProgramClass);
14
15
  registerBasicCategoricalLegendActions(ProgramClass);
15
16
  registerGradientLegendActions(ProgramClass);
17
+ registerIntervalLegendActions(ProgramClass);
16
18
  registerSizeLegendActions(ProgramClass);
17
19
  registerBasicGridActions(ProgramClass);
18
20
  registerGuideCollectionActions(ProgramClass);