ggaction 0.0.17 → 0.0.19

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 (315) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +6 -2
  3. package/knowledge/action-card.schema.json +1 -1
  4. package/knowledge/action-cards.json +1 -1
  5. package/knowledge/intent-taxonomy.json +1 -1
  6. package/knowledge/mcp-resources.json +1 -1
  7. package/knowledge/task-resolver.js +1 -1
  8. package/package.json +3 -3
  9. package/src/BasicChartProgram.js +1 -1
  10. package/src/accessibility.js +3 -2
  11. package/src/actions/boxPlots/create.js +14 -4
  12. package/src/actions/boxPlots/edit.js +39 -33
  13. package/src/actions/boxPlots/materialize.js +7 -3
  14. package/src/actions/boxPlots/options.js +37 -11
  15. package/src/actions/canvas/actions.js +70 -95
  16. package/src/actions/canvas/fitting.js +20 -1
  17. package/src/actions/canvas/index.js +3 -0
  18. package/src/actions/canvas/plotBorder.js +28 -0
  19. package/src/actions/charts/area.js +2 -2
  20. package/src/actions/charts/bar.js +34 -7
  21. package/src/actions/charts/ecdf.js +4 -18
  22. package/src/actions/charts/endpoints.js +4 -18
  23. package/src/actions/charts/heatmap.js +8 -7
  24. package/src/actions/charts/index.js +2 -0
  25. package/src/actions/charts/interval-regression.js +2 -2
  26. package/src/actions/charts/line.js +1 -1
  27. package/src/actions/charts/pie.js +3 -47
  28. package/src/actions/charts/radial.js +34 -12
  29. package/src/actions/charts/scatter.js +16 -2
  30. package/src/actions/charts/shared.js +2 -2
  31. package/src/actions/charts/text.js +35 -0
  32. package/src/actions/composition/actions.js +6 -2
  33. package/src/actions/data/box.js +2 -1
  34. package/src/actions/data/derived.js +11 -1
  35. package/src/actions/data/edit.js +3 -1
  36. package/src/actions/data/index.js +4 -0
  37. package/src/actions/data/regression.js +25 -16
  38. package/src/actions/data/regressionStatistics.js +73 -0
  39. package/src/actions/data/shared.js +8 -4
  40. package/src/actions/data/stack.js +3 -1
  41. package/src/actions/distributions/revision.js +24 -18
  42. package/src/actions/encodings/appearance.js +37 -29
  43. package/src/actions/encodings/barWidth.js +36 -11
  44. package/src/actions/encodings/channels.js +0 -1
  45. package/src/actions/encodings/color/continuous.js +1 -1
  46. package/src/actions/encodings/color/index.js +1 -1
  47. package/src/actions/encodings/color/policy.js +10 -3
  48. package/src/actions/encodings/density/resolve.js +1 -6
  49. package/src/actions/encodings/position/policies/arc.js +12 -10
  50. package/src/actions/encodings/position/policies/area.js +3 -3
  51. package/src/actions/encodings/position/policies/bar.js +15 -6
  52. package/src/actions/encodings/position/policies/line.js +17 -6
  53. package/src/actions/encodings/position/resolve.js +3 -2
  54. package/src/actions/encodings/ranged.js +17 -0
  55. package/src/actions/encodings/remove.js +14 -8
  56. package/src/actions/encodings/seriesLayout.js +1 -0
  57. package/src/actions/encodings/shared.js +26 -24
  58. package/src/actions/encodings/text.js +6 -3
  59. package/src/actions/errorBands/create.js +2 -2
  60. package/src/actions/errorBands/edit.js +2 -1
  61. package/src/actions/facets/actions.js +176 -247
  62. package/src/actions/facets/guides.js +2 -1
  63. package/src/actions/gradientPlots/resolve.js +2 -17
  64. package/src/actions/guides/applicability.js +1 -1
  65. package/src/actions/guides/axes/axes.js +30 -11
  66. package/src/actions/guides/axes/axis.js +72 -10
  67. package/src/actions/guides/axes/components.js +13 -13
  68. package/src/actions/guides/axes/edit.js +36 -24
  69. package/src/actions/guides/axes/labels.js +155 -71
  70. package/src/actions/guides/axes/lines.js +27 -24
  71. package/src/actions/guides/axes/parallel/lifecycle.js +1 -1
  72. package/src/actions/guides/axes/parallel/policy.js +8 -7
  73. package/src/actions/guides/axes/remove.js +13 -8
  74. package/src/actions/guides/axes/tickGroups.js +11 -7
  75. package/src/actions/guides/axes/ticks.js +36 -24
  76. package/src/actions/guides/axes/titles.js +107 -69
  77. package/src/actions/guides/basic.js +2 -0
  78. package/src/actions/guides/facadeAxes.js +33 -24
  79. package/src/actions/guides/facadeLegend.js +12 -4
  80. package/src/actions/guides/grids/grid.js +8 -2
  81. package/src/actions/guides/grids/resolve.js +102 -58
  82. package/src/actions/guides/legends/blocks.js +46 -20
  83. package/src/actions/guides/legends/categorical/actions.js +95 -19
  84. package/src/actions/guides/legends/categorical/components.js +15 -18
  85. package/src/actions/guides/legends/categorical/index.js +8 -0
  86. package/src/actions/guides/legends/categorical/layout.js +45 -20
  87. package/src/actions/guides/legends/categorical/options.js +39 -22
  88. package/src/actions/guides/legends/categorical/recipes.js +7 -3
  89. package/src/actions/guides/legends/categorical/resolve.js +11 -4
  90. package/src/actions/guides/legends/categorical/symbols.js +38 -18
  91. package/src/actions/guides/legends/categorical/validation.js +1 -6
  92. package/src/actions/guides/legends/colorSize.js +110 -0
  93. package/src/actions/guides/legends/combine.js +37 -0
  94. package/src/actions/guides/legends/continuous/colorSymbols.js +1 -0
  95. package/src/actions/guides/legends/continuous/common.js +1 -447
  96. package/src/actions/guides/legends/continuous/gradient.js +73 -46
  97. package/src/actions/guides/legends/continuous/interval.js +4 -4
  98. package/src/actions/guides/legends/continuous/opacity.js +33 -21
  99. package/src/actions/guides/legends/creation.js +2 -0
  100. package/src/actions/guides/legends/edit.js +1 -604
  101. package/src/actions/guides/legends/focused.js +8 -7
  102. package/src/actions/guides/legends/group.js +1 -0
  103. package/src/actions/guides/legends/lane.js +1 -516
  104. package/src/actions/guides/legends/lifecycle.js +2 -1
  105. package/src/actions/guides/legends/remove.js +9 -3
  106. package/src/actions/guides/legends/sampling.js +1 -1
  107. package/src/actions/guides/legends/size.js +65 -47
  108. package/src/actions/guides/legends/strokeWidth.js +13 -29
  109. package/src/actions/guides/legends/target.js +2 -79
  110. package/src/actions/guides/legends/transition.js +44 -11
  111. package/src/actions/guides/polar/axes/labels.js +4 -2
  112. package/src/actions/guides/polar/axes/titles.js +7 -5
  113. package/src/actions/guides/polar/resolve.js +6 -14
  114. package/src/actions/guides/tickValues.js +8 -5
  115. package/src/actions/index.js +16 -16
  116. package/src/actions/marks/arc/actions.js +3 -10
  117. package/src/actions/marks/area/actions.js +8 -5
  118. package/src/actions/marks/area/materialize.js +8 -9
  119. package/src/actions/marks/bar/create.js +10 -4
  120. package/src/actions/marks/bar/edit.js +29 -14
  121. package/src/actions/marks/bar/materialize.js +15 -7
  122. package/src/actions/marks/line/actions.js +11 -4
  123. package/src/actions/marks/line/materialize.js +9 -5
  124. package/src/actions/marks/point/edit.js +1 -1
  125. package/src/actions/marks/point/materialize.js +5 -1
  126. package/src/actions/marks/rect/actions.js +3 -3
  127. package/src/actions/marks/remove.js +12 -10
  128. package/src/actions/marks/rule/actions.js +7 -4
  129. package/src/actions/marks/text/actions.js +17 -5
  130. package/src/actions/marks/text/layout.js +3 -0
  131. package/src/actions/primitives/createGraphics.js +3 -2
  132. package/src/actions/primitives/editGraphics.js +15 -11
  133. package/src/actions/primitives/graphicProperties.js +1 -6
  134. package/src/actions/primitives/semanticAction.js +27 -1
  135. package/src/actions/primitives/semanticValidation/guide.js +11 -1
  136. package/src/actions/primitives/semanticValidation/layer.js +8 -0
  137. package/src/actions/regression/create.js +1 -1
  138. package/src/actions/regression/edit.js +18 -6
  139. package/src/actions/regression/resolve.js +2 -6
  140. package/src/actions/scales/channels.js +9 -4
  141. package/src/actions/scales/consumers/families.js +16 -5
  142. package/src/actions/scales/create.js +10 -5
  143. package/src/actions/scales/definitions.js +1 -425
  144. package/src/actions/scales/edit.js +3 -3
  145. package/src/actions/scales/editPolicy.js +12 -3
  146. package/src/actions/scales/materialize.js +1 -1
  147. package/src/actions/scales/patch.js +1 -1
  148. package/src/actions/scales/preview.js +21 -1
  149. package/src/actions/scales/quantitativeColor.js +8 -3
  150. package/src/actions/selection/actions.js +1 -13
  151. package/src/actions/theme/reconcile.js +126 -45
  152. package/src/actions/theme/state.js +12 -0
  153. package/src/actions/titles/actions.js +5 -18
  154. package/src/actions/titles/resolve.js +41 -19
  155. package/src/actions/violinPlots/edit.js +3 -20
  156. package/src/core/compositionState.js +6 -3
  157. package/src/core/font.js +9 -2
  158. package/src/core/graphicProperties.js +1 -0
  159. package/src/core/optionRequirements.js +1 -0
  160. package/src/core/programState.js +4 -0
  161. package/src/core/resourceReferences.js +5 -2
  162. package/src/core/textMetricProfile.js +3 -2
  163. package/src/core/textMetrics.js +41 -4
  164. package/src/core/validation.js +35 -0
  165. package/src/core/vocabulary.js +7 -0
  166. package/src/grammar/aggregate.js +4 -18
  167. package/src/grammar/arcs.js +1 -386
  168. package/src/grammar/areaPaint.js +10 -0
  169. package/src/grammar/areaSeries.js +18 -10
  170. package/src/grammar/axisTextPlacement.js +1 -0
  171. package/src/grammar/bars/geometry.js +30 -3
  172. package/src/grammar/bars/policy.js +12 -1
  173. package/src/grammar/bin.js +2 -12
  174. package/src/grammar/bin2d.js +1 -6
  175. package/src/grammar/boxPlot.js +56 -9
  176. package/src/grammar/categoryOrder.js +1 -6
  177. package/src/grammar/colorSizeLegend.js +39 -0
  178. package/src/grammar/complete.js +15 -19
  179. package/src/grammar/computed.js +115 -26
  180. package/src/grammar/curveCommands.js +29 -16
  181. package/src/grammar/datasetSchema.js +13 -9
  182. package/src/grammar/displayLabels.js +36 -0
  183. package/src/grammar/ecdf.js +2 -9
  184. package/src/grammar/facets/dependencies.js +6 -2
  185. package/src/grammar/facets/guides.js +58 -30
  186. package/src/grammar/facets/index.js +2 -5
  187. package/src/grammar/facets/legendIntent.js +65 -0
  188. package/src/grammar/fold.js +2 -12
  189. package/src/grammar/gradientProfile.js +1 -6
  190. package/src/grammar/guideDash.js +1 -0
  191. package/src/grammar/impute.js +21 -48
  192. package/src/grammar/interval.js +2 -9
  193. package/src/grammar/legendBlocks.js +1 -0
  194. package/src/grammar/legendBorder.js +9 -0
  195. package/src/grammar/legendClip.js +19 -0
  196. package/src/grammar/legendCombinations.js +45 -0
  197. package/src/grammar/legendGradient.js +27 -0
  198. package/src/grammar/legendGroups.js +1 -0
  199. package/src/grammar/legendOpacity.js +6 -0
  200. package/src/grammar/legendTitleStyle.js +22 -0
  201. package/src/grammar/lineSeries.js +32 -17
  202. package/src/grammar/markLabels.js +5 -2
  203. package/src/grammar/normalize.js +4 -54
  204. package/src/grammar/numeric.js +1 -290
  205. package/src/grammar/parallelCoordinates.js +1 -6
  206. package/src/grammar/parseTime.js +41 -0
  207. package/src/grammar/pathCommands.js +11 -4
  208. package/src/grammar/pointShapes.js +16 -2
  209. package/src/grammar/polar.js +1 -184
  210. package/src/grammar/positionCompatibility.js +3 -3
  211. package/src/grammar/regression/derive.js +16 -2
  212. package/src/grammar/regression/models.js +142 -54
  213. package/src/grammar/regression/parameters.js +23 -4
  214. package/src/grammar/regression/statistics.js +64 -0
  215. package/src/grammar/roundedRect.js +42 -54
  216. package/src/grammar/scales/appearance.js +1 -206
  217. package/src/grammar/scales/color.js +1 -382
  218. package/src/grammar/scales/colorConsumers.js +5 -2
  219. package/src/grammar/scales/definition.js +1 -197
  220. package/src/grammar/scales/discretized.js +1 -195
  221. package/src/grammar/scales/size.js +1 -456
  222. package/src/grammar/scales/temporal.js +2 -13
  223. package/src/grammar/scales/transformed.js +1 -383
  224. package/src/grammar/scales/types.js +1 -1
  225. package/src/grammar/scales/validation.js +1 -66
  226. package/src/grammar/schemas/concreteGraphic.js +14 -0
  227. package/src/grammar/schemas/graphic.js +6 -0
  228. package/src/grammar/schemas/graphicBounds.js +31 -7
  229. package/src/grammar/schemas/semanticPath.js +19 -3
  230. package/src/grammar/stack.js +2 -10
  231. package/src/grammar/statisticalReference.js +1 -6
  232. package/src/grammar/summary.js +4 -27
  233. package/src/grammar/text.js +19 -0
  234. package/src/grammar/timeUnit.js +22 -11
  235. package/src/grammar/transformKeys.js +47 -0
  236. package/src/grammar/transformTopology.js +1 -0
  237. package/src/grammar/transforms.js +15 -2
  238. package/src/grammar/valueFormat.js +17 -5
  239. package/src/grammar/weightedStatistics.js +1 -6
  240. package/src/grammar/window.js +4 -58
  241. package/src/inspectionDescriptors.js +1 -1
  242. package/src/layout/canvas.js +1 -110
  243. package/src/layout/facets.js +1 -244
  244. package/src/layout/guideCollisions.js +9 -1
  245. package/src/layout/labels.js +1 -0
  246. package/src/layout/legend.js +13 -0
  247. package/src/layout/legendGroup.js +1 -0
  248. package/src/layout/legendItems.js +59 -15
  249. package/src/layout/legendLane.js +1 -441
  250. package/src/layout/legendText.js +1 -0
  251. package/src/layout/title.js +35 -13
  252. package/src/materialization/bars/aggregate.js +11 -5
  253. package/src/materialization/bars/histogram.js +4 -1
  254. package/src/materialization/bars/ranged.js +19 -6
  255. package/src/materialization/bars/resolve.js +22 -1
  256. package/src/materialization/compositionSnapshot.js +2 -7
  257. package/src/materialization/coordinateBounds.js +21 -0
  258. package/src/materialization/dependencies.js +4 -44
  259. package/src/materialization/facetGuides/edit.js +30 -0
  260. package/src/materialization/facetGuides/layout.js +1 -0
  261. package/src/materialization/facetGuides/owner.js +125 -0
  262. package/src/materialization/facetGuides/ownerLayout.js +71 -0
  263. package/src/materialization/facetGuides/placement.js +71 -14
  264. package/src/materialization/facetGuides/preparation.js +22 -7
  265. package/src/materialization/facetHeaders.js +1 -347
  266. package/src/materialization/facets.js +1 -185
  267. package/src/materialization/graphicHierarchy.js +5 -1
  268. package/src/materialization/graphicTranslation.js +73 -0
  269. package/src/materialization/guides/layout.js +1 -57
  270. package/src/materialization/guides/resources.js +1 -123
  271. package/src/materialization/layout.js +53 -0
  272. package/src/materialization/legendBorder.js +28 -0
  273. package/src/materialization/legendClip.js +76 -0
  274. package/src/materialization/legendGroups.js +1 -0
  275. package/src/materialization/legendText.js +1 -0
  276. package/src/materialization/legends.js +2 -2
  277. package/src/materialization/marks/capabilities.js +5 -6
  278. package/src/materialization/rect.js +1 -1
  279. package/src/materialization/scaleGuideDependencies.js +14 -15
  280. package/src/materialization/scales/map.js +6 -0
  281. package/src/materialization/scales/policies/arc.js +8 -7
  282. package/src/materialization/scales/policies/bar.js +19 -7
  283. package/src/materialization/scales/policies/series.js +2 -1
  284. package/src/materialization/scales/resolve.js +7 -3
  285. package/src/materialization/selection/items/arc.js +1 -1
  286. package/src/materialization/selection/items/bar.js +7 -6
  287. package/src/materialization/selection/items/path.js +7 -6
  288. package/src/materialization/selection/items/rect.js +2 -2
  289. package/src/materialization/text.js +13 -4
  290. package/src/materialization/textLines.js +15 -0
  291. package/src/materialization/typography.js +10 -2
  292. package/src/persistence/codec.js +37 -6
  293. package/src/persistence/validation.js +220 -0
  294. package/src/persistence.js +19 -6
  295. package/src/renderers/canvas/circle.js +6 -3
  296. package/src/renderers/canvas/fill.js +7 -1
  297. package/src/renderers/canvas/index.js +22 -11
  298. package/src/renderers/canvas/isolated.js +166 -0
  299. package/src/renderers/canvas/limits.js +3 -0
  300. package/src/renderers/canvas/line.js +1 -0
  301. package/src/renderers/canvas/path.js +1 -1
  302. package/src/renderers/canvas/rect.js +11 -8
  303. package/src/renderers/canvas/stroke.js +2 -1
  304. package/src/renderers/canvas/text.js +5 -3
  305. package/src/renderers/pdf.js +2 -2
  306. package/src/renderers/png.js +2 -2
  307. package/src/renderers/svg.js +14 -4
  308. package/src/selectors/axes.js +1 -0
  309. package/src/selectors/graphicCapacity.js +38 -0
  310. package/src/selectors/layers.js +19 -0
  311. package/src/theme/defaults.js +8 -2
  312. package/src/version.js +1 -1
  313. package/types/basic.d.ts +3 -0
  314. package/types/index.d.ts +12 -0
  315. package/types/program.d.ts +536 -129
@@ -1,604 +1 @@
1
- import { withGuideLayoutValidation } from "../../../materialization/guides/layout.js";
2
- import { resolveLegendStepConfig } from "./creation.js";
3
- import { normalizeLegendOrder } from "../../../grammar/categoryOrder.js";
4
- import { resolveDefinition } from "./categorical/resolve.js";
5
- import { action } from "../../../core/action.js";
6
- import {
7
- validateOptionObject
8
- } from "../../../core/validation.js";
9
- import { normalizeOptions } from "./categorical/options.js";
10
- import { resolveLegendSymbol } from "./categorical/recipes.js";
11
- import { resolveLayout } from "./categorical/layout.js";
12
- import { resolveLegendCreationPlan } from "./categorical/actions.js";
13
- import { categoricalSymbolIds, reconcileCategoricalSymbols, createCategoricalLegendFromConfig, resolveCategoricalLegendRevision, removeLegendKinds } from "./lifecycle.js";
14
- import { createGradientLegendFromConfig } from "./continuous/gradient.js";
15
- import {
16
- normalizeItemLegendLayout,
17
- normalizeLegendBorder,
18
- normalizeLegendTextOptions,
19
- normalizeLegendTitleOptions,
20
- normalizeContinuousLegend,
21
- validatePositive
22
- } from "./continuous/common.js";
23
- import { normalizeOpacitySymbol, createOpacityLegendFromConfig } from "./continuous/opacity.js";
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";
31
- import { findLayer } from "../../../selectors/layers.js";
32
- import { resolveLegendGraphicPlacement } from
33
- "../../../materialization/graphicHierarchy.js";
34
- import { resolveLegendTarget, validateLegendChannels } from "./target.js";
35
- import { isDiscreteSizeScaleType } from "../../../grammar/scales/index.js";
36
- import { SIZE_LEGEND_LABELS, SIZE_LEGEND_TITLE_STYLE, resolveSizeLegendLayout, createSizeLegendFromConfig } from "./size.js";
37
- import {
38
- STROKE_WIDTH_LEGEND_LABELS,
39
- STROKE_WIDTH_LEGEND_TITLE_STYLE,
40
- resolveStrokeWidthLegendLayout,
41
- createStrokeWidthLegendFromConfig
42
- } from "./strokeWidth.js";
43
- import { normalizeLegendSampling } from "./sampling.js";
44
- import { planLegendBlockTransitions } from "./transition.js";
45
-
46
- const OPTIONS = Object.freeze([
47
- "target", "channels", "position", "layout", "align", "direction", "columns", "offset",
48
- "titlePosition", "title", "symbol", "labels", "titleStyle", "itemGap",
49
- "border", "count", "values", "gradient", "order"
50
- ]);
51
-
52
- function mergeObject(previous, patch) {
53
- return patch === undefined
54
- ? previous
55
- : { ...previous, ...patch };
56
- }
57
-
58
- function mergeBorder(previous, patch) {
59
- if (patch === undefined) return previous;
60
- if (patch === false || patch === true) return patch;
61
- return previous === false ? patch : { ...previous, ...patch };
62
- }
63
-
64
- function reconcileGraphic(program, id, shouldExist, definition) {
65
- const exists = program.graphicSpec.objects[id] !== undefined;
66
- if (exists && !shouldExist) return program.editGraphics({ target: id, remove: true });
67
- if (!exists && shouldExist) {
68
- return program.createGraphics({
69
- id,
70
- ...definition,
71
- ...resolveLegendGraphicPlacement(program, {
72
- ...(definition.before === undefined ? {} : { before: definition.before }),
73
- ...(definition.after === undefined ? {} : { after: definition.after })
74
- })
75
- });
76
- }
77
- return program;
78
- }
79
-
80
- function resolveContinuousEdit(program, kind, previous, args) {
81
- const gradient = ["gradient", "strokeGradient"].includes(kind);
82
- const allowed = gradient
83
- ? ["target", "position", "align", "offset", "title", "labels",
84
- "titleStyle", "titlePosition", "border", "count", "gradient"]
85
- : ["target", "position", "align", "offset", "title", "symbol", "labels",
86
- "titleStyle", "titlePosition", "itemGap", "border", "count", "values"];
87
- for (const key of Object.keys(args)) {
88
- if (!allowed.includes(key)) throw new Error(`${kind} legend does not accept ${key}.`);
89
- }
90
- const titleMode = args.title;
91
- const inferredTitle = titleMode === "auto"
92
- ? true
93
- : typeof titleMode === "string" ? false : previous.inferredTitle;
94
- const titleVisible = titleMode === false ? false
95
- : titleMode === undefined ? previous.titleVisible !== false : true;
96
- const layer = findLayer(program, previous.target);
97
- const channel = kind === "gradient" ? "color"
98
- : kind === "strokeGradient" ? "stroke" : "opacity";
99
- const inferred = layer?.encoding?.[channel]?.field;
100
- const title = titleMode === "auto"
101
- ? inferred
102
- : typeof titleMode === "string" ? titleMode : previous.title;
103
- const titlePosition = args.titlePosition ?? previous.titlePosition;
104
- const enteringInline = kind === "opacity" &&
105
- titlePosition === "left" && previous.titlePosition !== "left";
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
- });
112
- const normalized = normalizeContinuousLegend({
113
- target: previous.target,
114
- position: args.position ?? previous.position,
115
- align: args.align ?? previous.align,
116
- offset: args.offset ?? previous.offset,
117
- ...(gradient ? { count: args.count ?? previous.count } : {}),
118
- titlePosition,
119
- title,
120
- labels: enteringInline && args.labels?.offset === undefined
121
- ? { ...labels, offset: 8 }
122
- : labels,
123
- titleStyle: mergeObject(previous.titleStyle, args.titleStyle),
124
- border: mergeBorder(previous.border, args.border),
125
- ...(kind === "opacity" ? {
126
- itemGap: args.itemGap ?? (enteringInline ? 20 : previous.itemGap),
127
- symbol: args.symbol ?? previous.symbol
128
- } : {})
129
- }, gradient ? "gradient" : kind);
130
- const { count: _legacyCount, ...canonicalPrevious } = previous;
131
- const config = {
132
- ...(gradient ? previous : canonicalPrevious),
133
- ...normalized,
134
- ...(gradient ? {} : { sampling }),
135
- inferredTitle,
136
- titleVisible
137
- };
138
- if (gradient) {
139
- const gradient = mergeObject(previous.gradient, args.gradient);
140
- validatePositive(gradient.length, "Gradient length");
141
- validatePositive(gradient.thickness, "Gradient thickness");
142
- config.gradient = gradient;
143
- } else {
144
- config.symbol = normalizeOpacitySymbol(args.symbol ?? previous.symbol);
145
- }
146
- return { config, titleMode, title, titleVisible };
147
- }
148
-
149
- function editContinuous(program, kind, previous, args) {
150
- const { config, titleMode, title, titleVisible } = resolveContinuousEdit(program, kind, previous, args);
151
- const prefix = kind === "gradient" ? "colorGradient"
152
- : kind === "strokeGradient" ? "strokeGradient" : "opacityLegend";
153
- const semanticKind = kind === "gradient" ? "color"
154
- : kind === "strokeGradient" ? "stroke" : "opacity";
155
- let next = program;
156
- if (titleMode === "auto" || typeof titleMode === "string") {
157
- next = next.editSemantic({
158
- property: `guide.legend.${semanticKind}.title`,
159
- value: title
160
- });
161
- }
162
- next = next._withLegendConfig(kind, config);
163
- next = reconcileGraphic(next, `${prefix}Background`, config.border !== false, {
164
- type: "rect",
165
- before: ["gradient", "strokeGradient"].includes(kind)
166
- ? `${prefix}Strips`
167
- : "opacityLegendSymbols"
168
- });
169
- next = reconcileGraphic(next, `${prefix}Title`, titleVisible, { type: "text" });
170
- return next.rematerializeLegend();
171
- }
172
-
173
- function resolveIntervalEdit(program, previous, args, channel = "color") {
174
- for (const key of Object.keys(args)) {
175
- if (![
176
- "target", "position", "align", "direction", "offset", "title",
177
- "symbol", "labels", "titleStyle", "itemGap", "border", "layout", "columns", "titlePosition"
178
- ].includes(key)) {
179
- throw new Error(`interval legend does not accept ${key}.`);
180
- }
181
- }
182
- const titleMode = args.title;
183
- const layer = findLayer(program, previous.target);
184
- const inferredTitle = titleMode === "auto"
185
- ? true
186
- : typeof titleMode === "string" ? false : previous.inferredTitle;
187
- const titleVisible = titleMode === false
188
- ? false
189
- : titleMode === undefined ? previous.titleVisible !== false : true;
190
- const title = titleMode === "auto"
191
- ? layer?.encoding?.[channel]?.field
192
- : typeof titleMode === "string" ? titleMode : previous.title;
193
- const normalized = normalizeIntervalLegend({
194
- target: previous.target,
195
- position: args.position ?? previous.position,
196
- align: args.align ?? previous.align,
197
- direction: args.direction ?? (args.position !== undefined && args.position !== previous.position
198
- ? undefined : previous.direction),
199
- layout: args.layout === undefined ? previous.layout : args.layout,
200
- columns: args.columns ?? previous.columns,
201
- titlePosition: args.titlePosition ?? previous.titlePosition,
202
- offset: args.offset ?? previous.offset,
203
- title,
204
- symbol: mergeObject(previous.symbol, args.symbol),
205
- labels: mergeObject(previous.labels, args.labels),
206
- titleStyle: mergeObject(previous.titleStyle, args.titleStyle),
207
- itemGap: args.itemGap ?? previous.itemGap,
208
- border: mergeBorder(previous.border, args.border)
209
- });
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
- );
227
- return { normalized, config, titleMode, title, titleVisible };
228
- }
229
-
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);
235
- if (titleMode === "auto" || typeof titleMode === "string") {
236
- next = next.editSemantic({
237
- property: `guide.legend.${channel}.title`,
238
- value: title
239
- });
240
- }
241
- next = reconcileGraphic(next, `${prefix}Title`, titleVisible, {
242
- type: "text"
243
- });
244
- next = reconcileGraphic(
245
- next,
246
- `${prefix}Background`,
247
- normalized.border !== false,
248
- { type: "rect", before: `${prefix}Symbols` }
249
- );
250
- return next.rematerializeLegend();
251
- }
252
-
253
- function resolveSampledLegendEdit(program, kind, previous, args) {
254
- const size = kind === "size";
255
- const label = size ? "size" : "stroke-width";
256
- const allowed = ["target", "title", "count", "values", "labels", "titleStyle",
257
- "position", "layout", "align", "direction", "columns", "titlePosition", "offset", "itemGap", "border"];
258
- for (const key of Object.keys(args)) {
259
- if (!allowed.includes(key)) {
260
- throw new Error(`${label} legend does not accept ${key}.`);
261
- }
262
- }
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.");
267
- }
268
- const sampling = discrete ? undefined : normalizeLegendSampling(args, {
269
- previous,
270
- operation: "edit",
271
- label: `${size ? "Size" : "Stroke-width"} legend`
272
- });
273
- const layer = findLayer(program, previous.target);
274
- const titleMode = args.title;
275
- const inferredTitle = titleMode === "auto"
276
- ? true
277
- : typeof titleMode === "string" ? false : previous.inferredTitle;
278
- const titleVisible = titleMode === false
279
- ? false
280
- : titleMode === undefined ? previous.titleVisible !== false : true;
281
- const title = titleMode === "auto"
282
- ? layer?.encoding?.[kind]?.field
283
- : typeof titleMode === "string" ? titleMode : previous.title;
284
- const { count: _legacyCount, ...continuousPrevious } = previous;
285
- const canonicalPrevious = discrete ? previous : continuousPrevious;
286
- const config = {
287
- ...canonicalPrevious,
288
- title,
289
- inferredTitle,
290
- titleVisible,
291
- ...(discrete ? {} : { sampling }),
292
- ...(size ? { inheritAppearance: false } : {}),
293
- labels: normalizeLegendTextOptions(
294
- args.labels,
295
- "editLegend.labels",
296
- previous.labels ?? (size ? SIZE_LEGEND_LABELS : STROKE_WIDTH_LEGEND_LABELS)
297
- ),
298
- titleStyle: normalizeLegendTitleOptions(
299
- args.titleStyle,
300
- "editLegend.titleStyle",
301
- previous.titleStyle ?? (size ? SIZE_LEGEND_TITLE_STYLE : STROKE_WIDTH_LEGEND_TITLE_STYLE)
302
- )
303
- };
304
- Object.assign(config, normalizeItemLegendLayout({ ...previous, ...args,
305
- direction: args.direction ?? (args.position !== undefined && args.position !== previous.position
306
- ? undefined : previous.direction) }));
307
- config.border = normalizeLegendBorder(mergeBorder(previous.border, args.border));
308
- (size ? resolveSizeLegendLayout : resolveStrokeWidthLegendLayout)(program, config);
309
- return { config, titleMode, title, titleVisible };
310
- }
311
-
312
- function editSampledLegend(program, kind, previous, args) {
313
- const { config, titleMode, title, titleVisible } = resolveSampledLegendEdit(program, kind, previous, args);
314
- const prefix = kind === "size" ? "sizeLegend" : "strokeWidthLegend";
315
- let next = program;
316
- if (titleMode === "auto" || typeof titleMode === "string") {
317
- next = next.editSemantic({
318
- property: `guide.legend.${kind}.title`,
319
- value: title
320
- });
321
- }
322
- next = next._withLegendConfig(kind, config);
323
- next = reconcileGraphic(next, `${prefix}Background`, config.border !== false, {
324
- type: "rect", before: `${prefix}Symbols`
325
- });
326
- next = reconcileGraphic(next, `${prefix}Title`, titleVisible, {
327
- type: "text", after: `${prefix}Labels`
328
- });
329
- return next.rematerializeLegend();
330
- }
331
-
332
- function resolveCompanionSizeEdit(program, previous, size, args) {
333
- if (size === undefined) return undefined;
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
- };
351
- if (args.labels === undefined && args.titleStyle === undefined) return config;
352
- const labels = size.inheritAppearance
353
- ? { ...previous.labels, offset: size.labels.offset } : size.labels ?? SIZE_LEGEND_LABELS;
354
- const titleStyle = size.inheritAppearance
355
- ? previous.titleStyle : size.titleStyle ?? SIZE_LEGEND_TITLE_STYLE;
356
- return { ...config, inheritAppearance: false,
357
- labels: normalizeLegendTextOptions(args.labels, "editLegend.labels", labels),
358
- titleStyle: normalizeLegendTitleOptions(args.titleStyle, "editLegend.titleStyle", titleStyle) };
359
- }
360
-
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
- }
367
- if (args.gradient !== undefined) {
368
- throw new Error("Categorical legends do not accept gradient.");
369
- }
370
- if (args.count !== undefined && size === undefined) {
371
- throw new Error("Legend count requires an existing size legend.");
372
- }
373
- const titleMode = args.title;
374
- const inferredTitle = titleMode === "auto"
375
- ? true
376
- : typeof titleMode === "string" ? false : previous.inferredTitle;
377
- const titleVisible = titleMode === false ? false
378
- : titleMode === undefined ? previous.titleVisible !== false : true;
379
- const title = titleMode === "auto"
380
- ? previous.field
381
- : typeof titleMode === "string" ? titleMode : previous.title;
382
- const mergedLabels = mergeObject(previous.labels, args.labels);
383
- if (mergedLabels.format !== undefined && mergedLabels.format !== "auto" && size === undefined) {
384
- throw new Error("Categorical legend labels do not accept format.");
385
- }
386
- const { format: _sizeFormat, ...categoricalLabels } = mergedLabels;
387
- const inferredSymbol = args.symbol === undefined
388
- ? previous.inferredSymbol
389
- : args.symbol === "auto";
390
- const normalized = normalizeOptions({
391
- target: previous.target,
392
- channels: previous.channels,
393
- layout: args.layout === undefined ? previous.layout : args.layout,
394
- position: args.position ?? previous.position,
395
- align: args.align ?? previous.align,
396
- direction: args.direction ?? (args.position !== undefined && args.position !== previous.position
397
- ? undefined : previous.direction),
398
- ...(args.columns === undefined && previous.columns === undefined
399
- ? {} : { columns: args.columns ?? previous.columns }),
400
- offset: args.offset ?? previous.offset,
401
- titlePosition: args.titlePosition ?? previous.titlePosition,
402
- title,
403
- symbol: args.symbol === undefined ? previous.symbol
404
- : resolveLegendSymbol(
405
- program,
406
- findLayer(program, previous.target),
407
- previous.channels,
408
- args.symbol,
409
- kind
410
- ),
411
- labels: categoricalLabels,
412
- titleStyle: mergeObject(previous.titleStyle, args.titleStyle),
413
- itemGap: args.itemGap ?? previous.itemGap,
414
- border: mergeBorder(previous.border, args.border)
415
- }, kind, { internalSymbol: inferredSymbol });
416
- const config = {
417
- ...previous,
418
- ...normalized,
419
- inferredTitle,
420
- inferredSymbol,
421
- titleVisible
422
- };
423
- const order = args.order === undefined
424
- ? storedOrder
425
- : normalizeLegendOrder(args.order);
426
- // Resolve the final domain before changing semantic/config/graphic state.
427
- resolveDefinition(program, findLayer(program, previous.target), previous.channels, title, order);
428
- return { config, order, titleMode, title, titleVisible, sizeConfig: resolveCompanionSizeEdit(program, previous, size, args) };
429
- }
430
-
431
- function editCategorical(program, kind, previous, size, args) {
432
- const { config, order, titleMode, title, titleVisible, sizeConfig } = resolveCategoricalEdit(program, kind, previous, size, args);
433
- const newSymbols = categoricalSymbolIds(config);
434
- let next = program;
435
- if (titleMode === "auto" || typeof titleMode === "string") {
436
- next = next.editSemantic({
437
- property: `guide.legend.${kind}.title`,
438
- value: title
439
- });
440
- }
441
- if (args.order !== undefined) {
442
- if (order === "scale") {
443
- if (program.semanticSpec.guides.legend?.[kind]?.order !== undefined) {
444
- next = next.editSemantic({ property: `guide.legend.${kind}.order`, remove: true });
445
- }
446
- } else {
447
- next = next.editSemantic({ property: `guide.legend.${kind}.order`, value: order });
448
- }
449
- }
450
- next = next._withLegendConfig(kind, config);
451
- next = reconcileCategoricalSymbols(next, previous, config);
452
- next = reconcileGraphic(next, `${kind}LegendBackground`,
453
- config.border !== false, {
454
- type: "rect",
455
- before: [...newSymbols][0]
456
- });
457
- const titleId = `${kind}LegendTitle`;
458
- next = reconcileGraphic(next, titleId, titleVisible, {
459
- type: "text",
460
- ...(next.graphicSpec.objects.sizeLegendSymbols === undefined
461
- ? {} : { before: next.graphicSpec.objects.sizeLegendBackground === undefined
462
- ? "sizeLegendSymbols" : "sizeLegendBackground" })
463
- });
464
- if (size !== undefined) {
465
- next = next._withLegendConfig("size", sizeConfig);
466
- }
467
- return next.rematerializeLegend();
468
- }
469
-
470
-
471
- function editLegendContent(program, target, args) {
472
- validateLegendChannels(args.channels, "editLegend");
473
- const { channels, target: _target, ...patch } = args;
474
- const previous = program.guideConfigs.legend ?? {};
475
- const removed = Object.keys(previous).filter(kind => previous[kind].target === target);
476
- let view = removed.reduce((next, kind) => next._withoutMaterializationConfig(["guides", "legend", kind]), program);
477
- const creation = resolveLegendCreationPlan(view, { target, channels });
478
- const oldCategorical = removed.find(kind =>
479
- ["series", "color", "stroke"].includes(kind)
480
- );
481
- let plans = creation.steps.map(step => {
482
- const descriptor = resolveLegendStepConfig(view, step);
483
- const { kind } = descriptor;
484
- if (previous[kind] !== undefined && previous[kind].target !== target) {
485
- throw new Error(`Legend ${kind} content already belongs to another target.`);
486
- }
487
- if (["series", "color", "stroke"].includes(kind) && oldCategorical !== undefined) {
488
- const order = patch.order === undefined ? program.semanticSpec.guides.legend?.[oldCategorical]?.order
489
- : normalizeLegendOrder(patch.order);
490
- return { ...resolveCategoricalLegendRevision(view, oldCategorical, previous[oldCategorical],
491
- descriptor.config.channels, { order, validateLayout: false }), kind };
492
- }
493
- return { ...descriptor, config: previous[kind]?.target === target ? previous[kind] : descriptor.config };
494
- });
495
- const categorical = plans.find(plan =>
496
- ["series", "color", "stroke"].includes(plan.kind)
497
- );
498
- const size = plans.find(plan => plan.kind === "size");
499
- if (categorical !== undefined) {
500
- if (size !== undefined && previous.size?.target !== target) {
501
- size.config = { ...size.config, inheritAppearance: categorical.config.position === "left" ||
502
- patch.position === "left" || patch.labels !== undefined || patch.titleStyle !== undefined };
503
- }
504
- const edited = resolveCategoricalEdit(view, categorical.kind, categorical.config, size?.config, patch, categorical.order);
505
- categorical.config = edited.config;
506
- categorical.order = edited.order;
507
- if (size !== undefined) {
508
- size.config = edited.sizeConfig;
509
- if (categorical.config.layout === "legacy-bottom") {
510
- throw new Error('Combined size legends require layout "edge".');
511
- }
512
- }
513
- } else {
514
- const plan = plans[0];
515
- if (plan.kind === "size" || plan.kind === "strokeWidth") {
516
- plan.config = resolveSampledLegendEdit(view, plan.kind, 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
- }
525
- else plan.config = resolveContinuousEdit(view, plan.kind, plan.config, patch).config;
526
- }
527
- plans = planLegendBlockTransitions(program, target, plans);
528
- // The complete final content and styles are validated before removing resources.
529
- for (const { kind, config } of plans) view = view._withLegendConfig(kind, config);
530
- if (categorical !== undefined) resolveLayout(view, categorical.config);
531
- let next = removeLegendKinds(program, removed);
532
- for (const { kind, config, order } of plans) {
533
- if (["series", "color", "stroke"].includes(kind)) {
534
- next = createCategoricalLegendFromConfig(next, config, order);
535
- }
536
- else next = ({ size: createSizeLegendFromConfig, strokeWidth: createStrokeWidthLegendFromConfig,
537
- gradient: createGradientLegendFromConfig, opacity: createOpacityLegendFromConfig,
538
- interval: createIntervalLegendFromConfig,
539
- strokeGradient: createStrokeGradientLegendFromConfig,
540
- strokeInterval: createStrokeIntervalLegendFromConfig })[kind](next, config);
541
- }
542
- return next.rematerializeLegend();
543
- }
544
-
545
- export const editLegend = /* @__PURE__ */ action(
546
- { op: "editLegend", description: "Edit one stable legend content, layout or appearance." },
547
- withGuideLayoutValidation(function (args = {}) {
548
- validateOptionObject(args, OPTIONS, "editLegend");
549
- const changes = Object.keys(args).filter(key => key !== "target");
550
- if (changes.length === 0) {
551
- throw new Error("editLegend requires at least one change.");
552
- }
553
- if (args.title !== undefined && args.title !== false && args.title !== "auto" && (
554
- typeof args.title !== "string" || args.title.length === 0
555
- )) {
556
- throw new TypeError('editLegend title must be a non-empty string, "auto", or false.');
557
- }
558
- const target = resolveLegendTarget(this, args.target, "editLegend");
559
- if (args.channels !== undefined) return editLegendContent(this, target, args);
560
- const configs = this.guideConfigs.legend ?? {};
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(
570
- kind => configs[kind]?.target === target
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];
578
- if (categoricalKind !== undefined) {
579
- return editCategorical(
580
- this,
581
- categoricalKind,
582
- configs[categoricalKind],
583
- configs.size?.target === target ? configs.size : undefined,
584
- args
585
- );
586
- }
587
- if (configs.interval?.target === target) {
588
- return editInterval(this, configs.interval, args);
589
- }
590
- if (configs.strokeInterval?.target === target) {
591
- return editInterval(this, configs.strokeInterval, args, "stroke");
592
- }
593
- if (configs.strokeWidth?.target === target) {
594
- return editSampledLegend(this, "strokeWidth", configs.strokeWidth, args);
595
- }
596
- if (configs.size?.target === target) {
597
- return editSampledLegend(this, "size", configs.size, args);
598
- }
599
- const continuousKind = ["gradient", "strokeGradient", "opacity"].find(
600
- kind => configs[kind]?.target === target
601
- );
602
- return editContinuous(this, continuousKind, configs[continuousKind], args);
603
- })
604
- );
1
+ import{resolveColorSizeEdit as R,createColorSizeLegendFromConfig as me}from"./colorSize.js";import{patchLegendClipHeight as Y,validateLegendClip as be}from"../../../grammar/legendClip.js";import{validateLegendCombine as ye,validateMappedSizeShapeSymbol as pe}from"../../../grammar/legendCombinations.js";import{legendBlockDescriptors as j}from"./target.js";import{editFacetParentLegend as he}from"../../../materialization/facetGuides/edit.js";import{localLegendPosition as Z,localLegendPlacement as Le}from"../../../materialization/legendGroups.js";import{mergeLegendTitleStyle as $}from"../../../grammar/legendTitleStyle.js";import{recordLegendOffsetIntent as J}from"./group.js";import{prepareLegendGroup as Se}from"./group.js";import{PLOT_LEGEND_POSITIONS as Q}from"../../../layout/legend.js";import{withGuideLayoutValidation as we}from"../../../materialization/guides/layout.js";import{resolveLegendStepConfig as ke}from"./creation.js";import{normalizeLegendOrder as U}from"../../../grammar/categoryOrder.js";import{resolveDefinition as ze}from"./categorical/resolve.js";import{action as Ce}from"../../../core/action.js";import{validateOptionObject as B}from"../../../core/validation.js";import{normalizeLegendOverflow as X,normalizeOptions as Oe}from"./categorical/options.js";import{resolveLegendSymbol as Ee}from"./categorical/recipes.js";import{resolveLayout as Ge}from"./categorical/layout.js";import{resolveLegendCreationPlan as ee}from"./categorical/actions.js";import{categoricalSymbolIds as _e,reconcileCategoricalSymbols as Pe,createCategoricalLegendFromConfig as je,resolveCategoricalLegendRevision as Te,removeLegendKinds as te}from"./lifecycle.js";import{normalizeColorSymbols as Ie}from"./continuous/colorSymbols.js";import{readLegendSampling as $e}from"./sampling.js";import{createGradientLegendFromConfig as ne}from"./continuous/gradient.js";import{normalizeLegendGradient as ie}from"../../../grammar/legendGradient.js";import{normalizeItemLegendLayout as xe,normalizeLegendBorder as Ve,normalizeLegendTextOptions as oe,normalizeLegendTitleOptions as le,normalizeContinuousLegend as re}from"./continuous/common.js";import{normalizeOpacitySymbol as De,createOpacityLegendFromConfig as Me}from"./continuous/opacity.js";import{normalizeIntervalLegend as qe,resolveIntervalConfig as Be,resolveIntervalLayout as Ae,createIntervalLegendFromConfig as We}from"./continuous/interval.js";import{createStrokeGradientLegendFromConfig as Fe,createStrokeIntervalLegendFromConfig as Ke,resolveStrokeIntervalConfig as He,resolveStrokeIntervalLayout as Ne}from"./continuous/stroke.js";import{findLayer as P}from"../../../selectors/layers.js";import{resolveLegendGraphicPlacement as ve}from"../../../materialization/graphicHierarchy.js";import{resolveLegendBlock as A,resolveLegendTarget as W,validateLegendChannels as Re}from"./target.js";import{isEnumeratedSizeScaleType as de}from"../../../grammar/scales/index.js";import{SIZE_LEGEND_LABELS as ce,SIZE_LEGEND_TITLE_STYLE as se,resolveSizeLegendLayout as Ye,createSizeLegendFromConfig as Ze}from"./size.js";import{STROKE_WIDTH_LEGEND_LABELS as Je,STROKE_WIDTH_LEGEND_TITLE_STYLE as Qe,resolveStrokeWidthLegendLayout as Ue,createStrokeWidthLegendFromConfig as Xe}from"./strokeWidth.js";import{normalizeLegendSampling as F}from"./sampling.js";import{patchLegendBlockOverride as et}from"./blocks.js";import{LEGEND_POSITIONS as tt}from"../../../core/vocabulary.js";import{planLegendBlockTransitions as nt}from"./transition.js";const fe=Object.freeze(["target","channel","channels","position","layout","align","direction","columns","offset","titlePosition","title","symbol","labels","titleStyle","itemGap","border","count","values","gradient","representation","order","overflow","blockLayout","at","combine","clipHeight"]);function T(n,t){return t===void 0?n:{...n,...t}}function I(n,t){return t===void 0?n:t===!1||t===!0||n===!1?t:{...n,...t}}function E(n,t,e,o){const i=n.graphicSpec.objects[t]!==void 0;return i&&!e?n.editGraphics({target:t,remove:!0}):!i&&e?n.createGraphics({id:t,...o,...ve(n,{...o.before===void 0?{}:{before:o.before},...o.after===void 0?{}:{after:o.after}})}):n}function it(n,t,e){const o=e.representation??t.representation??"gradient";if(!["gradient","symbols"].includes(o))throw new Error("Legend representation must be gradient or symbols.");const i=["target","representation","position","align","offset","title","labels","titleStyle","titlePosition","border","count",...o==="symbols"?["symbol","values","itemGap","layout","direction","columns"]:["gradient","direction"]];for(const a of Object.keys(e))if(!i.includes(a))throw new Error(`Color ${o} legend does not accept ${a}.`);const s=(t.representation??"gradient")!==o,u=e.title,r=u==="auto"?P(n,t.target)?.encoding?.color?.field:typeof u=="string"?u:t.title,f={target:t.target,position:e.position??t.position,align:e.align??t.align,offset:e.offset??t.offset,title:r,labels:T(t.labels,e.labels),titleStyle:$(t.titleStyle,e.titleStyle,{edit:!0}),border:I(t.border,e.border),titlePosition:e.titlePosition??(s&&(o==="gradient"||t.titlePosition==="bottom")?"top":t.titlePosition)};let m;if(o==="symbols"){e.symbol!==void 0&&e.symbol!=="auto"&&B(e.symbol,["type","radius","stroke","strokeWidth"],"Sampled color symbol");const a=s?{count:t.count}:{};for(const h of["values","count"])Object.hasOwn(e,h)&&(a[h]=e[h]);Object.hasOwn(e,"values")&&delete a.count,Object.hasOwn(e,"count")&&(a.count=e.count),m=Ie({...f,...a,layout:e.layout??(s?void 0:t.layout),direction:e.direction??(s||e.position!==void 0&&e.position!==t.position?void 0:t.direction),columns:e.columns??(s?void 0:t.columns),itemGap:e.itemGap??(s?void 0:t.itemGap),symbol:e.symbol==="auto"?"auto":e.symbol===void 0?s?void 0:t.symbol:{...s?{}:t.symbol,...e.symbol}},s?{count:t.count}:t,"edit")}else{m=re({...f,...e.direction==="auto"?{}:e.direction!==void 0?{direction:e.direction}:t.gradientDirection===void 0?{}:{direction:t.gradientDirection},count:e.count??(s?$e(t).count:t.count)},"gradient");const a=ie(e.gradient,s?void 0:t.gradient);m.gradient=a}const{representation:c,sampling:S,symbol:l,layout:L,direction:C,columns:z,gradient:k,count:w,...g}=t,p=s&&o==="gradient"&&t.blockOverrides!==void 0?Object.fromEntries(Object.entries(t.blockOverrides).map(([a,h])=>{const{symbol:d,labelMap:b,...y}=h;return[a,y]})):t.blockOverrides;return o==="gradient"&&e.direction==="auto"&&delete g.gradientDirection,{config:{...g,...m,...p===void 0?{}:{blockOverrides:p},inferredTitle:u==="auto"?!0:typeof u=="string"?!1:t.inferredTitle,titleVisible:u===!1?!1:u===void 0?t.titleVisible!==!1:!0},titleMode:u,title:r,titleVisible:u===!1?!1:u===void 0?t.titleVisible!==!1:!0}}function x(n,t,e,o){if(t==="gradient"&&(e.representation==="symbols"||o.representation!==void 0))return it(n,e,o);const i=["gradient","strokeGradient"].includes(t),s=i?["target","position","align","offset","title","labels","titleStyle","titlePosition","border","count","gradient","representation","direction"]:["target","position","align","offset","title","symbol","labels","titleStyle","titlePosition","itemGap","border","count","values"];for(const h of Object.keys(o))if(!s.includes(h))throw new Error(`${t} legend does not accept ${h}.`);const u=o.title,r=u==="auto"?!0:typeof u=="string"?!1:e.inferredTitle,f=u===!1?!1:u===void 0?e.titleVisible!==!1:!0,m=P(n,e.target),c=t==="gradient"?"color":t==="strokeGradient"?"stroke":"opacity",S=m?.encoding?.[c]?.field,l=u==="auto"?S:typeof u=="string"?u:e.title,L=o.titlePosition??e.titlePosition,C=t==="opacity"&&L==="left"&&e.titlePosition!=="left",z=T(e.labels,o.labels),k=i?void 0:F(o,{previous:e,operation:"edit",label:"Opacity legend"}),w=re({target:e.target,position:o.position??e.position,align:o.align??e.align,offset:o.offset??e.offset,...i?{count:o.count??e.count,...o.direction==="auto"?{}:o.direction!==void 0?{direction:o.direction}:e.gradientDirection===void 0?{}:{direction:e.gradientDirection}}:{},titlePosition:L,title:l,labels:C&&o.labels?.offset===void 0?{...z,offset:8}:z,titleStyle:$(e.titleStyle,o.titleStyle,{edit:!0}),border:I(e.border,o.border),...t==="opacity"?{itemGap:o.itemGap??(C?20:e.itemGap),symbol:o.symbol??e.symbol}:{}},i?"gradient":t),{count:g,...p}=e,a={...i?e:p,...w,...i?{}:{sampling:k},inferredTitle:r,titleVisible:f};if(i){o.direction==="auto"&&delete a.gradientDirection;const h=ie(o.gradient,e.gradient);a.gradient=h}else a.symbol=De(o.symbol??e.symbol);return{config:a,titleMode:u,title:l,titleVisible:f}}function V(n,t,e,o){const{config:i,titleMode:s,title:u,titleVisible:r}=x(n,t,e,o);if(t==="gradient"&&(e.representation??"gradient")!==(i.representation??"gradient"))return ne(te(n,[t]),i).rematerializeLegend();const f=t==="gradient"?"colorGradient":t==="strokeGradient"?"strokeGradient":"opacityLegend",m=t==="gradient"?"color":t==="strokeGradient"?"stroke":"opacity";let c=n;return(s==="auto"||typeof s=="string")&&(c=c.editSemantic({property:`guide.legend.${m}.title`,value:u})),c=c._withLegendConfig(t,i),c=E(c,`${f}Background`,i.border!==!1,{type:"rect",before:["gradient","strokeGradient"].includes(t)?`${f}${i.representation==="symbols"?"Symbols":"Strips"}`:"opacityLegendSymbols"}),c=E(c,`${f}Title`,r,{type:"text"}),c.rematerializeLegend()}function ae(n,t,e,o="color"){for(const l of Object.keys(e))if(!["target","position","align","direction","offset","title","symbol","labels","titleStyle","itemGap","border","layout","columns","titlePosition"].includes(l))throw new Error(`interval legend does not accept ${l}.`);const i=e.title,s=P(n,t.target),u=i==="auto"?!0:typeof i=="string"?!1:t.inferredTitle,r=i===!1?!1:i===void 0?t.titleVisible!==!1:!0,f=i==="auto"?s?.encoding?.[o]?.field:typeof i=="string"?i:t.title,m=qe({target:t.target,position:e.position??t.position,align:e.align??t.align,direction:e.direction??(e.position!==void 0&&e.position!==t.position?void 0:t.direction),layout:e.layout===void 0?t.layout:e.layout,columns:e.columns??t.columns,titlePosition:e.titlePosition??t.titlePosition,offset:e.offset??t.offset,title:f,symbol:T(t.symbol,e.symbol),labels:T(t.labels,e.labels),titleStyle:$(t.titleStyle,e.titleStyle,{edit:!0}),itemGap:e.itemGap??t.itemGap,border:I(t.border,e.border)}),c={...t,...m,inferredTitle:u,titleVisible:r,...o==="stroke"&&e.symbol?.strokeWidth!==void 0?{inferredStrokeWidth:!1}:{}},S=o==="stroke"?He(n,c):Be(n,c);return(o==="stroke"?Ne:Ae)(n,S.config,S.scale),{normalized:m,config:c,titleMode:i,title:f,titleVisible:r}}function D(n,t,e,o="color"){const i=o==="stroke"?"strokeInterval":"interval",s=o==="stroke"?"strokeInterval":"colorLegend",{normalized:u,config:r,titleMode:f,title:m,titleVisible:c}=ae(n,t,e,o);let S=n._withLegendConfig(i,r);return(f==="auto"||typeof f=="string")&&(S=S.editSemantic({property:`guide.legend.${o}.title`,value:m})),S=E(S,`${s}Title`,c,{type:"text"}),S=E(S,`${s}Background`,u.border!==!1,{type:"rect",before:`${s}Symbols`}),S.rematerializeLegend()}function K(n,t,e,o,i=!1){const s=t==="size",u=s?"size":"stroke-width",r=["target","title","count","values","labels","titleStyle","position","layout","align","direction","columns","titlePosition","offset","itemGap","border"];s&&r.push("overflow");for(const a of Object.keys(o))if(!r.includes(a))throw new Error(`${u} legend does not accept ${a}.`);const f=s&&de(n.resolvedScales[e.scale]?.type),m=s&&n.resolvedScales[e.scale]?.type==="ordinal";if(o.overflow!==void 0&&!m)throw new Error("Size legend overflow requires an ordinal scale.");if(f&&(o.count!==void 0||o.values!==void 0))throw new Error("Discrete size legends do not support count or exact values.");const c=f?void 0:F(o,{previous:e,operation:"edit",label:`${s?"Size":"Stroke-width"} legend`}),S=P(n,e.target),l=o.title,L=l==="auto"?!0:typeof l=="string"?!1:e.inferredTitle,C=l===!1?!1:l===void 0?e.titleVisible!==!1:!0,z=l==="auto"?S?.encoding?.[t]?.field:typeof l=="string"?l:e.title,{count:k,...w}=e,p={...f?e:w,title:z,inferredTitle:L,titleVisible:C,...f?{}:{sampling:c},...m&&(o.overflow!==void 0||e.overflow!==void 0)?{overflow:X(o.overflow===void 0?e.overflow:o.overflow)}:{},...s?{inheritAppearance:i&&e.inheritAppearance===!0,...o.position===void 0?{}:{inheritPosition:!1}}:{},labels:oe(o.labels,"editLegend.labels",e.labels??(s?ce:Je)),titleStyle:le(o.titleStyle,"editLegend.titleStyle",e.titleStyle??(s?se:Qe),!0)};return Object.assign(p,xe({...e,...o,titleStyle:p.titleStyle,direction:o.direction??(o.position!==void 0&&o.position!==e.position?void 0:e.direction)})),p.border=Ve(I(e.border,o.border)),(s?Ye:Ue)(n,p),{config:p,titleMode:l,title:z,titleVisible:C}}function M(n,t,e,o,i=!1){const{config:s,titleMode:u,title:r,titleVisible:f}=K(n,t,e,o,i),m=t==="size"?"sizeLegend":"strokeWidthLegend";let c=n;return(u==="auto"||typeof u=="string")&&(c=c.editSemantic({property:`guide.legend.${t}.title`,value:r})),c=c._withLegendConfig(t,s),c=E(c,`${m}Background`,s.border!==!1,{type:"rect",before:`${m}Symbols`}),c=E(c,`${m}Title`,f,{type:"text",after:`${m}Labels`}),c.rematerializeLegend()}function ot(n,t,e,o){if(e===void 0)return;const i=de(n.resolvedScales[e.scale]?.type);if(i&&(o.count!==void 0||o.values!==void 0))throw new Error("Discrete size legends do not support count or exact values.");const s=i?void 0:F(o,{previous:e,operation:"edit",label:"Size legend"}),{count:u,...r}=e,m={...i?e:r,...i?{}:{sampling:s},...o.overflow!==void 0&&n.resolvedScales[e.scale]?.type==="ordinal"?{overflow:X(o.overflow)}:{}};if(o.labels===void 0&&o.titleStyle===void 0)return m;const c=e.inheritAppearance?{...t.labels,offset:e.labels.offset}:e.labels??ce,S=e.inheritAppearance?t.titleStyle:e.titleStyle??se;return{...m,inheritAppearance:!1,labels:oe(o.labels,"editLegend.labels",c),titleStyle:le(o.titleStyle,"editLegend.titleStyle",S,!0)}}function ue(n,t,e,o,i,s=n.semanticSpec.guides.legend?.[t]?.order){if(i.values!==void 0)throw new Error("Combined and categorical legends require a channel block selector for exact values.");if(i.gradient!==void 0)throw new Error("Categorical legends do not accept gradient.");if(i.count!==void 0&&o===void 0)throw new Error("Legend count requires an existing size legend.");const u=i.title,r=u==="auto"?!0:typeof u=="string"?!1:e.inferredTitle,f=u===!1?!1:u===void 0?e.titleVisible!==!1:!0,m=u==="auto"?e.field:typeof u=="string"?u:e.title,c=T(e.labels,i.labels);if(c.format!==void 0&&c.format!=="auto"&&o===void 0)throw new Error("Categorical legend labels do not accept format.");const{format:S,...l}=c,L=i.symbol===void 0?e.inferredSymbol:i.symbol==="auto",C=Oe({target:e.target,channels:e.channels,...i.overflow===void 0&&e.overflow===void 0?{}:{overflow:i.overflow===void 0?e.overflow:i.overflow},layout:i.layout===void 0?e.layout:i.layout,position:i.position??e.position,align:i.align??e.align,direction:i.direction??(i.position!==void 0&&i.position!==e.position?void 0:e.direction),...i.columns===void 0&&e.columns===void 0?{}:{columns:i.columns??e.columns},offset:i.offset??e.offset,titlePosition:i.titlePosition??e.titlePosition,title:m,symbol:i.symbol===void 0?e.symbol:Ee(n,P(n,e.target),e.channels,i.symbol,t),labels:l,titleStyle:$(e.titleStyle,i.titleStyle,{edit:!0}),itemGap:i.itemGap??e.itemGap,border:I(e.border,i.border)},t,{internalSymbol:L}),z={...e,...C,inferredTitle:r,inferredSymbol:L,titleVisible:f},k=i.order===void 0?s:U(i.order);return ze(n,P(n,e.target),e.channels,m,k),{config:z,order:k,titleMode:u,title:m,titleVisible:f,sizeConfig:ot(n,e,o,i)}}function H(n,t,e,o,i){e.combine==="size-shape"&&pe(i.symbol);const{config:s,order:u,titleMode:r,title:f,titleVisible:m,sizeConfig:c}=ue(n,t,e,o,i),S=_e(s);let l=n;(r==="auto"||typeof r=="string")&&(l=l.editSemantic({property:`guide.legend.${t}.title`,value:f})),i.order!==void 0&&(u==="scale"?n.semanticSpec.guides.legend?.[t]?.order!==void 0&&(l=l.editSemantic({property:`guide.legend.${t}.order`,remove:!0})):l=l.editSemantic({property:`guide.legend.${t}.order`,value:u})),l=l._withLegendConfig(t,s),l=Pe(l,e,s),l=E(l,`${t}LegendBackground`,s.border!==!1,{type:"rect",before:[...S][0]});const L=`${t}LegendTitle`;return l=E(l,L,m,{type:"text",...l.graphicSpec.objects.sizeLegendSymbols===void 0?{}:{before:l.graphicSpec.objects.sizeLegendBackground===void 0?"sizeLegendSymbols":"sizeLegendBackground"}}),o!==void 0&&(l=l._withLegendConfig("size",c)),l.rematerializeLegend()}function N(n,t,e){const o=R(n,t,e);let i=n._withLegendConfig("colorSize",o);return i=E(i,"colorSizeLegendBackground",o.border!==!1,{type:"rect",before:"colorSizeLegendSymbols"}),i=E(i,"colorSizeLegendTitle",o.titleVisible!==!1,{type:"text",after:"colorSizeLegendLabels"}),i.rematerializeLegend()}function v(n,t,e,o={position:e.position}){Re(e.channels,"editLegend");const{channels:i,target:s,combine:u,...r}=e,f=n.guideConfigs.legend??{},m=Object.keys(f).filter(d=>f[d].target===t);let c=m.reduce((d,b)=>d._withoutMaterializationConfig(["guides","legend",b]),n);const S=u==="color-size"&&r.representation===void 0?void 0:r.representation??(i.includes("color")&&f.gradient?.target===t?f.gradient.representation:void 0);if(r.representation!==void 0&&!i.includes("color"))throw new Error("Legend representation requires selected continuous color.");const l=Object.values(f).find(d=>d.target===t&&["size-shape","color-size"].includes(d.combine)),L=l?.combine==="color-size"?["color","size"]:["shape","size"],C=L.every(d=>i.includes(d));if(l&&u===void 0&&!C&&i.some(d=>L.includes(d)))throw new Error("An edited combined legend keeps its combination; explicitly choose separate to split it.");const z=u??(l&&C?l.combine:void 0);let k;if(u==="separate"&&l){const d=i.filter(y=>L.includes(y));k={steps:[...l.combine==="color-size"?d.map(y=>[y]):[d],...i.filter(y=>!L.includes(y)).map(y=>[y])].filter(y=>y.length).map(y=>ee(c,{target:t,channels:y})).flatMap(y=>y.steps),...n.guideConfigs.legendGroups?.[t]===void 0?{}:{legendGroup:{target:t,config:n.guideConfigs.legendGroups[t]}}}}else k=ee(c,{target:t,channels:i,...z===void 0?{}:{combine:z},...S===void 0?{}:{representation:S}},n.semanticSpec.layers,i);k.legendGroup!==void 0&&(c=c._withMaterializationConfig(["guides","legendGroups",t],k.legendGroup.config));const w=m.find(d=>["series","color","stroke"].includes(d));let g=k.steps.map(d=>{const b=ke(c,d),{kind:y}=b;if(f[y]!==void 0&&f[y].target!==t)throw new Error(`Legend ${y} content already belongs to another target.`);if(b.config.combine==="size-shape"&&f.series?.combine!=="size-shape"||b.kind==="colorSize"&&f.colorSize?.target!==t)return b;if(l?.combine==="color-size"&&["gradient","size"].includes(y)){const G=Object.fromEntries(["title","inferredTitle","titleVisible","labels","titleStyle","position","align","offset","border"].map(_=>[_,l[_]]));return y==="size"&&Object.assign(G,Object.fromEntries(["sampling","direction","columns","titlePosition","itemGap","layout","clipHeight"].map(_=>[_,l[_]])),{inheritAppearance:!1}),{...b,config:{...b.config,...G}}}if(y==="size"&&l){const O=Object.fromEntries(["title","inferredTitle","titleVisible","labels","titleStyle","position","align","direction","columns","offset","titlePosition","itemGap","border","layout","clipHeight"].map(G=>[G,l[G]]));return{...b,config:{...b.config,...O,inheritAppearance:!1}}}if(["series","color","stroke"].includes(y)&&w!==void 0){const O=f[y]?.target===t?y:w,G=r.order===void 0?n.semanticSpec.guides.legend?.[O]?.order:U(r.order);return{...Te(c,O,f[O],b.config.channels,{order:G,validateLayout:!1}),kind:y}}return{...b,config:f[y]?.target===t?f[y]:b.config}});const p=g.find(d=>["series","color","stroke"].includes(d.kind)),a=g.find(d=>d.kind==="size");if(r.representation!==void 0||g.some(d=>d.kind==="gradient"&&d.config.representation==="symbols")){const d=g.find(b=>b.kind==="gradient");if(d===void 0)throw new Error("Legend representation requires continuous color.");d.config=x(c,"gradient",d.config,r).config}else if(p!==void 0){a!==void 0&&f.size?.target!==t&&(a.config={...a.config,inheritAppearance:p.config.position==="left"||r.position==="left"||r.labels!==void 0||r.titleStyle!==void 0});const d=ue(c,p.kind,p.config,a?.config,r,p.order);if(p.config=d.config,p.order=d.order,a!==void 0&&(a.config=d.sizeConfig,p.config.layout==="legacy-bottom"))throw new Error('Combined size legends require layout "edge".')}else{const d=g[0];if(d.kind==="colorSize")d.config=R(c,d.config,r);else if(l?.combine==="color-size"&&u==="separate")for(const b of g)b.config=b.kind==="size"?K(c,"size",b.config,r).config:x(c,b.kind,b.config,Object.fromEntries(Object.entries(r).filter(([y])=>!["values","itemGap","layout","columns"].includes(y)))).config;else d.kind==="size"||d.kind==="strokeWidth"?d.config=K(c,d.kind,d.config,r).config:["interval","strokeInterval"].includes(d.kind)?d.config=ae(c,d.config,r,d.kind==="strokeInterval"?"stroke":"color").config:d.config=x(c,d.kind,d.config,r).config}g=nt(n,t,g,o.position);for(const{kind:d,config:b}of g)c=c._withLegendConfig(d,b);p!==void 0&&Ge(c,p.config);let h=te(n,m);k.legendGroup!==void 0&&(h=h._withMaterializationConfig(["guides","legendGroups",t],k.legendGroup.config));for(const{kind:d,config:b,order:y}of g)["series","color","stroke"].includes(d)?h=je(h,b,y):h={colorSize:me,size:Ze,strokeWidth:Xe,gradient:ne,opacity:Me,interval:We,strokeGradient:Fe,strokeInterval:Ke}[d](h,b);return h.rematerializeLegend()}function q(n={},t=!1,e={position:n.position}){if(B(n,fe,"editLegend"),Object.keys(n).filter(l=>l!=="target"&&l!=="channel").length===0)throw new Error("editLegend requires at least one change.");if(n.title!==void 0&&n.title!==!1&&n.title!=="auto"&&(typeof n.title!="string"||n.title.length===0))throw new TypeError('editLegend title must be a non-empty string, "auto", or false.');const i=W(this,n.target,"editLegend");if(Object.hasOwn(n,"channel")){if(Object.hasOwn(n,"channels"))throw new Error("editLegend cannot combine channel and channels.");if(Object.hasOwn(n,"combine"))throw new Error("Selected legend edits cannot change combine.");if(n.blockLayout!==void 0||n.at!==void 0)throw new Error("Selected legend blocks do not accept whole-group blockLayout or at.");let l=this;const L=A(l,n,"editLegend"),C=Object.hasOwn(n,"position");if(C){if(n.position!=="inherit"&&!tt.includes(n.position))throw new Error("Selected legend position requires an anchor or inherit.");const O=et(L.config,L.key,n.position==="inherit"?{removePosition:!0}:{position:n.position}),{blockOverrides:G,..._}=L.config;l=l._withLegendConfig(L.kind,O===void 0?_:{..._,blockOverrides:O});const{position:lt,...ge}=n;n=ge}const z=l.guideConfigs.legend?.size,k=["series","color","stroke"].map(O=>l.guideConfigs.legend?.[O]).find(O=>O?.target===i);z?.target===i&&z.inheritAppearance===!0&&k&&(L.kind==="size"||["series","color","stroke"].includes(L.kind))&&(l=l._withLegendConfig("size",{...z,inheritAppearance:!1,labels:{...k.labels,offset:z.labels.offset},titleStyle:{...k.titleStyle}}));const{kind:w}=L,g=l.guideConfigs.legend[w],{channel:p,...a}=n,h=Le(g),d=h===void 0?g:{...g,...h},b=C&&!["gradient","strokeGradient","opacity"].includes(w)?{...d,direction:void 0}:d,y=O=>h===void 0?O:O._withLegendConfig(w,{...O.guideConfigs.legend[w],position:g.position,...a.align===void 0?{align:g.align}:{},...a.offset===void 0?{offset:g.offset,inferredOffset:g.inferredOffset}:{}}).rematerializeLegend();return["series","color","stroke"].includes(w)?y(H(l,w,b,void 0,a)):w==="colorSize"?y(N(l,b,a)):["size","strokeWidth"].includes(w)?y(M(l,w,b,a)):["interval","strokeInterval"].includes(w)?y(D(l,b,a,w==="strokeInterval"?"stroke":"color")):y(V(l,w,b,a))}const s=this.guideConfigs.legendGroups?.[i];if(!t&&(s!==void 0||n.blockLayout!==void 0||n.at!==void 0||Q.includes(n.position)&&Object.values(this.guideConfigs.legend??{}).some(l=>l.target===i&&l.kind===void 0))){const l=Se(this,n,i,{corner:Q.includes(n.position)}),{channels:L,...C}=l.options;if(L!==void 0)return v(l.program,i,l.options,e);const z=["position","layout","align","direction","columns","offset","titlePosition","itemGap"];if(Object.keys(C).every(g=>g==="target"||z.includes(g))){let g=l.program;const p=Object.entries(g.guideConfigs.legend??{}).filter(([,a])=>a.target===i);for(const[a]of p){const h=g.guideConfigs.legend[a],d={...C};if(Z(h)!==void 0)for(const b of["position","align","offset"])delete d[b];if(["gradient","strokeGradient","opacity"].includes(a)&&h.representation!=="symbols")for(const b of a==="opacity"?["layout","columns","direction"]:["layout","columns"]){if(Object.hasOwn(n,b))throw new Error(`${a} legend does not accept ${b}.`);delete d[b]}["gradient","strokeGradient"].includes(a)&&h.representation!=="symbols"&&delete d.itemGap,["series","color","stroke"].includes(a)?g=H(g,a,h,void 0,d):a==="colorSize"?g=N(g,h,d):["size","strokeWidth"].includes(a)?g=M(g,a,h,d,!0):["interval","strokeInterval"].includes(a)?g=D(g,h,d,a==="strokeInterval"?"stroke":"color"):g=V(g,a,h,d)}return g.rematerializeLegend()}if(n.combine===void 0&&Object.values(this.guideConfigs.legend??{}).some(g=>g.target===i&&["size-shape","color-size"].includes(g.combine))){if(n.values!==void 0&&j(l.program,i).length>1)throw new Error("A target with multiple legend blocks requires a channel block selector for exact values.");let g=l.program;const p={...n};for(const a of["position","align","offset","blockLayout","at"])delete p[a];for(const a of j(g,i))g=q.call(g,{...p,target:i,channel:a.channels[0]},!0);return g.rematerializeLegend()}let w=q.call(l.program,l.options,!0,e);for(const[g,p]of Object.entries(this.guideConfigs.legend??{}))p.target!==i||Z(p)===void 0||w.guideConfigs.legend[g]===void 0||(w=w._withLegendConfig(g,{...w.guideConfigs.legend[g],position:p.position,align:p.align,offset:p.offset,inferredOffset:p.inferredOffset,...n.direction===void 0?{direction:p.direction}:{}}));return w.rematerializeLegend()}if(Object.hasOwn(n,"combine")){if(ye(n.combine),n.combine==="size-shape"&&n.channels!==void 0&&!(n.channels.length===2&&n.channels.includes("size")&&n.channels.includes("shape")))throw new Error("size-shape combine requires exactly shape and size channels.");const l=n.channels??[...new Set(j(this,i).flatMap(L=>L.channels))];return v(this,i,{...n,channels:l},e)}if(n.channels!==void 0)return v(this,i,n,e);const r=this.guideConfigs.legend??{},f=Object.keys(r).filter(l=>r[l]?.target===i);if(n.representation!==void 0){if(r.gradient?.target!==i)throw new Error("Legend representation requires continuous color.");return V(this,"gradient",r.gradient,n)}if(r.gradient?.target===i&&r.gradient.representation==="symbols"&&f.length>1)throw new Error("Multiple legend blocks require editLegendBlock with an explicit channel for appearance or samples.");if(n.values!==void 0&&f.length>1)throw new Error("A target with multiple legend blocks requires a channel block selector for exact values.");const m=["series","color","stroke"].filter(l=>r[l]?.target===i);if(m.length>1)throw new Error("editLegend requires channels when a target owns multiple categorical legend blocks.");const c=m[0];if(c!==void 0)return H(this,c,r[c],r.size?.target===i?r.size:void 0,n);if(r.colorSize?.target===i)return N(this,r.colorSize,n);if(r.interval?.target===i)return D(this,r.interval,n);if(r.strokeInterval?.target===i)return D(this,r.strokeInterval,n,"stroke");if(r.strokeWidth?.target===i)return M(this,"strokeWidth",r.strokeWidth,n);if(r.size?.target===i)return M(this,"size",r.size,n);const S=["gradient","strokeGradient","opacity"].find(l=>r[l]?.target===i);return V(this,S,r[S],n)}const vt=Ce({op:"editLegend",description:"Edit one stable legend content, layout or appearance.",scope:"any"},we(function(n={}){if(this.compositionSpec!==void 0)return he(this,"editLegend",n);if(B(n,fe,"editLegend"),!Object.hasOwn(n,"clipHeight"))return J(this,q.call(this,n),n);const{clipHeight:t,...e}=n,o=Object.keys(e).some(f=>!["target","channel"].includes(f));let i=this;if(t==="auto"){const f=W(i,n.target,"editLegend"),m=n.channel===void 0?j(i,f):[A(i,n,"editLegend")];for(const c of m)i=i._withLegendConfig(c.kind,Y(c.config,"auto"))}let s=o?q.call(i,e):i;const u=W(s,n.target,"editLegend"),r=n.channel===void 0?j(s,u):[A(s,n,"editLegend")];if(!r.length)throw new Error("Legend clipHeight requires an existing legend block.");for(const f of r){const m=Y(s.guideConfigs.legend[f.kind],t);be(f.kind,m),s=s._withLegendConfig(f.kind,m)}return J(this,s.rematerializeLegend(),n)}));export{vt as editLegend};