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
@@ -1,3 +1,4 @@
1
+ import { editGraphicProperties } from "../../../primitives/graphicProperties.js";
1
2
  import { action } from "../../../../core/action.js";
2
3
  import {
3
4
  activeConfig,
@@ -11,6 +12,18 @@ import {
11
12
  } from "./layout.js";
12
13
  import { resolveCategoricalLegendPlacement } from "../lifecycle.js";
13
14
  import { createPointShapeGraphic } from "../../../../grammar/pointShapes.js";
15
+ import {
16
+ materializeRectItem,
17
+ requestedRectStyleDetails
18
+ } from "../../../../grammar/roundedRect.js";
19
+ import { requestedStrokeDetails } from "../../../../grammar/strokeStyle.js";
20
+ import { replaceGraphicItems } from "../../../primitives/editGraphics.js";
21
+
22
+ function applyLayerOpacity(program, id, opacity) {
23
+ return opacity === undefined
24
+ ? program
25
+ : program.editGraphics({ target: id, property: "opacity", value: opacity });
26
+ }
14
27
 
15
28
  function makeEditSymbol(type) {
16
29
  const suffix = { line: "Lines", point: "Points", swatch: "Swatches" }[type];
@@ -19,20 +32,23 @@ function makeEditSymbol(type) {
19
32
  { op, description: `Rematerialize categorical legend ${type} symbols.` },
20
33
  function (args = {}) {
21
34
  noOptions(args, op);
22
- const { config } = activeConfig(this);
35
+ const { config } = activeConfig(this, args.kind);
23
36
  const layer = layerFor(config, type);
24
37
  const id = symbolGraphic(config, type);
25
38
  const dynamicPoint = type === "point" && config.channels.includes("shape");
26
- const expected = dynamicPoint
39
+ const styledSwatch = type === "swatch" && Object.hasOwn(layer, "cornerRadius");
40
+ const expected = dynamicPoint || styledSwatch
27
41
  ? "collection"
28
42
  : { line: "line", point: "circle", swatch: "rect" }[type];
29
- if (this.graphicSpec.objects[id]?.type !== expected) {
43
+ const actual = this.graphicSpec.objects[id]?.type;
44
+ const compatibleStyledSwatch = styledSwatch && ["rect", "collection"].includes(actual);
45
+ if (actual !== expected && !compatibleStyledSwatch) {
30
46
  throw new Error(`${op} requires existing ${type} symbols.`);
31
47
  }
32
48
  const layout = resolveLayout(this, config);
33
49
  const appearance = resolveAppearance(this, config);
34
50
  let next = this;
35
- if (!dynamicPoint) {
51
+ if (!dynamicPoint && !styledSwatch) {
36
52
  next = next.editGraphics({
37
53
  target: id,
38
54
  property: "length",
@@ -43,18 +59,29 @@ function makeEditSymbol(type) {
43
59
  const x1 = layout.symbolX.map(
44
60
  value => value + (symbolWidth(config) - layer.length) / 2
45
61
  );
46
- return next
47
- .editGraphics({ target: id, property: "x1", value: x1 })
48
- .editGraphics({ target: id, property: "y1", value: layout.itemY })
62
+ next = editGraphicProperties(editGraphicProperties(editGraphicProperties(next, id, {
63
+ x1: x1,
64
+ y1: layout.itemY
65
+ }), id, {
66
+ x2: x1.map(value => value + layer.length),
67
+ y2: layout.itemY
68
+ })
49
69
  .editGraphics({
50
70
  target: id,
51
- property: "x2",
52
- value: x1.map(value => value + layer.length)
53
- })
54
- .editGraphics({ target: id, property: "y2", value: layout.itemY })
55
- .editGraphics({ target: id, property: "stroke", value: appearance.colors })
56
- .editGraphics({ target: id, property: "strokeWidth", value: layer.lineWidth })
57
- .editGraphics({ target: id, property: "strokeDash", value: appearance.dashes });
71
+ property: "stroke",
72
+ value: layer.stroke ?? (config.channels.includes("stroke")
73
+ ? appearance.strokes
74
+ : appearance.colors)
75
+ }), id, {
76
+ strokeWidth: layer.lineWidth,
77
+ strokeDash: appearance.dashes
78
+ });
79
+ for (const [property, value] of Object.entries(
80
+ requestedStrokeDetails(layer, "Legend line symbol")
81
+ )) {
82
+ next = next.editGraphics({ target: id, property, value });
83
+ }
84
+ return applyLayerOpacity(next, id, layer.opacity);
58
85
  }
59
86
  if (type === "point") {
60
87
  const x = layout.symbolX.map(value => value + symbolWidth(config) / 2);
@@ -67,8 +94,12 @@ function makeEditSymbol(type) {
67
94
  y: layout.itemY[index],
68
95
  area: Math.PI * layer.size ** 2,
69
96
  fill,
70
- stroke: layer.stroke,
71
- strokeWidth: layer.strokeWidth
97
+ stroke: config.channels.includes("stroke")
98
+ ? appearance.strokes[index]
99
+ : layer.stroke,
100
+ strokeWidth: layer.strokeWidth,
101
+ opacity: layer.opacity,
102
+ ...requestedStrokeDetails(layer, "Legend point symbol")
72
103
  });
73
104
  });
74
105
  return next.editGraphics({
@@ -77,33 +108,53 @@ function makeEditSymbol(type) {
77
108
  value: items
78
109
  });
79
110
  }
80
- return next
81
- .editGraphics({ target: id, property: "x", value: x })
82
- .editGraphics({ target: id, property: "y", value: layout.itemY })
83
- .editGraphics({ target: id, property: "radius", value: layer.size })
111
+ next = editGraphicProperties(next, id, {
112
+ x: x,
113
+ y: layout.itemY,
114
+ radius: layer.size,
115
+ fill: layer.fill ?? appearance.colors
116
+ })
84
117
  .editGraphics({
85
118
  target: id,
86
- property: "fill",
87
- value: layer.fill ?? appearance.colors
119
+ property: "stroke",
120
+ value: config.channels.includes("stroke")
121
+ ? appearance.strokes
122
+ : layer.stroke
88
123
  })
89
- .editGraphics({ target: id, property: "stroke", value: layer.stroke })
90
124
  .editGraphics({ target: id, property: "strokeWidth", value: layer.strokeWidth });
125
+ for (const [property, value] of Object.entries(
126
+ requestedStrokeDetails(layer, "Legend point symbol")
127
+ )) {
128
+ next = next.editGraphics({ target: id, property, value });
129
+ }
130
+ return applyLayerOpacity(next, id, layer.opacity);
91
131
  }
92
132
  const x = layout.symbolX.map(
93
133
  value => value + (symbolWidth(config) - layer.width) / 2
94
134
  );
95
- return next
96
- .editGraphics({ target: id, property: "x", value: x })
97
- .editGraphics({
98
- target: id,
99
- property: "y",
100
- value: layout.itemY.map(value => value - layer.height / 2)
101
- })
102
- .editGraphics({ target: id, property: "width", value: layer.width })
103
- .editGraphics({ target: id, property: "height", value: layer.height })
104
- .editGraphics({ target: id, property: "fill", value: appearance.colors })
105
- .editGraphics({ target: id, property: "stroke", value: layer.stroke })
106
- .editGraphics({ target: id, property: "strokeWidth", value: layer.strokeWidth });
135
+ const details = requestedRectStyleDetails(layer, "Legend swatch symbol");
136
+ const fills = layer.fill === undefined
137
+ ? appearance.colors
138
+ : config.domain.map(() => layer.fill);
139
+ const strokes = config.channels.includes("stroke")
140
+ ? appearance.strokes
141
+ : config.domain.map(() => layer.stroke);
142
+ return replaceGraphicItems(
143
+ next,
144
+ id,
145
+ "rect",
146
+ config.domain.map((_, index) => materializeRectItem({
147
+ x: x[index],
148
+ y: layout.itemY[index] - layer.height / 2,
149
+ width: layer.width,
150
+ height: layer.height,
151
+ fill: fills[index],
152
+ stroke: strokes[index],
153
+ strokeWidth: layer.strokeWidth,
154
+ ...(layer.opacity === undefined ? {} : { opacity: layer.opacity }),
155
+ ...requestedStrokeDetails(details, "Legend swatch symbol")
156
+ }, details.cornerRadius ?? 0))
157
+ );
107
158
  }
108
159
  );
109
160
  }
@@ -115,13 +166,14 @@ function makeCreateSymbol(type, edit) {
115
166
  { op, description: `Create categorical legend ${type} symbols.` },
116
167
  function (args = {}) {
117
168
  noOptions(args, op);
118
- const { config } = activeConfig(this);
119
- layerFor(config, type);
169
+ const { config } = activeConfig(this, args.kind);
170
+ const layer = layerFor(config, type);
120
171
  const id = symbolGraphic(config, type);
121
172
  if (this.graphicSpec.objects[id] !== undefined) {
122
173
  throw new Error(`${op} requires missing ${type} symbols.`);
123
174
  }
124
- const graphicType = type === "point" && config.channels.includes("shape")
175
+ const graphicType = type === "point" && config.channels.includes("shape") ||
176
+ type === "swatch" && (layer.cornerRadius ?? 0) > 0
125
177
  ? "collection"
126
178
  : { line: "line", point: "circle", swatch: "rect" }[type];
127
179
  return this
@@ -136,32 +188,32 @@ function makeCreateSymbol(type, edit) {
136
188
  ? {}
137
189
  : { length: config.domain.length })
138
190
  })
139
- [edit]();
191
+ [edit]({ kind: config.kind });
140
192
  }
141
193
  );
142
194
  }
143
195
 
144
- export const rematerializeLegendSymbolLines = makeEditSymbol("line");
145
- export const rematerializeLegendSymbolPoints = makeEditSymbol("point");
146
- export const rematerializeLegendSymbolSwatches = makeEditSymbol("swatch");
147
- export const createLegendSymbolLines = makeCreateSymbol(
196
+ export const rematerializeLegendSymbolLines = /* @__PURE__ */ makeEditSymbol("line");
197
+ export const rematerializeLegendSymbolPoints = /* @__PURE__ */ makeEditSymbol("point");
198
+ export const rematerializeLegendSymbolSwatches = /* @__PURE__ */ makeEditSymbol("swatch");
199
+ export const createLegendSymbolLines = /* @__PURE__ */ makeCreateSymbol(
148
200
  "line",
149
201
  "rematerializeLegendSymbolLines"
150
202
  );
151
- export const createLegendSymbolPoints = makeCreateSymbol(
203
+ export const createLegendSymbolPoints = /* @__PURE__ */ makeCreateSymbol(
152
204
  "point",
153
205
  "rematerializeLegendSymbolPoints"
154
206
  );
155
- export const createLegendSymbolSwatches = makeCreateSymbol(
207
+ export const createLegendSymbolSwatches = /* @__PURE__ */ makeCreateSymbol(
156
208
  "swatch",
157
209
  "rematerializeLegendSymbolSwatches"
158
210
  );
159
211
 
160
- export const createLegendSymbols = action(
212
+ export const createLegendSymbols = /* @__PURE__ */ action(
161
213
  { op: "createLegendSymbols", description: "Create layered legend symbols." },
162
214
  function (args = {}) {
163
215
  noOptions(args, "createLegendSymbols");
164
- const { config } = activeConfig(this);
216
+ const { config } = activeConfig(this, args.kind);
165
217
  let next = this;
166
218
  for (const layer of config.symbol.layers) {
167
219
  const operation = {
@@ -169,17 +221,17 @@ export const createLegendSymbols = action(
169
221
  point: "createLegendSymbolPoints",
170
222
  swatch: "createLegendSymbolSwatches"
171
223
  }[layer.type];
172
- next = next[operation]();
224
+ next = next[operation]({ kind: config.kind });
173
225
  }
174
226
  return next;
175
227
  }
176
228
  );
177
229
 
178
- export const rematerializeLegendSymbols = action(
230
+ export const rematerializeLegendSymbols = /* @__PURE__ */ action(
179
231
  { op: "rematerializeLegendSymbols", description: "Rematerialize layered legend symbols." },
180
232
  function (args = {}) {
181
233
  noOptions(args, "rematerializeLegendSymbols");
182
- const { config } = activeConfig(this);
234
+ const { config } = activeConfig(this, args.kind);
183
235
  let next = this;
184
236
  for (const layer of config.symbol.layers) {
185
237
  const operation = {
@@ -187,13 +239,13 @@ export const rematerializeLegendSymbols = action(
187
239
  point: "rematerializeLegendSymbolPoints",
188
240
  swatch: "rematerializeLegendSymbolSwatches"
189
241
  }[layer.type];
190
- next = next[operation]();
242
+ next = next[operation]({ kind: config.kind });
191
243
  }
192
244
  return next;
193
245
  }
194
246
  );
195
247
 
196
- export const rematerializeBasicLegendHighlights = action(
248
+ export const rematerializeBasicLegendHighlights = /* @__PURE__ */ action(
197
249
  {
198
250
  op: "rematerializeLegendHighlights",
199
251
  description: "Reflect exact categorical mark highlights in legend symbols."
@@ -25,9 +25,12 @@ import { findLayer } from "../../../../selectors/layers.js";
25
25
  import { isOpacityLegendLayer } from "../../../../materialization/legends.js";
26
26
  import { findCanvasGraphic } from
27
27
  "../../../../materialization/graphicHierarchy.js";
28
+ import { normalizeLegendSampling } from "../sampling.js";
29
+ import { editGraphicProperties } from "../../../primitives/graphicProperties.js";
28
30
 
29
31
  const OPTIONS = [
30
32
  "target", "channels", "position", "align", "offset", "title", "count",
33
+ "values",
31
34
  "gradient", "symbol", "labels", "titleStyle", "itemGap", "border",
32
35
  "direction", "columns", "titlePosition"
33
36
  ];
@@ -61,12 +64,7 @@ const DEFAULT_BORDER = {
61
64
  export const validatePositive = validatePositiveFinite;
62
65
  export const validateNonNegative = validateNonNegativeFinite;
63
66
 
64
- export function editGraphicProperties(program, target, properties) {
65
- for (const [property, value] of Object.entries(properties)) {
66
- program = program.editGraphics({ target, property, value });
67
- }
68
- return program;
69
- }
67
+ export { editGraphicProperties };
70
68
 
71
69
  export function normalizeLegendTextOptions(value, label, defaults) {
72
70
  if (value === undefined) return { ...defaults };
@@ -129,13 +127,24 @@ export function normalizeContinuousLegend(args, kind) {
129
127
  if (["left", "right"].includes(position) && align !== "center") {
130
128
  throw new Error("Side continuous legends require center alignment.");
131
129
  }
132
- const count = args.count ?? 5;
133
- if (!Number.isInteger(count) || count < 2) {
134
- throw new RangeError(
135
- "Continuous legend count must be an integer of at least 2."
136
- );
130
+ if (kind === "gradient" && Object.hasOwn(args, "values")) {
131
+ throw new Error("Gradient legends do not support exact sample values.");
132
+ }
133
+ const sampling = kind === "opacity"
134
+ ? normalizeLegendSampling(args, {
135
+ operation: "create",
136
+ label: "Opacity legend"
137
+ })
138
+ : undefined;
139
+ const count = kind === "gradient" ? args.count ?? 5 : undefined;
140
+ if (kind === "gradient") {
141
+ if (!Number.isInteger(count) || count < 2) {
142
+ throw new RangeError(
143
+ "Continuous legend count must be an integer of at least 2."
144
+ );
145
+ }
146
+ validateGeneratedItemLimit(count, "Continuous legend count");
137
147
  }
138
- validateGeneratedItemLimit(count, "Continuous legend count");
139
148
  const offset = args.offset ?? 30;
140
149
  validateNonNegative(offset, "Legend offset");
141
150
  const titlePosition = args.titlePosition ?? "top";
@@ -171,7 +180,7 @@ export function normalizeContinuousLegend(args, kind) {
171
180
  position,
172
181
  align,
173
182
  offset,
174
- count,
183
+ ...(kind === "gradient" ? { count } : { sampling }),
175
184
  title: args.title,
176
185
  inferredTitle: args.title === undefined,
177
186
  labels: normalizeLegendTextOptions(
@@ -218,18 +227,23 @@ export function resolveContinuousLegendLayer(program, requested, channel) {
218
227
  return layer;
219
228
  }
220
229
 
221
- export function resolveContinuousColorLayer(program, requested) {
230
+ export function resolveContinuousColorLayer(program, requested, channel = "color") {
222
231
  const layer = selectLegendLayer(
223
232
  program,
224
233
  requested,
225
- candidate => ["point", "bar", "rect"].includes(candidate.mark?.type) &&
226
- candidate.encoding?.color?.scale !== undefined
234
+ candidate => (channel === "stroke"
235
+ ? ["point", "line", "area", "bar", "rect", "arc", "rule", "tick"]
236
+ : ["point", "bar", "rect"]
237
+ ).includes(candidate.mark?.type) &&
238
+ candidate.encoding?.[channel]?.scale !== undefined
227
239
  );
228
240
  if (layer === undefined) {
229
241
  throw new Error(
230
242
  requested === undefined
231
- ? "color legend requires one eligible point, bar, or rect mark."
232
- : `Unknown color legend target "${requested}".`
243
+ ? channel === "color"
244
+ ? "color legend requires one eligible point, bar, or rect mark."
245
+ : "stroke legend requires one eligible mark."
246
+ : `Unknown ${channel} legend target "${requested}".`
233
247
  );
234
248
  }
235
249
  return layer;
@@ -300,7 +314,7 @@ export function styleContinuousText(
300
314
  });
301
315
  }
302
316
 
303
- export function resolveLegendTextBounds(position, text, style) {
317
+ export function resolveLegendTextBounds(position, text, style, profile) {
304
318
  return resolveTextBounds({
305
319
  x: position.x,
306
320
  y: position.y,
@@ -308,7 +322,7 @@ export function resolveLegendTextBounds(position, text, style) {
308
322
  ...style,
309
323
  textAlign: position.align ?? "left",
310
324
  textBaseline: "middle"
311
- });
325
+ }, profile);
312
326
  }
313
327
 
314
328
  export function assertLegendBoundsInsideCanvas(bounds, canvas, label, config) {
@@ -389,3 +403,45 @@ export function normalizeItemLegendLayout(args) {
389
403
  validatePositive(itemGap, "Legend itemGap");
390
404
  return { position, layout, align, direction, columns, titlePosition, offset, itemGap };
391
405
  }
406
+
407
+ export function materializeItemLegend(program, kind, config, layout, {
408
+ symbols, optional = {}, text = layout.labels,
409
+ labels = layout.config.labels, titleStyle = layout.config.titleStyle
410
+ }) {
411
+ const prefix = `${kind}Legend`;
412
+ let next = program
413
+ .editSemantic({ property: `guide.legend.${kind}.scale`, value: config.scale })
414
+ .editSemantic({ property: `guide.legend.${kind}.title`, value: config.title })
415
+ ._withLegendConfig(kind, config);
416
+ next = editGraphicProperties(next, `${prefix}Symbols`, {
417
+ length: text.length,
418
+ ...symbols
419
+ });
420
+ next = editGraphicProperties(next, `${prefix}Labels`, {
421
+ length: text.length, x: layout.labelX, y: layout.itemY, text
422
+ });
423
+ next = editLegendBackground(next, `${prefix}Background`, layout.background, config.border);
424
+ next = editGraphicProperties(next, `${prefix}Symbols`, Object.fromEntries(
425
+ Object.entries(optional).filter(([, value]) => value !== undefined)
426
+ ));
427
+ next = styleContinuousText(next, `${prefix}Labels`, labels);
428
+ const effective = layout.config;
429
+ if (effective.titleVisible === false) return next;
430
+ next = editGraphicProperties(next, `${prefix}Title`, {
431
+ x: layout.title.x, y: layout.title.y, text: effective.title
432
+ });
433
+ return styleContinuousText(next, `${prefix}Title`, titleStyle, { align: layout.title.align });
434
+ }
435
+
436
+ export function createItemLegendGraphics(program, kind, config, count, type, placement) {
437
+ const prefix = `${kind}Legend`;
438
+ let next = program
439
+ .editSemantic({ property: `guide.legend.${kind}.scale`, value: config.scale })
440
+ .editSemantic({ property: `guide.legend.${kind}.title`, value: config.title })
441
+ ._withLegendConfig(kind, config);
442
+ if (config.border !== false) next = next.createGraphics({ id: `${prefix}Background`, type: "rect", ...placement });
443
+ next = next.createGraphics({ id: `${prefix}Symbols`, type, length: count, ...placement })
444
+ .createGraphics({ id: `${prefix}Labels`, type: "text", length: count, ...placement });
445
+ if (config.titleVisible !== false) next = next.createGraphics({ id: `${prefix}Title`, type: "text", ...placement });
446
+ return next;
447
+ }