ggaction 0.0.12 → 0.0.15

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 (378) hide show
  1. package/CHANGELOG.md +127 -0
  2. package/README.md +20 -11
  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 +10 -3
  11. package/src/actions/basic.js +4 -2
  12. package/src/actions/boxPlots/components.js +14 -4
  13. package/src/actions/boxPlots/edit.js +31 -5
  14. package/src/actions/boxPlots/materialize.js +13 -4
  15. package/src/actions/boxPlots/options.js +48 -16
  16. package/src/actions/canvas/actions.js +3 -1
  17. package/src/actions/canvas/fitting.js +211 -0
  18. package/src/actions/canvas/index.js +2 -0
  19. package/src/actions/categoryOrder/index.js +18 -6
  20. package/src/actions/charts/area.js +51 -0
  21. package/src/actions/charts/bar.js +13 -6
  22. package/src/actions/charts/beeswarm.js +70 -0
  23. package/src/actions/charts/density.js +48 -0
  24. package/src/actions/charts/ecdf.js +186 -0
  25. package/src/actions/charts/endpoints.js +473 -0
  26. package/src/actions/charts/heatmap.js +4 -3
  27. package/src/actions/charts/histogram.js +5 -4
  28. package/src/actions/charts/horizon.js +73 -0
  29. package/src/actions/charts/index.js +37 -0
  30. package/src/actions/charts/interval-regression.js +172 -0
  31. package/src/actions/charts/line.js +8 -6
  32. package/src/actions/charts/parallel.js +8 -2
  33. package/src/actions/charts/pie.js +52 -0
  34. package/src/actions/charts/polar.js +134 -0
  35. package/src/actions/charts/radar.js +305 -0
  36. package/src/actions/charts/radial.js +50 -0
  37. package/src/actions/charts/raincloud.js +422 -0
  38. package/src/actions/charts/rug-strip.js +283 -0
  39. package/src/actions/charts/scatter.js +11 -3
  40. package/src/actions/charts/shared.js +69 -10
  41. package/src/actions/composition/actions.js +129 -3
  42. package/src/actions/coordinates/actions.js +1 -1
  43. package/src/actions/coordinates/edit.js +76 -0
  44. package/src/actions/data/bin.js +29 -0
  45. package/src/actions/data/bin2d.js +57 -185
  46. package/src/actions/data/complete.js +26 -0
  47. package/src/actions/data/computed.js +24 -0
  48. package/src/actions/data/create.js +2 -2
  49. package/src/actions/data/density.js +12 -20
  50. package/src/actions/data/derived.js +111 -15
  51. package/src/actions/data/ecdf.js +38 -0
  52. package/src/actions/data/edit.js +539 -0
  53. package/src/actions/data/filter.js +226 -30
  54. package/src/actions/data/fold.js +24 -0
  55. package/src/actions/data/impute.js +26 -0
  56. package/src/actions/data/index.js +38 -0
  57. package/src/actions/data/interval.js +2 -0
  58. package/src/actions/data/intervalEdit.js +86 -4
  59. package/src/actions/data/normalize.js +27 -0
  60. package/src/actions/data/regression.js +19 -21
  61. package/src/actions/data/revisionBindings.js +145 -0
  62. package/src/actions/data/shared.js +20 -4
  63. package/src/actions/data/stack.js +26 -0
  64. package/src/actions/data/summary.js +26 -0
  65. package/src/actions/data/timeUnit.js +4 -1
  66. package/src/actions/data/window.js +1 -1
  67. package/src/actions/encodings/appearance.js +32 -13
  68. package/src/actions/encodings/barWidth.js +8 -0
  69. package/src/actions/encodings/basic.js +2 -0
  70. package/src/actions/encodings/channels.js +415 -0
  71. package/src/actions/encodings/color/continuous.js +19 -59
  72. package/src/actions/encodings/color/index.js +30 -23
  73. package/src/actions/encodings/color/layout.js +3 -97
  74. package/src/actions/encodings/color/policy.js +5 -8
  75. package/src/actions/encodings/density/resolve.js +37 -5
  76. package/src/actions/encodings/density.js +124 -34
  77. package/src/actions/encodings/histogram.js +3 -2
  78. package/src/actions/encodings/horizon/resolve.js +8 -4
  79. package/src/actions/encodings/horizon.js +18 -8
  80. package/src/actions/encodings/index.js +6 -0
  81. package/src/actions/encodings/offset.js +30 -29
  82. package/src/actions/encodings/parallel.js +2 -8
  83. package/src/actions/encodings/position/apply.js +63 -11
  84. package/src/actions/encodings/position/index.js +52 -6
  85. package/src/actions/encodings/position/policies/arc.js +14 -0
  86. package/src/actions/encodings/position/policies/area.js +4 -42
  87. package/src/actions/encodings/position/policies/bar.js +57 -13
  88. package/src/actions/encodings/position/resolve.js +88 -26
  89. package/src/actions/encodings/ranged.js +123 -97
  90. package/src/actions/encodings/remove.js +44 -34
  91. package/src/actions/encodings/ruleAppearance.js +24 -32
  92. package/src/actions/encodings/seriesLayout.js +105 -0
  93. package/src/actions/encodings/shared.js +107 -37
  94. package/src/actions/encodings/stroke.js +226 -0
  95. package/src/actions/encodings/strokeDash.js +9 -6
  96. package/src/actions/encodings/temporal.js +7 -0
  97. package/src/actions/encodings/text.js +30 -15
  98. package/src/actions/errorBands/create.js +41 -8
  99. package/src/actions/errorBands/edit.js +360 -25
  100. package/src/actions/errorBars/create.js +56 -17
  101. package/src/actions/errorBars/edit.js +372 -21
  102. package/src/actions/facets/actions.js +619 -41
  103. package/src/actions/facets/derive.js +139 -25
  104. package/src/actions/gradientPlots/components.js +43 -13
  105. package/src/actions/gradientPlots/edit.js +14 -4
  106. package/src/actions/gradientPlots/materialize.js +8 -8
  107. package/src/actions/gradientPlots/options.js +10 -2
  108. package/src/actions/guides/applicability.js +28 -19
  109. package/src/actions/guides/axes/axes.js +4 -3
  110. package/src/actions/guides/axes/axis.js +18 -18
  111. package/src/actions/guides/axes/components.js +43 -0
  112. package/src/actions/guides/axes/edit.js +16 -51
  113. package/src/actions/guides/axes/labels.js +184 -28
  114. package/src/actions/guides/axes/lines.js +13 -7
  115. package/src/actions/guides/axes/parallel/lifecycle.js +117 -0
  116. package/src/actions/guides/axes/parallel/policy.js +137 -0
  117. package/src/actions/guides/axes/parallel/resolve.js +50 -25
  118. package/src/actions/guides/axes/parallel.js +93 -217
  119. package/src/actions/guides/axes/policy.js +21 -67
  120. package/src/actions/guides/axes/tickGroups.js +15 -8
  121. package/src/actions/guides/axes/ticks.js +11 -6
  122. package/src/actions/guides/axes/titles.js +32 -11
  123. package/src/actions/guides/basic.js +2 -0
  124. package/src/actions/guides/facade.js +106 -0
  125. package/src/actions/guides/facadeAxes.js +200 -0
  126. package/src/actions/guides/facadeLegend.js +63 -0
  127. package/src/actions/guides/grids/grid.js +37 -33
  128. package/src/actions/guides/grids/resolve.js +17 -4
  129. package/src/actions/guides/guides.js +54 -31
  130. package/src/actions/guides/legends/blocks.js +426 -0
  131. package/src/actions/guides/legends/categorical/actions.js +309 -279
  132. package/src/actions/guides/legends/categorical/components.js +21 -17
  133. package/src/actions/guides/legends/categorical/index.js +37 -33
  134. package/src/actions/guides/legends/categorical/layout.js +123 -354
  135. package/src/actions/guides/legends/categorical/options.js +32 -16
  136. package/src/actions/guides/legends/categorical/recipes.js +114 -9
  137. package/src/actions/guides/legends/categorical/resolve.js +30 -15
  138. package/src/actions/guides/legends/categorical/symbols.js +87 -33
  139. package/src/actions/guides/legends/continuous/common.js +99 -26
  140. package/src/actions/guides/legends/continuous/gradient.js +120 -89
  141. package/src/actions/guides/legends/continuous/index.js +8 -2
  142. package/src/actions/guides/legends/continuous/interval.js +79 -95
  143. package/src/actions/guides/legends/continuous/opacity.js +113 -89
  144. package/src/actions/guides/legends/continuous/stroke.js +525 -0
  145. package/src/actions/guides/legends/creation.js +40 -0
  146. package/src/actions/guides/legends/edit.js +321 -90
  147. package/src/actions/guides/legends/focused.js +2 -2
  148. package/src/actions/guides/legends/index.js +2 -0
  149. package/src/actions/guides/legends/lane.js +149 -102
  150. package/src/actions/guides/legends/lifecycle.js +143 -0
  151. package/src/actions/guides/legends/remove.js +43 -78
  152. package/src/actions/guides/legends/sampling.js +140 -0
  153. package/src/actions/guides/legends/size.js +213 -116
  154. package/src/actions/guides/legends/strokeWidth.js +121 -73
  155. package/src/actions/guides/legends/target.js +123 -0
  156. package/src/actions/guides/legends/transition.js +192 -0
  157. package/src/actions/guides/polar/axes/facade.js +81 -66
  158. package/src/actions/guides/polar/axes/labels.js +28 -8
  159. package/src/actions/guides/polar/axes/lines.js +12 -5
  160. package/src/actions/guides/polar/axes/shared.js +22 -0
  161. package/src/actions/guides/polar/axes/ticks.js +6 -5
  162. package/src/actions/guides/polar/axes/titles.js +6 -5
  163. package/src/actions/guides/polar/grids.js +8 -8
  164. package/src/actions/guides/polar/resolve.js +12 -8
  165. package/src/actions/guides/reuse.js +44 -0
  166. package/src/actions/guides/tickValues.js +19 -21
  167. package/src/actions/index.js +15 -2
  168. package/src/actions/intervals/resolve.js +35 -15
  169. package/src/actions/marks/arc/actions.js +78 -20
  170. package/src/actions/marks/area/actions.js +51 -12
  171. package/src/actions/marks/area/materialize.js +22 -3
  172. package/src/actions/marks/bar/create.js +11 -2
  173. package/src/actions/marks/bar/edit.js +20 -3
  174. package/src/actions/marks/bar/materialize.js +33 -9
  175. package/src/actions/marks/index.js +14 -0
  176. package/src/actions/marks/line/actions.js +71 -113
  177. package/src/actions/marks/line/materialize.js +57 -56
  178. package/src/actions/marks/point/create.js +8 -2
  179. package/src/actions/marks/point/edit.js +29 -21
  180. package/src/actions/marks/point/index.js +1 -0
  181. package/src/actions/marks/point/jitter.js +3 -0
  182. package/src/actions/marks/point/materialize.js +154 -16
  183. package/src/actions/marks/point/packing.js +68 -0
  184. package/src/actions/marks/rect/actions.js +33 -12
  185. package/src/actions/marks/references.js +658 -0
  186. package/src/actions/marks/remove.js +87 -4
  187. package/src/actions/marks/rule/actions.js +77 -9
  188. package/src/actions/marks/rule/appearance.js +26 -0
  189. package/src/actions/marks/shared.js +13 -13
  190. package/src/actions/marks/text/actions.js +296 -13
  191. package/src/actions/marks/text/index.js +4 -0
  192. package/src/actions/marks/text/layout.js +156 -8
  193. package/src/actions/marks/text/removal.js +173 -0
  194. package/src/actions/marks/text/selection.js +73 -0
  195. package/src/actions/marks/tick/actions.js +22 -5
  196. package/src/actions/primitives/createGraphics.js +3 -1
  197. package/src/actions/primitives/editGraphics.js +26 -0
  198. package/src/actions/primitives/semantic.js +3 -2
  199. package/src/actions/primitives/semanticAction.js +120 -10
  200. package/src/actions/primitives/semanticValidation/guide.js +10 -0
  201. package/src/actions/primitives/semanticValidation/index.js +9 -1
  202. package/src/actions/primitives/semanticValidation/layer.js +23 -1
  203. package/src/actions/primitives/semanticValidation/scale.js +38 -8
  204. package/src/actions/regression/components.js +26 -11
  205. package/src/actions/regression/create.js +11 -3
  206. package/src/actions/regression/edit.js +40 -13
  207. package/src/actions/regression/resolve.js +1 -1
  208. package/src/actions/resources/index.js +7 -0
  209. package/src/actions/resources/remove.js +172 -0
  210. package/src/actions/scales/channels.js +212 -0
  211. package/src/actions/scales/consumers/common.js +21 -54
  212. package/src/actions/scales/consumers/families.js +28 -14
  213. package/src/actions/scales/consumers/index.js +42 -4
  214. package/src/actions/scales/consumers/seriesLayout.js +57 -12
  215. package/src/actions/scales/create.js +58 -26
  216. package/src/actions/scales/definitions.js +109 -39
  217. package/src/actions/scales/edit.js +101 -34
  218. package/src/actions/scales/editPolicy.js +95 -35
  219. package/src/actions/scales/index.js +6 -0
  220. package/src/actions/scales/materialize.js +14 -49
  221. package/src/actions/scales/offset.js +48 -0
  222. package/src/actions/scales/parallel.js +71 -0
  223. package/src/actions/scales/patch.js +1 -1
  224. package/src/actions/scales/preview.js +140 -0
  225. package/src/actions/scales/quantitativeColor.js +9 -5
  226. package/src/actions/selection/actions.js +154 -30
  227. package/src/actions/selection/index.js +3 -0
  228. package/src/actions/theme/actions.js +98 -0
  229. package/src/actions/theme/composition.js +238 -0
  230. package/src/actions/theme/index.js +16 -0
  231. package/src/actions/theme/reconcile.js +1130 -0
  232. package/src/actions/theme/state.js +160 -0
  233. package/src/actions/titles/actions.js +11 -10
  234. package/src/actions/titles/resolve.js +1 -40
  235. package/src/actions/violinPlots/create.js +90 -43
  236. package/src/actions/violinPlots/edit.js +192 -0
  237. package/src/actions/violinPlots/index.js +2 -0
  238. package/src/core/ChartProgram.js +7 -0
  239. package/src/core/action.js +32 -2
  240. package/src/core/compositionState.js +141 -8
  241. package/src/core/programState.js +20 -0
  242. package/src/core/resourceReferences.js +332 -0
  243. package/src/core/vocabulary.js +16 -3
  244. package/src/grammar/aggregate.js +66 -23
  245. package/src/grammar/arcs.js +58 -1
  246. package/src/grammar/areaEndpoints.js +46 -0
  247. package/src/grammar/areaSeries.js +87 -41
  248. package/src/grammar/bars/aggregate.js +37 -13
  249. package/src/grammar/bars/geometry.js +44 -0
  250. package/src/grammar/bars/policy.js +9 -7
  251. package/src/grammar/bin.js +226 -0
  252. package/src/grammar/categoryOrder.js +34 -0
  253. package/src/grammar/complete.js +279 -0
  254. package/src/grammar/computed.js +351 -0
  255. package/src/grammar/coordinates.js +37 -0
  256. package/src/grammar/density.js +239 -37
  257. package/src/grammar/displayLabels.js +57 -0
  258. package/src/grammar/ecdf.js +198 -0
  259. package/src/grammar/facets/dependencies.js +133 -1
  260. package/src/grammar/facets/guides.js +3 -0
  261. package/src/grammar/facets/index.js +123 -52
  262. package/src/grammar/facets/scales.js +15 -18
  263. package/src/grammar/fold.js +123 -0
  264. package/src/grammar/horizon.js +6 -4
  265. package/src/grammar/impute.js +340 -0
  266. package/src/grammar/interval.js +37 -20
  267. package/src/grammar/lineSeries.js +20 -63
  268. package/src/grammar/markFilter.js +22 -4
  269. package/src/grammar/markLabelSelection.js +57 -0
  270. package/src/grammar/markLabels.js +110 -0
  271. package/src/grammar/normalize.js +366 -0
  272. package/src/grammar/parallelAxisTitles.js +16 -0
  273. package/src/grammar/pathSeries.js +109 -0
  274. package/src/grammar/pointPacking.js +183 -0
  275. package/src/grammar/pointShapes.js +13 -4
  276. package/src/grammar/polar.js +90 -8
  277. package/src/grammar/positionDatum.js +12 -0
  278. package/src/grammar/rects.js +9 -1
  279. package/src/grammar/regression/derive.js +4 -0
  280. package/src/grammar/regression/index.js +8 -2
  281. package/src/grammar/regression/models.js +22 -7
  282. package/src/grammar/regression/parameters.js +63 -13
  283. package/src/grammar/rotation.js +29 -0
  284. package/src/grammar/roundedRect.js +180 -0
  285. package/src/grammar/rules.js +8 -21
  286. package/src/grammar/scales/color.js +23 -1
  287. package/src/grammar/scales/colorConsumers.js +57 -0
  288. package/src/grammar/scales/continuous.js +2 -2
  289. package/src/grammar/scales/definition.js +42 -5
  290. package/src/grammar/scales/discretized.js +11 -5
  291. package/src/grammar/scales/fields.js +38 -8
  292. package/src/grammar/scales/index.js +1 -0
  293. package/src/grammar/scales/mapping.js +2 -0
  294. package/src/grammar/scales/ordinal.js +12 -3
  295. package/src/grammar/scales/policies.js +2 -2
  296. package/src/grammar/scales/radial.js +55 -0
  297. package/src/grammar/scales/size.js +456 -0
  298. package/src/grammar/scales/types.js +1 -0
  299. package/src/grammar/schemas/concreteGraphic.js +14 -2
  300. package/src/grammar/schemas/graphic.js +15 -0
  301. package/src/grammar/schemas/graphicBounds.js +179 -30
  302. package/src/grammar/schemas/semanticPath.js +23 -4
  303. package/src/grammar/stack.js +170 -0
  304. package/src/grammar/statisticalReference.js +103 -0
  305. package/src/grammar/statistics/confidenceInterval.js +101 -0
  306. package/src/grammar/strokeStyle.js +77 -0
  307. package/src/grammar/summary.js +245 -0
  308. package/src/grammar/text.js +22 -16
  309. package/src/grammar/timeUnit.js +234 -13
  310. package/src/grammar/transformTopology.js +10 -0
  311. package/src/grammar/transforms.js +418 -16
  312. package/src/grammar/valueFormat.js +112 -0
  313. package/src/grammar/weightedStatistics.js +374 -0
  314. package/src/grammar/window.js +232 -21
  315. package/src/layout/aspect.js +80 -0
  316. package/src/layout/facets.js +100 -12
  317. package/src/layout/guideCollisions.js +12 -0
  318. package/src/layout/labels.js +343 -0
  319. package/src/layout/legend.js +2 -10
  320. package/src/layout/legendItems.js +102 -0
  321. package/src/layout/legendLane.js +100 -11
  322. package/src/materialization/bars/aggregate.js +56 -147
  323. package/src/materialization/bars/histogram.js +104 -20
  324. package/src/materialization/bars/ranged.js +19 -3
  325. package/src/materialization/bars/resolve.js +7 -4
  326. package/src/materialization/categorySlotOffset.js +40 -0
  327. package/src/materialization/composition.js +3 -1
  328. package/src/materialization/compositionSnapshot.js +1 -1
  329. package/src/materialization/coordinateBounds.js +204 -0
  330. package/src/materialization/dependencies.js +93 -16
  331. package/src/materialization/encodings.js +1 -1
  332. package/src/materialization/facetGuides/legacyCategorical.js +3 -0
  333. package/src/materialization/facetGuides/placement.js +8 -2
  334. package/src/materialization/facetGuides/preparation.js +12 -7
  335. package/src/materialization/facetHeaders.js +346 -0
  336. package/src/materialization/facets.js +23 -66
  337. package/src/materialization/guides/layout.js +57 -0
  338. package/src/materialization/guides/resources.js +28 -2
  339. package/src/materialization/legends.js +11 -2
  340. package/src/materialization/marks/capabilities.js +20 -5
  341. package/src/materialization/marks/grouping.js +52 -0
  342. package/src/materialization/marks/index.js +68 -27
  343. package/src/materialization/marks/policies.js +4 -1
  344. package/src/materialization/rect.js +55 -42
  345. package/src/materialization/rectConfig.js +5 -1
  346. package/src/materialization/rowEncoding.js +13 -13
  347. package/src/materialization/scaleGuideDependencies.js +8 -5
  348. package/src/materialization/scales/map.js +6 -3
  349. package/src/materialization/scales/policies/arc.js +42 -0
  350. package/src/materialization/scales/policies/binnedPosition.js +7 -4
  351. package/src/materialization/scales/policies/offset.js +50 -8
  352. package/src/materialization/scales/policies/series.js +5 -1
  353. package/src/materialization/scales/resolve.js +65 -17
  354. package/src/materialization/selection/compatibility.js +11 -0
  355. package/src/materialization/selection/items/arc.js +28 -4
  356. package/src/materialization/selection/items/bar.js +42 -71
  357. package/src/materialization/selection/items/common.js +27 -6
  358. package/src/materialization/selection/items/path.js +3 -13
  359. package/src/materialization/selection/items/rect.js +35 -8
  360. package/src/materialization/selection/path.js +1 -1
  361. package/src/materialization/selection/point.js +3 -1
  362. package/src/materialization/text.js +324 -14
  363. package/src/mcp/adapter.js +2 -2
  364. package/src/renderers/canvas/circle.js +2 -3
  365. package/src/renderers/canvas/line.js +2 -3
  366. package/src/renderers/canvas/native.js +4 -4
  367. package/src/renderers/canvas/path.js +2 -3
  368. package/src/renderers/canvas/rect.js +2 -3
  369. package/src/renderers/canvas/stroke.js +11 -0
  370. package/src/renderers/svg.js +19 -0
  371. package/src/selectors/datasets.js +44 -0
  372. package/src/selectors/layers.js +7 -2
  373. package/src/selectors/scales.js +6 -0
  374. package/src/theme/defaults.js +140 -0
  375. package/types/basic.d.ts +17 -2
  376. package/types/index.d.ts +211 -0
  377. package/types/program.d.ts +2182 -204
  378. package/src/materialization/bars/grouped.js +0 -144
@@ -1,8 +1,10 @@
1
+ import { resolveLegendItemLayout } from "../../../../layout/legendItems.js";
1
2
  import { action } from "../../../../core/action.js";
2
3
  import { isPlainObject } from "../../../../core/immutable.js";
3
4
  import {
4
5
  validateKeys,
5
- validateOptionObject
6
+ validateOptionObject,
7
+ validateNonEmptyString
6
8
  } from "../../../../core/validation.js";
7
9
  import { formatDiscretizedIntervals } from "../../../../grammar/scales/index.js";
8
10
  import { DEFAULT_COLORS, DEFAULT_FONT_FAMILY } from
@@ -12,21 +14,23 @@ import {
12
14
  editGraphicProperties,
13
15
  editLegendBackground,
14
16
  normalizeLegendBorder,
17
+ normalizeItemLegendLayout,
15
18
  normalizeLegendTextOptions,
19
+ normalizeLegendTitleOptions,
16
20
  resolveContinuousBounds,
17
- resolveContinuousPoint,
21
+ resolveContinuousColorLayer,
18
22
  resolveLegendBackgroundFromBounds,
19
- resolveLegendTextBounds,
20
23
  styleContinuousText,
21
24
  validateNonNegative,
22
25
  validatePositive
23
26
  } from "./common.js";
24
27
  import { resolveLegendGraphicPlacement } from
25
28
  "../../../../materialization/graphicHierarchy.js";
29
+ import { resolveEffectiveLegendBlockConfig } from "../blocks.js";
26
30
 
27
31
  const OPTIONS = [
28
32
  "target", "channels", "position", "align", "offset", "title",
29
- "symbol", "labels", "titleStyle", "itemGap", "direction", "border"
33
+ "symbol", "labels", "titleStyle", "itemGap", "direction", "border", "layout", "columns", "titlePosition"
30
34
  ];
31
35
  const SYMBOL_OPTIONS = [
32
36
  "width", "height", "stroke", "strokeWidth"
@@ -41,12 +45,7 @@ export function normalizeIntervalLegend(args) {
41
45
  )) {
42
46
  throw new Error('Interval legend requires channels: ["color"].');
43
47
  }
44
- if ((args.position ?? "right") !== "right") {
45
- throw new Error('Interval legends currently support position "right".');
46
- }
47
- if ((args.direction ?? "vertical") !== "vertical") {
48
- throw new Error('Interval legends currently support direction "vertical".');
49
- }
48
+ const layout = normalizeItemLegendLayout(args);
50
49
  if (args.symbol !== undefined && !isPlainObject(args.symbol)) {
51
50
  throw new TypeError("createLegend.symbol must be a plain object.");
52
51
  }
@@ -61,17 +60,11 @@ export function normalizeIntervalLegend(args) {
61
60
  validatePositive(symbol.width, "Legend symbol width");
62
61
  validatePositive(symbol.height, "Legend symbol height");
63
62
  validateNonNegative(symbol.strokeWidth, "Legend symbol strokeWidth");
64
- const offset = args.offset ?? 30;
65
- const itemGap = args.itemGap ?? 28;
66
- validateNonNegative(offset, "Legend offset");
67
- validatePositive(itemGap, "Legend itemGap");
63
+ if (args.title !== undefined) validateNonEmptyString(args.title, "Legend title");
64
+ validateNonEmptyString(symbol.stroke, "Legend symbol stroke");
68
65
  return {
69
66
  target: args.target,
70
- position: "right",
71
- direction: "vertical",
72
- align: args.align ?? "center",
73
- offset,
74
- itemGap,
67
+ ...layout,
75
68
  title: args.title,
76
69
  inferredTitle: args.title === undefined,
77
70
  titleVisible: true,
@@ -83,7 +76,7 @@ export function normalizeIntervalLegend(args) {
83
76
  fontFamily: DEFAULT_FONT_FAMILY,
84
77
  fontWeight: "normal"
85
78
  }),
86
- titleStyle: normalizeLegendTextOptions(args.titleStyle, "createLegend.titleStyle", {
79
+ titleStyle: normalizeLegendTitleOptions(args.titleStyle, "createLegend.titleStyle", {
87
80
  color: DEFAULT_COLORS.text,
88
81
  fontSize: 13,
89
82
  fontFamily: DEFAULT_FONT_FAMILY,
@@ -93,8 +86,8 @@ export function normalizeIntervalLegend(args) {
93
86
  };
94
87
  }
95
88
 
96
- function resolveIntervalConfig(program, stored) {
97
- const layer = resolveContinuousPoint(program, stored.target, "color");
89
+ export function resolveIntervalConfig(program, stored) {
90
+ const layer = resolveContinuousColorLayer(program, stored.target);
98
91
  const encoding = layer.encoding.color;
99
92
  if (encoding.fieldType !== "quantitative") {
100
93
  throw new Error("Interval legend requires quantitative color.");
@@ -111,47 +104,26 @@ function resolveIntervalConfig(program, stored) {
111
104
  } };
112
105
  }
113
106
 
114
- function resolveIntervalLayout(program, config, scale) {
107
+ export function resolveIntervalLayout(program, config, scale) {
115
108
  const { plot, canvas } = resolveContinuousBounds(program);
116
- const labels = formatDiscretizedIntervals(scale.thresholds);
117
- const symbolX = plot.x + plot.width + config.offset;
118
- const itemY = labels.map((_, index) => plot.y + 52 + index * config.itemGap);
119
- const labelX = symbolX + config.symbol.width + config.labels.offset;
120
- const title = { x: symbolX, y: plot.y + 20 };
121
- const strokeExtent = config.symbol.strokeWidth / 2;
122
- const occupiedBounds = [
123
- resolveLegendTextBounds(
124
- { ...title, align: "left" },
125
- config.title,
126
- config.titleStyle
127
- ),
128
- ...labels.map((label, index) => resolveLegendTextBounds(
129
- { x: labelX, y: itemY[index], align: "left" },
130
- label,
131
- config.labels
132
- )),
133
- ...itemY.map(y => ({
134
- left: symbolX - strokeExtent,
135
- right: symbolX + config.symbol.width + strokeExtent,
136
- top: y - config.symbol.height / 2 - strokeExtent,
137
- bottom: y + config.symbol.height / 2 + strokeExtent
138
- }))
139
- ];
109
+ const labels = formatDiscretizedIntervals(scale.thresholds, config.labels.format);
110
+ const layout = resolveLegendItemLayout(plot, config, labels, config.symbol);
111
+ const occupiedBounds = layout.bounds;
140
112
  assertLegendBoundsInsideCanvas(
141
113
  occupiedBounds,
142
114
  canvas,
143
- "Interval legend layout"
115
+ "Interval legend layout", config
144
116
  );
145
117
  const background = resolveLegendBackgroundFromBounds(
146
118
  occupiedBounds,
147
119
  config.border,
148
120
  canvas,
149
- "Interval legend"
121
+ "Interval legend", config
150
122
  );
151
- return { labels, symbolX, labelX, itemY, title, background };
123
+ return { labels, ...layout, background };
152
124
  }
153
125
 
154
- export const rematerializeIntervalLegend = action(
126
+ export const rematerializeIntervalLegend = /* @__PURE__ */ action(
155
127
  {
156
128
  op: "rematerializeIntervalLegend",
157
129
  description: "Rematerialize a discretized color interval legend."
@@ -162,12 +134,13 @@ export const rematerializeIntervalLegend = action(
162
134
  if (stored === undefined) {
163
135
  throw new Error("Interval legend requires stored configuration.");
164
136
  }
165
- const { encoding, scale, config } = resolveIntervalConfig(this, stored);
137
+ const { encoding, scale, config: currentConfig } = resolveIntervalConfig(this, stored);
138
+ const config = resolveEffectiveLegendBlockConfig(this, "interval", currentConfig);
166
139
  const layout = resolveIntervalLayout(this, config, scale);
167
140
  let next = editGraphicProperties(this
168
141
  .editSemantic({ property: "guide.legend.color.scale", value: encoding.scale })
169
142
  .editSemantic({ property: "guide.legend.color.title", value: config.title })
170
- ._withLegendConfig("interval", config), "colorLegendSymbols", {
143
+ ._withLegendConfig("interval", currentConfig), "colorLegendSymbols", {
171
144
  length: scale.range.length,
172
145
  x: layout.symbolX,
173
146
  y: layout.itemY.map(value => value - config.symbol.height / 2),
@@ -175,7 +148,10 @@ export const rematerializeIntervalLegend = action(
175
148
  height: config.symbol.height,
176
149
  fill: scale.range,
177
150
  stroke: config.symbol.stroke,
178
- strokeWidth: config.symbol.strokeWidth
151
+ strokeWidth: config.symbol.strokeWidth,
152
+ ...(config.blockSymbol?.opacity === undefined
153
+ ? {}
154
+ : { opacity: config.blockSymbol.opacity })
179
155
  });
180
156
  next = editGraphicProperties(next, "colorLegendLabels", {
181
157
  length: layout.labels.length,
@@ -196,11 +172,57 @@ export const rematerializeIntervalLegend = action(
196
172
  y: layout.title.y,
197
173
  text: config.title
198
174
  });
199
- return styleContinuousText(next, "colorLegendTitle", config.titleStyle);
175
+ return styleContinuousText(next, "colorLegendTitle", config.titleStyle, { align: layout.title.align });
200
176
  }
201
177
  );
202
178
 
203
- export const createIntervalLegend = action(
179
+ export function createIntervalLegendFromConfig(program, config) {
180
+ const resolved = resolveIntervalConfig(program, config);
181
+ resolveIntervalLayout(program, resolved.config, resolved.scale);
182
+ let next = program
183
+ .editSemantic({
184
+ property: "guide.legend.color.scale",
185
+ value: resolved.encoding.scale
186
+ })
187
+ .editSemantic({
188
+ property: "guide.legend.color.title",
189
+ value: resolved.config.title
190
+ })
191
+ ._withLegendConfig("interval", resolved.config);
192
+ const placement = resolveLegendGraphicPlacement(next);
193
+ if (resolved.config.border !== false) {
194
+ next = next.createGraphics({
195
+ id: "colorLegendBackground",
196
+ type: "rect",
197
+ ...placement
198
+ });
199
+ }
200
+ next = next
201
+ .createGraphics({
202
+ id: "colorLegendSymbols",
203
+ type: "rect",
204
+ length: 0,
205
+ ...resolveLegendGraphicPlacement(next, resolved.config.border === false
206
+ ? {}
207
+ : { after: "colorLegendBackground" })
208
+ })
209
+ .createGraphics({
210
+ id: "colorLegendLabels",
211
+ type: "text",
212
+ length: 0,
213
+ ...placement
214
+ });
215
+ if (resolved.config.titleVisible !== false) {
216
+ next = next.createGraphics({
217
+ id: "colorLegendTitle",
218
+ type: "text",
219
+ ...placement
220
+ });
221
+ }
222
+ return next.rematerializeIntervalLegend();
223
+ }
224
+
225
+ export const createIntervalLegend = /* @__PURE__ */ action(
204
226
  {
205
227
  op: "createIntervalLegend",
206
228
  description: "Create a discretized color interval legend."
@@ -212,44 +234,6 @@ export const createIntervalLegend = action(
212
234
  if (this.graphicSpec.objects.colorLegendSymbols !== undefined) {
213
235
  throw new Error("createIntervalLegend requires a missing interval legend.");
214
236
  }
215
- let next = this
216
- .editSemantic({
217
- property: "guide.legend.color.scale",
218
- value: resolved.encoding.scale
219
- })
220
- .editSemantic({
221
- property: "guide.legend.color.title",
222
- value: resolved.config.title
223
- })
224
- ._withLegendConfig("interval", resolved.config);
225
- const placement = resolveLegendGraphicPlacement(next);
226
- if (resolved.config.border !== false) {
227
- next = next.createGraphics({
228
- id: "colorLegendBackground",
229
- type: "rect",
230
- ...placement
231
- });
232
- }
233
- return next
234
- .createGraphics({
235
- id: "colorLegendSymbols",
236
- type: "rect",
237
- length: 0,
238
- ...resolveLegendGraphicPlacement(next, resolved.config.border === false
239
- ? {}
240
- : { after: "colorLegendBackground" })
241
- })
242
- .createGraphics({
243
- id: "colorLegendLabels",
244
- type: "text",
245
- length: 0,
246
- ...placement
247
- })
248
- .createGraphics({
249
- id: "colorLegendTitle",
250
- type: "text",
251
- ...placement
252
- })
253
- .rematerializeIntervalLegend();
237
+ return createIntervalLegendFromConfig(this, resolved.config);
254
238
  }
255
239
  );
@@ -11,16 +11,17 @@ import {
11
11
  normalizeContinuousLegend,
12
12
  requireResolvedLegendScale,
13
13
  resolveContinuousBounds,
14
- resolveContinuousPoint,
14
+ resolveContinuousLegendLayer,
15
15
  resolveLegendBackgroundFromBounds,
16
16
  resolveLegendTextBounds,
17
- sampleContinuousValues,
18
17
  styleContinuousText,
19
18
  validateNonNegative,
20
19
  validatePositive
21
20
  } from "./common.js";
22
21
  import { resolveLegendGraphicPlacement } from
23
22
  "../../../../materialization/graphicHierarchy.js";
23
+ import { resolveLegendSampleValues } from "../sampling.js";
24
+ import { resolveEffectiveLegendBlockConfig } from "../blocks.js";
24
25
 
25
26
  const SYMBOL_OPTIONS = Object.freeze([
26
27
  "type", "radius", "fill", "stroke", "strokeWidth"
@@ -62,7 +63,7 @@ export function normalizeOpacitySymbol(value) {
62
63
  }
63
64
 
64
65
  function resolveOpacityConfig(program, config) {
65
- const layer = resolveContinuousPoint(program, config.target, "opacity");
66
+ const layer = resolveContinuousLegendLayer(program, config.target, "opacity");
66
67
  const encoding = layer.encoding.opacity;
67
68
  if (encoding.fieldType !== "quantitative") {
68
69
  throw new Error("Opacity legend requires quantitative field opacity.");
@@ -85,9 +86,15 @@ function resolveOpacityConfig(program, config) {
85
86
  function resolveOpacityLayout(program, config, scale) {
86
87
  const { plot, canvas } = resolveContinuousBounds(program);
87
88
  const vertical = ["right", "left"].includes(config.position);
88
- const values = sampleContinuousValues(scale.domain, config.count);
89
- const texts = formatContinuousValues(values, scale.domain, "quantitative");
90
- const radius = config.symbol.radius;
89
+ const values = resolveLegendSampleValues(config, scale, "Opacity legend");
90
+ const texts = formatContinuousValues(
91
+ values,
92
+ scale.domain,
93
+ "quantitative",
94
+ config.labels.format
95
+ );
96
+ const symbolExtent = config.symbol.radius + (config.symbol.strokeWidth ?? 0) / 2;
97
+ const labelWidths = texts.map(text => measureTextWidth(text, config.labels));
91
98
  let symbols;
92
99
  let labels;
93
100
  let title;
@@ -95,57 +102,61 @@ function resolveOpacityLayout(program, config, scale) {
95
102
  const baseX = config.position === "right"
96
103
  ? plot.x + plot.width + config.offset
97
104
  : plot.x - config.offset;
98
- const y = values.map((_, index) => plot.y + 46 + index * config.itemGap);
105
+ const itemHeight = Math.max(symbolExtent * 2, config.labels.fontSize);
106
+ const pitch = Math.max(config.itemGap, itemHeight);
107
+ const firstY = Math.max(plot.y + 46, config.titleVisible === false ? plot.y + 46
108
+ : plot.y + 20 + config.titleStyle.fontSize / 2 + 12 + itemHeight / 2);
109
+ const y = values.map((_, index) => firstY + index * pitch);
99
110
  symbols = y.map(itemY => ({
100
- x: config.position === "right" ? baseX + radius : baseX - radius,
111
+ x: config.position === "right" ? baseX + symbolExtent : baseX - symbolExtent,
101
112
  y: itemY
102
113
  }));
103
114
  labels = y.map(itemY => ({
104
115
  x: config.position === "right"
105
- ? baseX + radius * 2 + config.labels.offset - 2
106
- : baseX - radius * 2 - config.labels.offset + 2,
116
+ ? baseX + symbolExtent * 2 + config.labels.offset
117
+ : baseX - symbolExtent * 2 - config.labels.offset,
107
118
  y: itemY,
108
119
  align: config.position === "right" ? "left" : "right"
109
120
  }));
110
121
  title = {
111
- x: config.position === "right" ? baseX : baseX - radius * 2,
122
+ x: config.position === "right" ? baseX : baseX - symbolExtent * 2,
112
123
  y: plot.y + 20,
113
124
  align: config.position === "right" ? "left" : "right"
114
125
  };
115
126
  } else if (config.titlePosition === "left") {
116
- const titleWidth = measureTextWidth(config.title, config.titleStyle);
117
- const labelWidths = texts.map(text =>
118
- measureTextWidth(text, config.labels)
119
- );
127
+ const titleWidth = config.titleVisible === false ? 0 : measureTextWidth(config.title, config.titleStyle);
128
+ const titlePrefix = config.titleVisible === false ? 0 : titleWidth + 20;
120
129
  const samplesWidth = labelWidths.reduce(
121
- (sum, width) => sum + radius * 2 + config.labels.offset + width,
130
+ (sum, width) => sum + symbolExtent * 2 + config.labels.offset + width,
122
131
  0
123
132
  ) + config.itemGap * (values.length - 1);
124
- const width = titleWidth + 20 + samplesWidth;
133
+ const width = titlePrefix + samplesWidth;
125
134
  const startX = config.align === "left" ? plot.x
126
135
  : config.align === "right" ? plot.x + plot.width - width
127
136
  : plot.x + (plot.width - width) / 2;
128
137
  const height = Math.max(
129
- radius * 2,
138
+ symbolExtent * 2,
130
139
  config.labels.fontSize,
131
- config.titleStyle.fontSize
140
+ config.titleVisible === false ? 0 : config.titleStyle.fontSize
132
141
  );
133
142
  const centerY = config.position === "top"
134
143
  ? plot.y - config.offset - height / 2
135
144
  : plot.y + plot.height + config.offset + height / 2;
136
145
  title = { x: startX, y: centerY, align: "left" };
137
- let cursor = startX + titleWidth + 20;
146
+ let cursor = startX + titlePrefix;
138
147
  symbols = [];
139
148
  labels = [];
140
149
  for (let index = 0; index < values.length; index += 1) {
141
- const symbolX = cursor + radius;
150
+ const symbolX = cursor + symbolExtent;
142
151
  symbols.push({ x: symbolX, y: centerY });
143
- const labelX = symbolX + radius + config.labels.offset;
152
+ const labelX = symbolX + symbolExtent + config.labels.offset;
144
153
  labels.push({ x: labelX, y: centerY, align: "left" });
145
154
  cursor = labelX + labelWidths[index] + config.itemGap;
146
155
  }
147
156
  } else {
148
- const width = (values.length - 1) * Math.max(56, config.itemGap * 2);
157
+ const pitch = Math.max(56, config.itemGap * 2,
158
+ symbolExtent * 2 + config.itemGap, Math.max(...labelWidths) + config.itemGap);
159
+ const width = (values.length - 1) * pitch;
149
160
  const startX = config.align === "left" ? plot.x
150
161
  : config.align === "right" ? plot.x + plot.width - width
151
162
  : plot.x + (plot.width - width) / 2;
@@ -153,27 +164,26 @@ function resolveOpacityLayout(program, config, scale) {
153
164
  const labelHalf = config.labels.fontSize / 2;
154
165
  const y = config.position === "top"
155
166
  ? plot.y - config.offset - config.labels.fontSize -
156
- config.labels.offset - radius
167
+ config.labels.offset - symbolExtent
157
168
  : plot.y + plot.height + config.offset +
158
- config.titleStyle.fontSize + 12 + radius;
169
+ (config.titleVisible === false ? 0 : config.titleStyle.fontSize + 12) + symbolExtent;
170
+ const samplePitch = values.length === 1 ? 0 : width / (values.length - 1);
159
171
  symbols = values.map((_, index) => ({
160
- x: startX + index * width / (values.length - 1),
172
+ x: startX + index * samplePitch,
161
173
  y
162
174
  }));
163
175
  labels = symbols.map(symbol => ({
164
176
  x: symbol.x,
165
- y: symbol.y + radius + config.labels.offset + labelHalf,
177
+ y: symbol.y + symbolExtent + config.labels.offset + labelHalf,
166
178
  align: "center"
167
179
  }));
168
180
  title = {
169
181
  x: startX + width / 2,
170
- y: y - radius - 12 - titleHalf,
182
+ y: y - symbolExtent - 12 - titleHalf,
171
183
  align: "center"
172
184
  };
173
185
  }
174
- const strokeExtent = (config.symbol.strokeWidth ?? 0) / 2;
175
- const symbolExtent = radius + strokeExtent;
176
- const titleBounds = resolveLegendTextBounds(
186
+ const titleBounds = config.titleVisible === false ? undefined : resolveLegendTextBounds(
177
187
  title,
178
188
  config.title,
179
189
  config.titleStyle
@@ -187,22 +197,22 @@ function resolveOpacityLayout(program, config, scale) {
187
197
  top: symbol.y - symbolExtent,
188
198
  bottom: symbol.y + symbolExtent
189
199
  }));
190
- const occupiedBounds = [titleBounds, ...labelBounds, ...symbolBounds];
200
+ const occupiedBounds = [...(config.titleVisible === false ? [] : [titleBounds]), ...labelBounds, ...symbolBounds];
191
201
  assertLegendBoundsInsideCanvas(
192
202
  occupiedBounds,
193
203
  canvas,
194
- "Opacity legend layout"
204
+ "Opacity legend layout", config
195
205
  );
196
206
  const background = resolveLegendBackgroundFromBounds(
197
207
  occupiedBounds,
198
208
  config.border,
199
209
  canvas,
200
- "Opacity legend"
210
+ "Opacity legend", config
201
211
  );
202
212
  return { values, texts, symbols, labels, title, background };
203
213
  }
204
214
 
205
- export const rematerializeOpacityLegend = action(
215
+ export const rematerializeOpacityLegend = /* @__PURE__ */ action(
206
216
  {
207
217
  op: "rematerializeOpacityLegend",
208
218
  description: "Rematerialize a field-opacity sample legend."
@@ -213,7 +223,8 @@ export const rematerializeOpacityLegend = action(
213
223
  if (stored === undefined) {
214
224
  throw new Error("Opacity legend requires stored configuration.");
215
225
  }
216
- const { encoding, scale, config } = resolveOpacityConfig(this, stored);
226
+ const { encoding, scale, config: currentConfig } = resolveOpacityConfig(this, stored);
227
+ const config = resolveEffectiveLegendBlockConfig(this, "opacity", currentConfig);
217
228
  const layout = resolveOpacityLayout(this, config, scale);
218
229
  const opacities = mapLinearValues(layout.values, scale.domain, scale.range, {
219
230
  clamp: scale.clamp ?? false
@@ -227,7 +238,7 @@ export const rematerializeOpacityLegend = action(
227
238
  property: "guide.legend.opacity.title",
228
239
  value: config.title
229
240
  })
230
- ._withLegendConfig("opacity", config)
241
+ ._withLegendConfig("opacity", currentConfig)
231
242
  .editGraphics({
232
243
  target: "opacityLegendSymbols",
233
244
  property: "length",
@@ -330,72 +341,85 @@ export const rematerializeOpacityLegend = action(
330
341
  }
331
342
  );
332
343
 
333
- export const createOpacityLegend = action(
344
+
345
+ export function resolveOpacityLegendCreation(program, args = {}) {
346
+ const config = normalizeContinuousLegend(args, "opacity");
347
+ if (args.channels !== undefined && (
348
+ !Array.isArray(args.channels) ||
349
+ args.channels.length !== 1 ||
350
+ args.channels[0] !== "opacity"
351
+ )) {
352
+ throw new Error('Opacity legend requires channels: ["opacity"].');
353
+ }
354
+ config.symbol = normalizeOpacitySymbol(args.symbol);
355
+ config.titleVisible = true;
356
+ const resolved = resolveOpacityConfig(program, config);
357
+ return resolved;
358
+ }
359
+
360
+ export function createOpacityLegendFromConfig(program, config) {
361
+ const resolved = resolveOpacityConfig(program, config);
362
+ resolveOpacityLayout(program, resolved.config, resolved.scale);
363
+ let next = program
364
+ .editSemantic({
365
+ property: "guide.legend.opacity.scale",
366
+ value: resolved.encoding.scale
367
+ })
368
+ .editSemantic({
369
+ property: "guide.legend.opacity.title",
370
+ value: resolved.config.title
371
+ })
372
+ ._withLegendConfig("opacity", resolved.config);
373
+ if (resolved.config.border !== false) {
374
+ next = next.createGraphics({
375
+ id: "opacityLegendBackground",
376
+ type: "rect",
377
+ ...resolveLegendGraphicPlacement(next)
378
+ });
379
+ }
380
+ next = next
381
+ .createGraphics({
382
+ id: "opacityLegendSymbols",
383
+ type: "circle",
384
+ length: 0,
385
+ ...resolveLegendGraphicPlacement(next, resolved.config.border === false
386
+ ? {}
387
+ : { after: "opacityLegendBackground" })
388
+ })
389
+ .createGraphics({
390
+ id: "opacityLegendLabels",
391
+ type: "text",
392
+ length: 0,
393
+ ...resolveLegendGraphicPlacement(next)
394
+ });
395
+ if (resolved.config.titleVisible !== false) {
396
+ next = next.createGraphics({
397
+ id: "opacityLegendTitle",
398
+ type: "text",
399
+ ...resolveLegendGraphicPlacement(next)
400
+ });
401
+ }
402
+ return next.rematerializeOpacityLegend();
403
+ }
404
+
405
+ export const createOpacityLegend = /* @__PURE__ */ action(
334
406
  {
335
407
  op: "createOpacityLegend",
336
408
  description: "Create a field-opacity sample legend."
337
409
  },
338
410
  function (args = {}) {
339
- const config = normalizeContinuousLegend(args, "opacity");
340
- if (args.channels !== undefined && (
341
- !Array.isArray(args.channels) ||
342
- args.channels.length !== 1 ||
343
- args.channels[0] !== "opacity"
344
- )) {
345
- throw new Error('Opacity legend requires channels: ["opacity"].');
346
- }
347
- config.symbol = normalizeOpacitySymbol(args.symbol);
348
- config.titleVisible = true;
349
- const resolved = resolveOpacityConfig(this, config);
411
+ const resolved = resolveOpacityLegendCreation(this, args);
350
412
  resolveOpacityLayout(this, resolved.config, resolved.scale);
351
413
  if (this.graphicSpec.objects.opacityLegendSymbols !== undefined) {
352
414
  throw new Error(
353
415
  "createOpacityLegend requires a missing opacity legend."
354
416
  );
355
417
  }
356
- let next = this
357
- .editSemantic({
358
- property: "guide.legend.opacity.scale",
359
- value: resolved.encoding.scale
360
- })
361
- .editSemantic({
362
- property: "guide.legend.opacity.title",
363
- value: resolved.config.title
364
- })
365
- ._withLegendConfig("opacity", resolved.config);
366
- if (resolved.config.border !== false) {
367
- next = next.createGraphics({
368
- id: "opacityLegendBackground",
369
- type: "rect",
370
- ...resolveLegendGraphicPlacement(next)
371
- });
372
- }
373
- return next
374
- .createGraphics({
375
- id: "opacityLegendSymbols",
376
- type: "circle",
377
- length: 0,
378
- ...resolveLegendGraphicPlacement(next),
379
- ...(resolved.config.border === false
380
- ? {}
381
- : { after: "opacityLegendBackground" })
382
- })
383
- .createGraphics({
384
- id: "opacityLegendLabels",
385
- type: "text",
386
- length: 0,
387
- ...resolveLegendGraphicPlacement(next)
388
- })
389
- .createGraphics({
390
- id: "opacityLegendTitle",
391
- type: "text",
392
- ...resolveLegendGraphicPlacement(next)
393
- })
394
- .rematerializeOpacityLegend();
418
+ return createOpacityLegendFromConfig(this, resolved.config);
395
419
  }
396
420
  );
397
421
 
398
- export const removeOpacityLegend = action(
422
+ export const removeOpacityLegend = /* @__PURE__ */ action(
399
423
  {
400
424
  op: "removeOpacityLegend",
401
425
  description: "Remove a field-opacity legend after switching to constant opacity."