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.
Files changed (359) hide show
  1. package/CHANGELOG.md +146 -0
  2. package/README.md +41 -12
  3. package/knowledge/action-cards.json +1 -1
  4. package/knowledge/intent-taxonomy.json +1 -1
  5. package/knowledge/mcp-resources.json +1 -1
  6. package/knowledge/task-packet.schema.json +1 -1
  7. package/knowledge/task-resolver.js +1 -1
  8. package/package.json +39 -7
  9. package/src/BasicChartProgram.js +3 -1
  10. package/src/ChartProgram.js +2 -0
  11. package/src/accessibility.js +157 -0
  12. package/src/actions/basic.js +2 -2
  13. package/src/actions/boxPlots/components.js +20 -12
  14. package/src/actions/boxPlots/create.js +4 -5
  15. package/src/actions/boxPlots/edit.js +34 -9
  16. package/src/actions/boxPlots/materialize.js +12 -4
  17. package/src/actions/boxPlots/options.js +48 -16
  18. package/src/actions/canvas/actions.js +3 -3
  19. package/src/actions/canvas/fitting.js +1 -1
  20. package/src/actions/categoryOrder/index.js +21 -12
  21. package/src/actions/charts/area.js +6 -2
  22. package/src/actions/charts/bar.js +3 -2
  23. package/src/actions/charts/beeswarm.js +1 -1
  24. package/src/actions/charts/density.js +7 -3
  25. package/src/actions/charts/ecdf.js +6 -3
  26. package/src/actions/charts/endpoints.js +29 -7
  27. package/src/actions/charts/heatmap.js +3 -2
  28. package/src/actions/charts/histogram.js +5 -4
  29. package/src/actions/charts/horizon.js +4 -2
  30. package/src/actions/charts/interval-regression.js +7 -4
  31. package/src/actions/charts/line.js +4 -2
  32. package/src/actions/charts/parallel.js +9 -3
  33. package/src/actions/charts/pie.js +11 -3
  34. package/src/actions/charts/polar.js +17 -6
  35. package/src/actions/charts/radar.js +9 -3
  36. package/src/actions/charts/radial.js +12 -4
  37. package/src/actions/charts/raincloud.js +2 -2
  38. package/src/actions/charts/rug-strip.js +8 -4
  39. package/src/actions/charts/scatter.js +4 -2
  40. package/src/actions/composition/actions.js +29 -24
  41. package/src/actions/coordinates/actions.js +2 -2
  42. package/src/actions/coordinates/edit.js +76 -0
  43. package/src/actions/data/basicBin2d.js +4 -5
  44. package/src/actions/data/bin.js +3 -3
  45. package/src/actions/data/bin2d.js +63 -193
  46. package/src/actions/data/bin2dMaterialize.js +4 -5
  47. package/src/actions/data/box.js +4 -4
  48. package/src/actions/data/complete.js +26 -0
  49. package/src/actions/data/computed.js +4 -4
  50. package/src/actions/data/create.js +7 -14
  51. package/src/actions/data/density.js +15 -23
  52. package/src/actions/data/derived.js +57 -31
  53. package/src/actions/data/ecdf.js +2 -2
  54. package/src/actions/data/edit.js +551 -0
  55. package/src/actions/data/filter.js +28 -22
  56. package/src/actions/data/fold.js +2 -2
  57. package/src/actions/data/gradientProfile.js +2 -2
  58. package/src/actions/data/horizon.js +2 -2
  59. package/src/actions/data/impute.js +26 -0
  60. package/src/actions/data/index.js +62 -47
  61. package/src/actions/data/interval.js +2 -2
  62. package/src/actions/data/intervalEdit.js +26 -18
  63. package/src/actions/data/normalize.js +27 -0
  64. package/src/actions/data/regression.js +21 -27
  65. package/src/actions/data/revise.js +48 -0
  66. package/src/actions/data/revisionBindings.js +125 -0
  67. package/src/actions/data/shared.js +20 -4
  68. package/src/actions/data/stack.js +2 -2
  69. package/src/actions/data/summary.js +3 -3
  70. package/src/actions/data/timeUnit.js +6 -3
  71. package/src/actions/data/window.js +3 -3
  72. package/src/actions/encodings/angle.js +1 -1
  73. package/src/actions/encodings/appearance.js +4 -4
  74. package/src/actions/encodings/barWidth.js +1 -1
  75. package/src/actions/encodings/channels.js +415 -0
  76. package/src/actions/encodings/color/index.js +3 -2
  77. package/src/actions/encodings/density.js +7 -4
  78. package/src/actions/encodings/histogram.js +4 -3
  79. package/src/actions/encodings/horizon.js +6 -8
  80. package/src/actions/encodings/index.js +4 -0
  81. package/src/actions/encodings/offset.js +22 -15
  82. package/src/actions/encodings/parallel.js +4 -5
  83. package/src/actions/encodings/pathOrder.js +6 -11
  84. package/src/actions/encodings/position/apply.js +53 -7
  85. package/src/actions/encodings/position/index.js +14 -6
  86. package/src/actions/encodings/position/policies/bar.js +9 -0
  87. package/src/actions/encodings/position/resolve.js +24 -7
  88. package/src/actions/encodings/ranged.js +5 -5
  89. package/src/actions/encodings/remove.js +13 -9
  90. package/src/actions/encodings/ruleAppearance.js +2 -24
  91. package/src/actions/encodings/seriesLayout.js +2 -1
  92. package/src/actions/encodings/shared.js +82 -14
  93. package/src/actions/encodings/stroke.js +226 -0
  94. package/src/actions/encodings/strokeDash.js +2 -2
  95. package/src/actions/encodings/text.js +1 -1
  96. package/src/actions/errorBands/create.js +34 -20
  97. package/src/actions/errorBands/edit.js +34 -137
  98. package/src/actions/errorBars/create.js +43 -20
  99. package/src/actions/errorBars/edit.js +29 -125
  100. package/src/actions/facets/actions.js +267 -73
  101. package/src/actions/facets/derive.js +99 -28
  102. package/src/actions/facets/guides.js +4 -5
  103. package/src/actions/facets/replay.js +4 -5
  104. package/src/actions/gradientPlots/components.js +15 -8
  105. package/src/actions/gradientPlots/create.js +4 -5
  106. package/src/actions/gradientPlots/edit.js +18 -9
  107. package/src/actions/gradientPlots/materialize.js +5 -3
  108. package/src/actions/gradientPlots/options.js +10 -2
  109. package/src/actions/gradientPlots/rebind.js +4 -5
  110. package/src/actions/guides/applicability.js +8 -2
  111. package/src/actions/guides/axes/axes.js +23 -4
  112. package/src/actions/guides/axes/axis.js +2 -2
  113. package/src/actions/guides/axes/edit.js +2 -2
  114. package/src/actions/guides/axes/labels.js +67 -21
  115. package/src/actions/guides/axes/lines.js +15 -9
  116. package/src/actions/guides/axes/parallel/lifecycle.js +13 -13
  117. package/src/actions/guides/axes/parallel/resolve.js +6 -2
  118. package/src/actions/guides/axes/parallel.js +5 -5
  119. package/src/actions/guides/axes/remove.js +4 -4
  120. package/src/actions/guides/axes/tickGroups.js +6 -5
  121. package/src/actions/guides/axes/ticks.js +19 -6
  122. package/src/actions/guides/axes/titles.js +21 -13
  123. package/src/actions/guides/grids/grid.js +18 -29
  124. package/src/actions/guides/grids/resolve.js +7 -2
  125. package/src/actions/guides/guides.js +20 -4
  126. package/src/actions/guides/legends/blocks.js +426 -0
  127. package/src/actions/guides/legends/categorical/actions.js +138 -52
  128. package/src/actions/guides/legends/categorical/components.js +55 -45
  129. package/src/actions/guides/legends/categorical/index.js +37 -33
  130. package/src/actions/guides/legends/categorical/layout.js +98 -16
  131. package/src/actions/guides/legends/categorical/options.js +2 -2
  132. package/src/actions/guides/legends/categorical/recipes.js +111 -20
  133. package/src/actions/guides/legends/categorical/resolve.js +12 -8
  134. package/src/actions/guides/legends/categorical/symbols.js +104 -52
  135. package/src/actions/guides/legends/continuous/common.js +76 -20
  136. package/src/actions/guides/legends/continuous/gradient.js +95 -158
  137. package/src/actions/guides/legends/continuous/index.js +2 -0
  138. package/src/actions/guides/legends/continuous/interval.js +21 -16
  139. package/src/actions/guides/legends/continuous/opacity.js +37 -83
  140. package/src/actions/guides/legends/continuous/stroke.js +225 -0
  141. package/src/actions/guides/legends/creation.js +14 -1
  142. package/src/actions/guides/legends/edit.js +159 -60
  143. package/src/actions/guides/legends/focused.js +5 -5
  144. package/src/actions/guides/legends/index.js +2 -0
  145. package/src/actions/guides/legends/lane.js +59 -24
  146. package/src/actions/guides/legends/lifecycle.js +19 -8
  147. package/src/actions/guides/legends/remove.js +17 -8
  148. package/src/actions/guides/legends/sampling.js +140 -0
  149. package/src/actions/guides/legends/size.js +100 -108
  150. package/src/actions/guides/legends/strokeWidth.js +38 -92
  151. package/src/actions/guides/legends/target.js +112 -1
  152. package/src/actions/guides/legends/transition.js +165 -16
  153. package/src/actions/guides/polar/axes/facade.js +8 -8
  154. package/src/actions/guides/polar/axes/labels.js +29 -10
  155. package/src/actions/guides/polar/axes/lines.js +19 -19
  156. package/src/actions/guides/polar/axes/shared.js +16 -0
  157. package/src/actions/guides/polar/axes/ticks.js +10 -16
  158. package/src/actions/guides/polar/axes/titles.js +6 -6
  159. package/src/actions/guides/polar/grids.js +12 -14
  160. package/src/actions/guides/polar/resolve.js +12 -8
  161. package/src/actions/index.js +16 -3
  162. package/src/actions/intervals/revision.js +126 -0
  163. package/src/actions/marks/arc/actions.js +80 -22
  164. package/src/actions/marks/area/actions.js +38 -13
  165. package/src/actions/marks/area/materialize.js +17 -1
  166. package/src/actions/marks/bar/create.js +12 -3
  167. package/src/actions/marks/bar/edit.js +21 -4
  168. package/src/actions/marks/bar/materialize.js +34 -40
  169. package/src/actions/marks/index.js +10 -1
  170. package/src/actions/marks/line/actions.js +40 -17
  171. package/src/actions/marks/line/materialize.js +29 -10
  172. package/src/actions/marks/point/create.js +9 -3
  173. package/src/actions/marks/point/edit.js +19 -7
  174. package/src/actions/marks/point/jitter.js +6 -8
  175. package/src/actions/marks/point/materialize.js +48 -22
  176. package/src/actions/marks/point/packing.js +6 -8
  177. package/src/actions/marks/rect/actions.js +36 -15
  178. package/src/actions/marks/references.js +602 -33
  179. package/src/actions/marks/remove.js +67 -41
  180. package/src/actions/marks/rule/actions.js +51 -14
  181. package/src/actions/marks/shared.js +1 -1
  182. package/src/actions/marks/text/actions.js +79 -14
  183. package/src/actions/marks/text/index.js +4 -0
  184. package/src/actions/marks/text/layout.js +157 -17
  185. package/src/actions/marks/text/removal.js +173 -0
  186. package/src/actions/marks/text/selection.js +72 -0
  187. package/src/actions/marks/tick/actions.js +25 -8
  188. package/src/actions/primitives/createGraphics.js +5 -4
  189. package/src/actions/primitives/editGraphics.js +31 -4
  190. package/src/actions/primitives/graphicProperties.js +6 -0
  191. package/src/actions/primitives/semantic.js +11 -3
  192. package/src/actions/primitives/semanticAction.js +90 -14
  193. package/src/actions/primitives/semanticValidation/dataset.js +2 -4
  194. package/src/actions/primitives/semanticValidation/guide.js +1 -1
  195. package/src/actions/primitives/semanticValidation/index.js +9 -1
  196. package/src/actions/primitives/semanticValidation/layer.js +5 -0
  197. package/src/actions/primitives/semanticValidation/scale.js +28 -7
  198. package/src/actions/regression/components.js +36 -25
  199. package/src/actions/regression/create.js +13 -6
  200. package/src/actions/regression/edit.js +24 -11
  201. package/src/actions/resources/index.js +7 -0
  202. package/src/actions/resources/remove.js +173 -0
  203. package/src/actions/scales/channels.js +212 -0
  204. package/src/actions/scales/consumers/common.js +1 -1
  205. package/src/actions/scales/consumers/families.js +3 -2
  206. package/src/actions/scales/consumers/index.js +19 -2
  207. package/src/actions/scales/consumers/seriesLayout.js +28 -14
  208. package/src/actions/scales/create.js +55 -23
  209. package/src/actions/scales/definitions.js +94 -40
  210. package/src/actions/scales/edit.js +67 -12
  211. package/src/actions/scales/editPolicy.js +81 -11
  212. package/src/actions/scales/index.js +6 -0
  213. package/src/actions/scales/materialize.js +5 -4
  214. package/src/actions/scales/offset.js +48 -0
  215. package/src/actions/scales/parallel.js +71 -0
  216. package/src/actions/scales/preview.js +111 -10
  217. package/src/actions/scales/quantitativeColor.js +4 -5
  218. package/src/actions/selection/actions.js +179 -66
  219. package/src/actions/selection/index.js +3 -0
  220. package/src/actions/textMetrics/index.js +56 -0
  221. package/src/actions/theme/actions.js +70 -15
  222. package/src/actions/theme/composition.js +238 -0
  223. package/src/actions/theme/index.js +9 -2
  224. package/src/actions/theme/reconcile.js +313 -121
  225. package/src/actions/theme/state.js +160 -0
  226. package/src/actions/titles/actions.js +8 -8
  227. package/src/actions/titles/resolve.js +3 -3
  228. package/src/actions/violinPlots/create.js +6 -3
  229. package/src/actions/violinPlots/edit.js +2 -1
  230. package/src/core/action.js +117 -35
  231. package/src/core/compositionState.js +6 -1
  232. package/src/core/diagnostics.js +16 -0
  233. package/src/core/extensionRegistry.js +27 -0
  234. package/src/core/font.js +18 -0
  235. package/src/core/identifiers.js +5 -4
  236. package/src/core/immutable.js +10 -3
  237. package/src/core/optionRequirements.js +8 -0
  238. package/src/core/programState.js +21 -1
  239. package/src/core/resourceReferences.js +341 -0
  240. package/src/core/textMetricProfile.js +28 -0
  241. package/src/core/textMetrics.js +24 -3
  242. package/src/core/validation.js +22 -10
  243. package/src/core/vocabulary.js +16 -3
  244. package/src/diagnostics.js +1 -0
  245. package/src/grammar/bars/aggregate.js +4 -1
  246. package/src/grammar/bars/geometry.js +44 -0
  247. package/src/grammar/bin.js +54 -9
  248. package/src/grammar/complete.js +279 -0
  249. package/src/grammar/computed.js +265 -64
  250. package/src/grammar/coordinates.js +37 -0
  251. package/src/grammar/density.js +239 -37
  252. package/src/grammar/displayLabels.js +57 -0
  253. package/src/grammar/facets/dependencies.js +133 -1
  254. package/src/grammar/facets/guides.js +3 -0
  255. package/src/grammar/facets/index.js +7 -47
  256. package/src/grammar/facets/scales.js +15 -18
  257. package/src/grammar/impute.js +341 -0
  258. package/src/grammar/markLabelSelection.js +57 -0
  259. package/src/grammar/markLabels.js +18 -1
  260. package/src/grammar/normalize.js +366 -0
  261. package/src/grammar/pathSeries.js +26 -8
  262. package/src/grammar/pointShapes.js +13 -4
  263. package/src/grammar/polar.js +90 -8
  264. package/src/grammar/regression/index.js +1 -0
  265. package/src/grammar/regression/parameters.js +23 -0
  266. package/src/grammar/roundedRect.js +180 -0
  267. package/src/grammar/rules.js +1 -0
  268. package/src/grammar/scales/colorConsumers.js +20 -1
  269. package/src/grammar/scales/continuous.js +2 -2
  270. package/src/grammar/scales/definition.js +27 -4
  271. package/src/grammar/scales/index.js +1 -0
  272. package/src/grammar/scales/ordinal.js +12 -3
  273. package/src/grammar/scales/policies.js +2 -2
  274. package/src/grammar/scales/size.js +456 -0
  275. package/src/grammar/scales/types.js +8 -0
  276. package/src/grammar/schemas/concreteGraphic.js +14 -2
  277. package/src/grammar/schemas/graphic.js +15 -0
  278. package/src/grammar/schemas/graphicBounds.js +190 -41
  279. package/src/grammar/schemas/semanticPath.js +7 -2
  280. package/src/grammar/statisticalReference.js +103 -0
  281. package/src/grammar/strokeStyle.js +77 -0
  282. package/src/grammar/summary.js +78 -16
  283. package/src/grammar/timeUnit.js +216 -5
  284. package/src/grammar/transformTopology.js +4 -0
  285. package/src/grammar/transforms.js +394 -28
  286. package/src/grammar/weightedStatistics.js +374 -0
  287. package/src/grammar/window.js +221 -16
  288. package/src/layout/aspect.js +80 -0
  289. package/src/layout/facets.js +59 -5
  290. package/src/layout/labels.js +351 -7
  291. package/src/layout/legend.js +4 -4
  292. package/src/layout/legendItems.js +6 -6
  293. package/src/layout/text.js +10 -9
  294. package/src/layout/title.js +9 -9
  295. package/src/materialization/bars/aggregate.js +25 -2
  296. package/src/materialization/bars/histogram.js +84 -24
  297. package/src/materialization/bars/ranged.js +19 -3
  298. package/src/materialization/bars/resolve.js +7 -4
  299. package/src/materialization/composition.js +3 -1
  300. package/src/materialization/coordinateBounds.js +204 -0
  301. package/src/materialization/dependencies.js +36 -0
  302. package/src/materialization/facetGuides/legacyCategorical.js +3 -0
  303. package/src/materialization/facetGuides/placement.js +9 -3
  304. package/src/materialization/facetGuides/preparation.js +13 -8
  305. package/src/materialization/facetHeaders.js +347 -0
  306. package/src/materialization/facets.js +15 -65
  307. package/src/materialization/guides/layout.js +3 -3
  308. package/src/materialization/guides/resources.js +26 -0
  309. package/src/materialization/legends.js +1 -1
  310. package/src/materialization/marks/index.js +11 -5
  311. package/src/materialization/marks/policies.js +2 -1
  312. package/src/materialization/rect.js +25 -12
  313. package/src/materialization/rectConfig.js +5 -1
  314. package/src/materialization/scaleGuideDependencies.js +10 -0
  315. package/src/materialization/scales/map.js +5 -3
  316. package/src/materialization/scales/policies/binnedPosition.js +2 -2
  317. package/src/materialization/scales/policies/offset.js +50 -8
  318. package/src/materialization/scales/policies/series.js +4 -1
  319. package/src/materialization/scales/resolve.js +22 -6
  320. package/src/materialization/selection/items/arc.js +28 -4
  321. package/src/materialization/selection/items/bar.js +30 -3
  322. package/src/materialization/selection/items/common.js +20 -5
  323. package/src/materialization/selection/items/rect.js +28 -4
  324. package/src/materialization/selection/path.js +1 -1
  325. package/src/materialization/selection/point.js +3 -1
  326. package/src/materialization/text.js +265 -14
  327. package/src/materialization/typography.js +31 -0
  328. package/src/mcp/cli.js +5 -3
  329. package/src/persistence/codec.js +65 -0
  330. package/src/persistence/validation.js +185 -0
  331. package/src/persistence.js +74 -0
  332. package/src/renderers/canvas/circle.js +2 -3
  333. package/src/renderers/canvas/index.js +5 -4
  334. package/src/renderers/canvas/line.js +2 -3
  335. package/src/renderers/canvas/native.js +4 -4
  336. package/src/renderers/canvas/path.js +2 -3
  337. package/src/renderers/canvas/rect.js +2 -3
  338. package/src/renderers/canvas/stroke.js +11 -0
  339. package/src/renderers/nativeBackend.js +10 -0
  340. package/src/renderers/options.js +13 -0
  341. package/src/renderers/pdf.js +25 -54
  342. package/src/renderers/png.js +22 -17
  343. package/src/renderers/svg.js +29 -14
  344. package/src/renderers/text.js +1 -11
  345. package/src/selectors/coordinates.js +3 -1
  346. package/src/selectors/datasets.js +38 -5
  347. package/src/selectors/layers.js +10 -4
  348. package/src/selectors/markOwners.js +46 -0
  349. package/src/selectors/scales.js +6 -4
  350. package/src/theme/defaults.js +103 -1
  351. package/src/version.js +2 -0
  352. package/types/accessibility.d.ts +37 -0
  353. package/types/basic.d.ts +10 -2
  354. package/types/diagnostics.d.ts +15 -0
  355. package/types/index.d.ts +90 -0
  356. package/types/pdf.d.ts +9 -0
  357. package/types/persistence.d.ts +7 -0
  358. package/types/png.d.ts +9 -0
  359. package/types/program.d.ts +944 -118
@@ -4,7 +4,6 @@ import { normalizeLegendOrder } from "../../../grammar/categoryOrder.js";
4
4
  import { resolveDefinition } from "./categorical/resolve.js";
5
5
  import { action } from "../../../core/action.js";
6
6
  import {
7
- validateGeneratedItemLimit,
8
7
  validateOptionObject
9
8
  } from "../../../core/validation.js";
10
9
  import { normalizeOptions } from "./categorical/options.js";
@@ -23,10 +22,17 @@ import {
23
22
  } from "./continuous/common.js";
24
23
  import { normalizeOpacitySymbol, createOpacityLegendFromConfig } from "./continuous/opacity.js";
25
24
  import { normalizeIntervalLegend, resolveIntervalConfig, resolveIntervalLayout, createIntervalLegendFromConfig } from "./continuous/interval.js";
25
+ import {
26
+ createStrokeGradientLegendFromConfig,
27
+ createStrokeIntervalLegendFromConfig,
28
+ resolveStrokeIntervalConfig,
29
+ resolveStrokeIntervalLayout
30
+ } from "./continuous/stroke.js";
26
31
  import { findLayer } from "../../../selectors/layers.js";
27
32
  import { resolveLegendGraphicPlacement } from
28
33
  "../../../materialization/graphicHierarchy.js";
29
34
  import { resolveLegendTarget, validateLegendChannels } from "./target.js";
35
+ import { isDiscreteSizeScaleType } from "../../../grammar/scales/index.js";
30
36
  import { SIZE_LEGEND_LABELS, SIZE_LEGEND_TITLE_STYLE, resolveSizeLegendLayout, createSizeLegendFromConfig } from "./size.js";
31
37
  import {
32
38
  STROKE_WIDTH_LEGEND_LABELS,
@@ -34,11 +40,13 @@ import {
34
40
  resolveStrokeWidthLegendLayout,
35
41
  createStrokeWidthLegendFromConfig
36
42
  } from "./strokeWidth.js";
43
+ import { normalizeLegendSampling } from "./sampling.js";
44
+ import { planLegendBlockTransitions } from "./transition.js";
37
45
 
38
46
  const OPTIONS = Object.freeze([
39
47
  "target", "channels", "position", "layout", "align", "direction", "columns", "offset",
40
48
  "titlePosition", "title", "symbol", "labels", "titleStyle", "itemGap",
41
- "border", "count", "gradient", "order"
49
+ "border", "count", "values", "gradient", "order"
42
50
  ]);
43
51
 
44
52
  function mergeObject(previous, patch) {
@@ -70,11 +78,12 @@ function reconcileGraphic(program, id, shouldExist, definition) {
70
78
  }
71
79
 
72
80
  function resolveContinuousEdit(program, kind, previous, args) {
73
- const allowed = kind === "gradient"
81
+ const gradient = ["gradient", "strokeGradient"].includes(kind);
82
+ const allowed = gradient
74
83
  ? ["target", "position", "align", "offset", "title", "labels",
75
84
  "titleStyle", "titlePosition", "border", "count", "gradient"]
76
85
  : ["target", "position", "align", "offset", "title", "symbol", "labels",
77
- "titleStyle", "titlePosition", "itemGap", "border", "count"];
86
+ "titleStyle", "titlePosition", "itemGap", "border", "count", "values"];
78
87
  for (const key of Object.keys(args)) {
79
88
  if (!allowed.includes(key)) throw new Error(`${kind} legend does not accept ${key}.`);
80
89
  }
@@ -85,7 +94,9 @@ function resolveContinuousEdit(program, kind, previous, args) {
85
94
  const titleVisible = titleMode === false ? false
86
95
  : titleMode === undefined ? previous.titleVisible !== false : true;
87
96
  const layer = findLayer(program, previous.target);
88
- const inferred = layer?.encoding?.[kind === "gradient" ? "color" : "opacity"]?.field;
97
+ const channel = kind === "gradient" ? "color"
98
+ : kind === "strokeGradient" ? "stroke" : "opacity";
99
+ const inferred = layer?.encoding?.[channel]?.field;
89
100
  const title = titleMode === "auto"
90
101
  ? inferred
91
102
  : typeof titleMode === "string" ? titleMode : previous.title;
@@ -93,12 +104,17 @@ function resolveContinuousEdit(program, kind, previous, args) {
93
104
  const enteringInline = kind === "opacity" &&
94
105
  titlePosition === "left" && previous.titlePosition !== "left";
95
106
  const labels = mergeObject(previous.labels, args.labels);
107
+ const sampling = gradient ? undefined : normalizeLegendSampling(args, {
108
+ previous,
109
+ operation: "edit",
110
+ label: "Opacity legend"
111
+ });
96
112
  const normalized = normalizeContinuousLegend({
97
113
  target: previous.target,
98
114
  position: args.position ?? previous.position,
99
115
  align: args.align ?? previous.align,
100
116
  offset: args.offset ?? previous.offset,
101
- count: args.count ?? previous.count,
117
+ ...(gradient ? { count: args.count ?? previous.count } : {}),
102
118
  titlePosition,
103
119
  title,
104
120
  labels: enteringInline && args.labels?.offset === undefined
@@ -110,14 +126,16 @@ function resolveContinuousEdit(program, kind, previous, args) {
110
126
  itemGap: args.itemGap ?? (enteringInline ? 20 : previous.itemGap),
111
127
  symbol: args.symbol ?? previous.symbol
112
128
  } : {})
113
- }, kind);
129
+ }, gradient ? "gradient" : kind);
130
+ const { count: _legacyCount, ...canonicalPrevious } = previous;
114
131
  const config = {
115
- ...previous,
132
+ ...(gradient ? previous : canonicalPrevious),
116
133
  ...normalized,
134
+ ...(gradient ? {} : { sampling }),
117
135
  inferredTitle,
118
136
  titleVisible
119
137
  };
120
- if (kind === "gradient") {
138
+ if (gradient) {
121
139
  const gradient = mergeObject(previous.gradient, args.gradient);
122
140
  validatePositive(gradient.length, "Gradient length");
123
141
  validatePositive(gradient.thickness, "Gradient thickness");
@@ -130,24 +148,29 @@ function resolveContinuousEdit(program, kind, previous, args) {
130
148
 
131
149
  function editContinuous(program, kind, previous, args) {
132
150
  const { config, titleMode, title, titleVisible } = resolveContinuousEdit(program, kind, previous, args);
133
- const prefix = kind === "gradient" ? "colorGradient" : "opacityLegend";
151
+ const prefix = kind === "gradient" ? "colorGradient"
152
+ : kind === "strokeGradient" ? "strokeGradient" : "opacityLegend";
153
+ const semanticKind = kind === "gradient" ? "color"
154
+ : kind === "strokeGradient" ? "stroke" : "opacity";
134
155
  let next = program;
135
156
  if (titleMode === "auto" || typeof titleMode === "string") {
136
157
  next = next.editSemantic({
137
- property: `guide.legend.${kind === "gradient" ? "color" : "opacity"}.title`,
158
+ property: `guide.legend.${semanticKind}.title`,
138
159
  value: title
139
160
  });
140
161
  }
141
162
  next = next._withLegendConfig(kind, config);
142
163
  next = reconcileGraphic(next, `${prefix}Background`, config.border !== false, {
143
164
  type: "rect",
144
- before: kind === "gradient" ? "colorGradientStrips" : "opacityLegendSymbols"
165
+ before: ["gradient", "strokeGradient"].includes(kind)
166
+ ? `${prefix}Strips`
167
+ : "opacityLegendSymbols"
145
168
  });
146
169
  next = reconcileGraphic(next, `${prefix}Title`, titleVisible, { type: "text" });
147
170
  return next.rematerializeLegend();
148
171
  }
149
172
 
150
- function resolveIntervalEdit(program, previous, args) {
173
+ function resolveIntervalEdit(program, previous, args, channel = "color") {
151
174
  for (const key of Object.keys(args)) {
152
175
  if (![
153
176
  "target", "position", "align", "direction", "offset", "title",
@@ -165,7 +188,7 @@ function resolveIntervalEdit(program, previous, args) {
165
188
  ? false
166
189
  : titleMode === undefined ? previous.titleVisible !== false : true;
167
190
  const title = titleMode === "auto"
168
- ? layer?.encoding?.color?.field
191
+ ? layer?.encoding?.[channel]?.field
169
192
  : typeof titleMode === "string" ? titleMode : previous.title;
170
193
  const normalized = normalizeIntervalLegend({
171
194
  target: previous.target,
@@ -184,29 +207,45 @@ function resolveIntervalEdit(program, previous, args) {
184
207
  itemGap: args.itemGap ?? previous.itemGap,
185
208
  border: mergeBorder(previous.border, args.border)
186
209
  });
187
- const config = { ...previous, ...normalized, inferredTitle, titleVisible };
188
- const resolved = resolveIntervalConfig(program, config);
189
- resolveIntervalLayout(program, resolved.config, resolved.scale);
210
+ const config = {
211
+ ...previous,
212
+ ...normalized,
213
+ inferredTitle,
214
+ titleVisible,
215
+ ...(channel === "stroke" && args.symbol?.strokeWidth !== undefined
216
+ ? { inferredStrokeWidth: false }
217
+ : {})
218
+ };
219
+ const resolved = channel === "stroke"
220
+ ? resolveStrokeIntervalConfig(program, config)
221
+ : resolveIntervalConfig(program, config);
222
+ (channel === "stroke" ? resolveStrokeIntervalLayout : resolveIntervalLayout)(
223
+ program,
224
+ resolved.config,
225
+ resolved.scale
226
+ );
190
227
  return { normalized, config, titleMode, title, titleVisible };
191
228
  }
192
229
 
193
- function editInterval(program, previous, args) {
194
- const { normalized, config, titleMode, title, titleVisible } = resolveIntervalEdit(program, previous, args);
195
- let next = program._withLegendConfig("interval", config);
230
+ function editInterval(program, previous, args, channel = "color") {
231
+ const kind = channel === "stroke" ? "strokeInterval" : "interval";
232
+ const prefix = channel === "stroke" ? "strokeInterval" : "colorLegend";
233
+ const { normalized, config, titleMode, title, titleVisible } = resolveIntervalEdit(program, previous, args, channel);
234
+ let next = program._withLegendConfig(kind, config);
196
235
  if (titleMode === "auto" || typeof titleMode === "string") {
197
236
  next = next.editSemantic({
198
- property: "guide.legend.color.title",
237
+ property: `guide.legend.${channel}.title`,
199
238
  value: title
200
239
  });
201
240
  }
202
- next = reconcileGraphic(next, "colorLegendTitle", titleVisible, {
241
+ next = reconcileGraphic(next, `${prefix}Title`, titleVisible, {
203
242
  type: "text"
204
243
  });
205
244
  next = reconcileGraphic(
206
245
  next,
207
- "colorLegendBackground",
246
+ `${prefix}Background`,
208
247
  normalized.border !== false,
209
- { type: "rect", before: "colorLegendSymbols" }
248
+ { type: "rect", before: `${prefix}Symbols` }
210
249
  );
211
250
  return next.rematerializeLegend();
212
251
  }
@@ -214,20 +253,23 @@ function editInterval(program, previous, args) {
214
253
  function resolveSampledLegendEdit(program, kind, previous, args) {
215
254
  const size = kind === "size";
216
255
  const label = size ? "size" : "stroke-width";
217
- const allowed = ["target", "title", "count", "labels", "titleStyle",
256
+ const allowed = ["target", "title", "count", "values", "labels", "titleStyle",
218
257
  "position", "layout", "align", "direction", "columns", "titlePosition", "offset", "itemGap", "border"];
219
258
  for (const key of Object.keys(args)) {
220
259
  if (!allowed.includes(key)) {
221
260
  throw new Error(`${label} legend does not accept ${key}.`);
222
261
  }
223
262
  }
224
- const count = args.count ?? previous.count;
225
- if (!Number.isInteger(count) || count < 2) {
226
- throw new RangeError(
227
- `${label} legend count must be an integer of at least 2.`
228
- );
263
+ const discrete = size &&
264
+ isDiscreteSizeScaleType(program.resolvedScales[previous.scale]?.type);
265
+ if (discrete && (args.count !== undefined || args.values !== undefined)) {
266
+ throw new Error("Discrete size legends do not support count or exact values.");
229
267
  }
230
- validateGeneratedItemLimit(count, `${label} legend count`);
268
+ const sampling = discrete ? undefined : normalizeLegendSampling(args, {
269
+ previous,
270
+ operation: "edit",
271
+ label: `${size ? "Size" : "Stroke-width"} legend`
272
+ });
231
273
  const layer = findLayer(program, previous.target);
232
274
  const titleMode = args.title;
233
275
  const inferredTitle = titleMode === "auto"
@@ -239,12 +281,14 @@ function resolveSampledLegendEdit(program, kind, previous, args) {
239
281
  const title = titleMode === "auto"
240
282
  ? layer?.encoding?.[kind]?.field
241
283
  : typeof titleMode === "string" ? titleMode : previous.title;
284
+ const { count: _legacyCount, ...continuousPrevious } = previous;
285
+ const canonicalPrevious = discrete ? previous : continuousPrevious;
242
286
  const config = {
243
- ...previous,
287
+ ...canonicalPrevious,
244
288
  title,
245
289
  inferredTitle,
246
290
  titleVisible,
247
- count,
291
+ ...(discrete ? {} : { sampling }),
248
292
  ...(size ? { inheritAppearance: false } : {}),
249
293
  labels: normalizeLegendTextOptions(
250
294
  args.labels,
@@ -285,9 +329,25 @@ function editSampledLegend(program, kind, previous, args) {
285
329
  return next.rematerializeLegend();
286
330
  }
287
331
 
288
- function resolveCompanionSizeEdit(previous, size, args) {
332
+ function resolveCompanionSizeEdit(program, previous, size, args) {
289
333
  if (size === undefined) return undefined;
290
- const config = { ...size, count: args.count ?? size.count };
334
+ const discrete = isDiscreteSizeScaleType(
335
+ program.resolvedScales[size.scale]?.type
336
+ );
337
+ if (discrete && (args.count !== undefined || args.values !== undefined)) {
338
+ throw new Error("Discrete size legends do not support count or exact values.");
339
+ }
340
+ const sampling = discrete ? undefined : normalizeLegendSampling(args, {
341
+ previous: size,
342
+ operation: "edit",
343
+ label: "Size legend"
344
+ });
345
+ const { count: _legacyCount, ...continuousSize } = size;
346
+ const canonicalSize = discrete ? size : continuousSize;
347
+ const config = {
348
+ ...canonicalSize,
349
+ ...(discrete ? {} : { sampling })
350
+ };
291
351
  if (args.labels === undefined && args.titleStyle === undefined) return config;
292
352
  const labels = size.inheritAppearance
293
353
  ? { ...previous.labels, offset: size.labels.offset } : size.labels ?? SIZE_LEGEND_LABELS;
@@ -299,6 +359,11 @@ function resolveCompanionSizeEdit(previous, size, args) {
299
359
  }
300
360
 
301
361
  function resolveCategoricalEdit(program, kind, previous, size, args, storedOrder = program.semanticSpec.guides.legend?.[kind]?.order) {
362
+ if (args.values !== undefined) {
363
+ throw new Error(
364
+ "Combined and categorical legends require a channel block selector for exact values."
365
+ );
366
+ }
302
367
  if (args.gradient !== undefined) {
303
368
  throw new Error("Categorical legends do not accept gradient.");
304
369
  }
@@ -319,6 +384,9 @@ function resolveCategoricalEdit(program, kind, previous, size, args, storedOrder
319
384
  throw new Error("Categorical legend labels do not accept format.");
320
385
  }
321
386
  const { format: _sizeFormat, ...categoricalLabels } = mergedLabels;
387
+ const inferredSymbol = args.symbol === undefined
388
+ ? previous.inferredSymbol
389
+ : args.symbol === "auto";
322
390
  const normalized = normalizeOptions({
323
391
  target: previous.target,
324
392
  channels: previous.channels,
@@ -333,17 +401,23 @@ function resolveCategoricalEdit(program, kind, previous, size, args, storedOrder
333
401
  titlePosition: args.titlePosition ?? previous.titlePosition,
334
402
  title,
335
403
  symbol: args.symbol === undefined ? previous.symbol
336
- : resolveLegendSymbol(program, findLayer(program, previous.target), previous.channels, args.symbol),
404
+ : resolveLegendSymbol(
405
+ program,
406
+ findLayer(program, previous.target),
407
+ previous.channels,
408
+ args.symbol,
409
+ kind
410
+ ),
337
411
  labels: categoricalLabels,
338
412
  titleStyle: mergeObject(previous.titleStyle, args.titleStyle),
339
413
  itemGap: args.itemGap ?? previous.itemGap,
340
414
  border: mergeBorder(previous.border, args.border)
341
- }, kind);
415
+ }, kind, { internalSymbol: inferredSymbol });
342
416
  const config = {
343
417
  ...previous,
344
418
  ...normalized,
345
419
  inferredTitle,
346
- inferredSymbol: args.symbol === undefined ? previous.inferredSymbol : args.symbol === "auto",
420
+ inferredSymbol,
347
421
  titleVisible
348
422
  };
349
423
  const order = args.order === undefined
@@ -351,13 +425,7 @@ function resolveCategoricalEdit(program, kind, previous, size, args, storedOrder
351
425
  : normalizeLegendOrder(args.order);
352
426
  // Resolve the final domain before changing semantic/config/graphic state.
353
427
  resolveDefinition(program, findLayer(program, previous.target), previous.channels, title, order);
354
- if (args.count !== undefined) {
355
- if (!Number.isInteger(args.count) || args.count < 2) {
356
- throw new RangeError("Size legend count must be an integer of at least 2.");
357
- }
358
- validateGeneratedItemLimit(args.count, "Size legend count");
359
- }
360
- return { config, order, titleMode, title, titleVisible, sizeConfig: resolveCompanionSizeEdit(previous, size, args) };
428
+ return { config, order, titleMode, title, titleVisible, sizeConfig: resolveCompanionSizeEdit(program, previous, size, args) };
361
429
  }
362
430
 
363
431
  function editCategorical(program, kind, previous, size, args) {
@@ -381,12 +449,12 @@ function editCategorical(program, kind, previous, size, args) {
381
449
  }
382
450
  next = next._withLegendConfig(kind, config);
383
451
  next = reconcileCategoricalSymbols(next, previous, config);
384
- next = reconcileGraphic(next, `${kind === "series" ? "series" : "color"}LegendBackground`,
452
+ next = reconcileGraphic(next, `${kind}LegendBackground`,
385
453
  config.border !== false, {
386
454
  type: "rect",
387
455
  before: [...newSymbols][0]
388
456
  });
389
- const titleId = `${kind === "series" ? "series" : "color"}LegendTitle`;
457
+ const titleId = `${kind}LegendTitle`;
390
458
  next = reconcileGraphic(next, titleId, titleVisible, {
391
459
  type: "text",
392
460
  ...(next.graphicSpec.objects.sizeLegendSymbols === undefined
@@ -407,16 +475,16 @@ function editLegendContent(program, target, args) {
407
475
  const removed = Object.keys(previous).filter(kind => previous[kind].target === target);
408
476
  let view = removed.reduce((next, kind) => next._withoutMaterializationConfig(["guides", "legend", kind]), program);
409
477
  const creation = resolveLegendCreationPlan(view, { target, channels });
410
- const oldCategorical = removed.find(kind => ["series", "color"].includes(kind));
411
- const plans = creation.steps.map(step => {
478
+ const oldCategorical = removed.find(kind =>
479
+ ["series", "color", "stroke"].includes(kind)
480
+ );
481
+ let plans = creation.steps.map(step => {
412
482
  const descriptor = resolveLegendStepConfig(view, step);
413
483
  const { kind } = descriptor;
414
- const otherCategorical = ["series", "color"].some(key => previous[key] !== undefined && previous[key].target !== target);
415
- if (previous[kind] !== undefined && previous[kind].target !== target ||
416
- ["series", "color"].includes(kind) && otherCategorical) {
484
+ if (previous[kind] !== undefined && previous[kind].target !== target) {
417
485
  throw new Error(`Legend ${kind} content already belongs to another target.`);
418
486
  }
419
- if (["series", "color"].includes(kind) && oldCategorical !== undefined) {
487
+ if (["series", "color", "stroke"].includes(kind) && oldCategorical !== undefined) {
420
488
  const order = patch.order === undefined ? program.semanticSpec.guides.legend?.[oldCategorical]?.order
421
489
  : normalizeLegendOrder(patch.order);
422
490
  return { ...resolveCategoricalLegendRevision(view, oldCategorical, previous[oldCategorical],
@@ -424,7 +492,9 @@ function editLegendContent(program, target, args) {
424
492
  }
425
493
  return { ...descriptor, config: previous[kind]?.target === target ? previous[kind] : descriptor.config };
426
494
  });
427
- const categorical = plans.find(plan => ["series", "color"].includes(plan.kind));
495
+ const categorical = plans.find(plan =>
496
+ ["series", "color", "stroke"].includes(plan.kind)
497
+ );
428
498
  const size = plans.find(plan => plan.kind === "size");
429
499
  if (categorical !== undefined) {
430
500
  if (size !== undefined && previous.size?.target !== target) {
@@ -444,23 +514,35 @@ function editLegendContent(program, target, args) {
444
514
  const plan = plans[0];
445
515
  if (plan.kind === "size" || plan.kind === "strokeWidth") {
446
516
  plan.config = resolveSampledLegendEdit(view, plan.kind, plan.config, patch).config;
447
- } else if (plan.kind === "interval") plan.config = resolveIntervalEdit(view, plan.config, patch).config;
517
+ } else if (["interval", "strokeInterval"].includes(plan.kind)) {
518
+ plan.config = resolveIntervalEdit(
519
+ view,
520
+ plan.config,
521
+ patch,
522
+ plan.kind === "strokeInterval" ? "stroke" : "color"
523
+ ).config;
524
+ }
448
525
  else plan.config = resolveContinuousEdit(view, plan.kind, plan.config, patch).config;
449
526
  }
527
+ plans = planLegendBlockTransitions(program, target, plans);
450
528
  // The complete final content and styles are validated before removing resources.
451
529
  for (const { kind, config } of plans) view = view._withLegendConfig(kind, config);
452
530
  if (categorical !== undefined) resolveLayout(view, categorical.config);
453
531
  let next = removeLegendKinds(program, removed);
454
532
  for (const { kind, config, order } of plans) {
455
- if (["series", "color"].includes(kind)) next = createCategoricalLegendFromConfig(next, config, order);
533
+ if (["series", "color", "stroke"].includes(kind)) {
534
+ next = createCategoricalLegendFromConfig(next, config, order);
535
+ }
456
536
  else next = ({ size: createSizeLegendFromConfig, strokeWidth: createStrokeWidthLegendFromConfig,
457
537
  gradient: createGradientLegendFromConfig, opacity: createOpacityLegendFromConfig,
458
- interval: createIntervalLegendFromConfig })[kind](next, config);
538
+ interval: createIntervalLegendFromConfig,
539
+ strokeGradient: createStrokeGradientLegendFromConfig,
540
+ strokeInterval: createStrokeIntervalLegendFromConfig })[kind](next, config);
459
541
  }
460
542
  return next.rematerializeLegend();
461
543
  }
462
544
 
463
- export const editLegend = action(
545
+ export const editLegend = /* @__PURE__ */ action(
464
546
  { op: "editLegend", description: "Edit one stable legend content, layout or appearance." },
465
547
  withGuideLayoutValidation(function (args = {}) {
466
548
  validateOptionObject(args, OPTIONS, "editLegend");
@@ -476,9 +558,23 @@ export const editLegend = action(
476
558
  const target = resolveLegendTarget(this, args.target, "editLegend");
477
559
  if (args.channels !== undefined) return editLegendContent(this, target, args);
478
560
  const configs = this.guideConfigs.legend ?? {};
479
- const categoricalKind = ["series", "color"].find(
561
+ const ownedKinds = Object.keys(configs).filter(
562
+ kind => configs[kind]?.target === target
563
+ );
564
+ if (args.values !== undefined && ownedKinds.length > 1) {
565
+ throw new Error(
566
+ "A target with multiple legend blocks requires a channel block selector for exact values."
567
+ );
568
+ }
569
+ const categoricalKinds = ["series", "color", "stroke"].filter(
480
570
  kind => configs[kind]?.target === target
481
571
  );
572
+ if (categoricalKinds.length > 1) {
573
+ throw new Error(
574
+ "editLegend requires channels when a target owns multiple categorical legend blocks."
575
+ );
576
+ }
577
+ const categoricalKind = categoricalKinds[0];
482
578
  if (categoricalKind !== undefined) {
483
579
  return editCategorical(
484
580
  this,
@@ -491,13 +587,16 @@ export const editLegend = action(
491
587
  if (configs.interval?.target === target) {
492
588
  return editInterval(this, configs.interval, args);
493
589
  }
590
+ if (configs.strokeInterval?.target === target) {
591
+ return editInterval(this, configs.strokeInterval, args, "stroke");
592
+ }
494
593
  if (configs.strokeWidth?.target === target) {
495
594
  return editSampledLegend(this, "strokeWidth", configs.strokeWidth, args);
496
595
  }
497
596
  if (configs.size?.target === target) {
498
597
  return editSampledLegend(this, "size", configs.size, args);
499
598
  }
500
- const continuousKind = ["gradient", "opacity"].find(
599
+ const continuousKind = ["gradient", "strokeGradient", "opacity"].find(
501
600
  kind => configs[kind]?.target === target
502
601
  );
503
602
  return editContinuous(this, continuousKind, configs[continuousKind], args);
@@ -17,7 +17,7 @@ function makeFocusedLegendEdit({ name, options, map }) {
17
17
  );
18
18
  }
19
19
 
20
- const editLegendLayout = makeFocusedLegendEdit({
20
+ const editLegendLayout = /* @__PURE__ */ makeFocusedLegendEdit({
21
21
  name: "editLegendLayout",
22
22
  options: [
23
23
  "position", "layout", "align", "direction", "columns", "offset",
@@ -26,13 +26,13 @@ const editLegendLayout = makeFocusedLegendEdit({
26
26
  map: args => args
27
27
  });
28
28
 
29
- const editLegendLabels = makeFocusedLegendEdit({
29
+ const editLegendLabels = /* @__PURE__ */ makeFocusedLegendEdit({
30
30
  name: "editLegendLabels",
31
31
  options: ["offset", "color", "fontSize", "fontFamily", "fontWeight", "format"],
32
32
  map: ({ target, ...labels }) => ({ target, labels })
33
33
  });
34
34
 
35
- const editLegendTitle = makeFocusedLegendEdit({
35
+ const editLegendTitle = /* @__PURE__ */ makeFocusedLegendEdit({
36
36
  name: "editLegendTitle",
37
37
  options: ["title", "color", "fontSize", "fontFamily", "fontWeight"],
38
38
  map: ({ target, title, ...titleStyle }) => ({
@@ -42,13 +42,13 @@ const editLegendTitle = makeFocusedLegendEdit({
42
42
  })
43
43
  });
44
44
 
45
- const editLegendSymbols = makeFocusedLegendEdit({
45
+ const editLegendSymbols = /* @__PURE__ */ makeFocusedLegendEdit({
46
46
  name: "editLegendSymbols",
47
47
  options: ["symbol", "count", "gradient"],
48
48
  map: args => args
49
49
  });
50
50
 
51
- const editLegendBorder = makeFocusedLegendEdit({
51
+ const editLegendBorder = /* @__PURE__ */ makeFocusedLegendEdit({
52
52
  name: "editLegendBorder",
53
53
  options: ["border"],
54
54
  map: args => args
@@ -4,6 +4,7 @@ import { registerSizeLegendActions } from "./size.js";
4
4
  import { registerStrokeWidthLegendActions } from "./strokeWidth.js";
5
5
  import { registerFocusedLegendActions } from "./focused.js";
6
6
  import { removeLegend } from "./remove.js";
7
+ import { editLegendBlock } from "./blocks.js";
7
8
 
8
9
  export function registerLegendActions(ProgramClass) {
9
10
  registerCategoricalLegendActions(ProgramClass);
@@ -11,5 +12,6 @@ export function registerLegendActions(ProgramClass) {
11
12
  registerSizeLegendActions(ProgramClass);
12
13
  registerStrokeWidthLegendActions(ProgramClass);
13
14
  registerFocusedLegendActions(ProgramClass);
15
+ ProgramClass.prototype.editLegendBlock = editLegendBlock;
14
16
  ProgramClass.prototype.removeLegend = removeLegend;
15
17
  }