ggaction 0.0.15 → 0.0.17

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 (270) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/README.md +23 -2
  3. package/knowledge/action-cards.json +1 -1
  4. package/knowledge/intent-taxonomy.json +1 -1
  5. package/knowledge/mcp-resources.json +1 -1
  6. package/knowledge/task-packet.schema.json +1 -1
  7. package/knowledge/task-resolver.js +1 -1
  8. package/package.json +40 -7
  9. package/src/BasicChartProgram.js +3 -1
  10. package/src/ChartProgram.js +2 -0
  11. package/src/accessibility.js +157 -0
  12. package/src/actions/boxPlots/components.js +6 -8
  13. package/src/actions/boxPlots/create.js +4 -5
  14. package/src/actions/boxPlots/edit.js +3 -4
  15. package/src/actions/boxPlots/materialize.js +1 -1
  16. package/src/actions/canvas/actions.js +3 -3
  17. package/src/actions/canvas/fitting.js +1 -1
  18. package/src/actions/categoryOrder/index.js +6 -8
  19. package/src/actions/charts/area.js +1 -1
  20. package/src/actions/charts/bar.js +1 -1
  21. package/src/actions/charts/beeswarm.js +1 -1
  22. package/src/actions/charts/density.js +1 -1
  23. package/src/actions/charts/ecdf.js +2 -2
  24. package/src/actions/charts/endpoints.js +4 -4
  25. package/src/actions/charts/heatmap.js +1 -1
  26. package/src/actions/charts/histogram.js +1 -1
  27. package/src/actions/charts/horizon.js +1 -1
  28. package/src/actions/charts/interval-regression.js +2 -2
  29. package/src/actions/charts/line.js +1 -1
  30. package/src/actions/charts/parallel.js +1 -1
  31. package/src/actions/charts/pie.js +1 -1
  32. package/src/actions/charts/polar.js +2 -2
  33. package/src/actions/charts/radar.js +1 -1
  34. package/src/actions/charts/radial.js +2 -2
  35. package/src/actions/charts/raincloud.js +2 -2
  36. package/src/actions/charts/rug-strip.js +2 -2
  37. package/src/actions/charts/scatter.js +1 -1
  38. package/src/actions/composition/actions.js +20 -24
  39. package/src/actions/coordinates/actions.js +1 -1
  40. package/src/actions/coordinates/edit.js +1 -1
  41. package/src/actions/data/basicBin2d.js +4 -5
  42. package/src/actions/data/bin.js +3 -3
  43. package/src/actions/data/bin2d.js +6 -8
  44. package/src/actions/data/bin2dMaterialize.js +4 -5
  45. package/src/actions/data/box.js +4 -4
  46. package/src/actions/data/complete.js +2 -2
  47. package/src/actions/data/computed.js +2 -2
  48. package/src/actions/data/create.js +23 -17
  49. package/src/actions/data/density.js +4 -4
  50. package/src/actions/data/derived.js +25 -15
  51. package/src/actions/data/ecdf.js +2 -2
  52. package/src/actions/data/edit.js +54 -29
  53. package/src/actions/data/filter.js +29 -24
  54. package/src/actions/data/fold.js +2 -2
  55. package/src/actions/data/gradientProfile.js +2 -2
  56. package/src/actions/data/horizon.js +2 -2
  57. package/src/actions/data/impute.js +2 -2
  58. package/src/actions/data/index.js +58 -54
  59. package/src/actions/data/interval.js +4 -2
  60. package/src/actions/data/intervalEdit.js +26 -18
  61. package/src/actions/data/normalize.js +2 -2
  62. package/src/actions/data/regression.js +9 -7
  63. package/src/actions/data/revise.js +80 -0
  64. package/src/actions/data/revisionBindings.js +13 -33
  65. package/src/actions/data/shared.js +30 -2
  66. package/src/actions/data/sort.js +21 -0
  67. package/src/actions/data/stack.js +2 -2
  68. package/src/actions/data/summary.js +3 -3
  69. package/src/actions/data/timeUnit.js +2 -2
  70. package/src/actions/data/window.js +2 -2
  71. package/src/actions/encodings/angle.js +5 -2
  72. package/src/actions/encodings/appearance.js +10 -6
  73. package/src/actions/encodings/barWidth.js +1 -1
  74. package/src/actions/encodings/channels.js +2 -2
  75. package/src/actions/encodings/color/continuous.js +4 -1
  76. package/src/actions/encodings/color/index.js +5 -2
  77. package/src/actions/encodings/density.js +2 -2
  78. package/src/actions/encodings/histogram.js +1 -1
  79. package/src/actions/encodings/horizon.js +6 -8
  80. package/src/actions/encodings/offset.js +6 -3
  81. package/src/actions/encodings/parallel.js +4 -5
  82. package/src/actions/encodings/pathOrder.js +6 -11
  83. package/src/actions/encodings/position/index.js +4 -4
  84. package/src/actions/encodings/position/resolve.js +6 -1
  85. package/src/actions/encodings/ranged.js +11 -6
  86. package/src/actions/encodings/remove.js +4 -5
  87. package/src/actions/encodings/ruleAppearance.js +5 -2
  88. package/src/actions/encodings/seriesLayout.js +1 -1
  89. package/src/actions/encodings/stroke.js +5 -2
  90. package/src/actions/encodings/strokeDash.js +8 -3
  91. package/src/actions/encodings/text.js +7 -2
  92. package/src/actions/errorBands/create.js +11 -17
  93. package/src/actions/errorBands/edit.js +13 -131
  94. package/src/actions/errorBars/create.js +8 -13
  95. package/src/actions/errorBars/edit.js +10 -121
  96. package/src/actions/facets/actions.js +30 -16
  97. package/src/actions/facets/derive.js +8 -2
  98. package/src/actions/facets/guides.js +4 -5
  99. package/src/actions/facets/replay.js +4 -5
  100. package/src/actions/gradientPlots/components.js +5 -6
  101. package/src/actions/gradientPlots/create.js +4 -5
  102. package/src/actions/gradientPlots/edit.js +4 -5
  103. package/src/actions/gradientPlots/materialize.js +2 -2
  104. package/src/actions/gradientPlots/rebind.js +4 -5
  105. package/src/actions/guides/axes/axes.js +23 -4
  106. package/src/actions/guides/axes/axis.js +2 -2
  107. package/src/actions/guides/axes/edit.js +2 -2
  108. package/src/actions/guides/axes/labels.js +9 -9
  109. package/src/actions/guides/axes/lines.js +9 -7
  110. package/src/actions/guides/axes/parallel/lifecycle.js +13 -13
  111. package/src/actions/guides/axes/parallel.js +5 -5
  112. package/src/actions/guides/axes/remove.js +4 -4
  113. package/src/actions/guides/axes/tickGroups.js +4 -4
  114. package/src/actions/guides/axes/ticks.js +13 -4
  115. package/src/actions/guides/axes/titles.js +10 -10
  116. package/src/actions/guides/grids/grid.js +18 -29
  117. package/src/actions/guides/guides.js +1 -1
  118. package/src/actions/guides/legends/blocks.js +1 -1
  119. package/src/actions/guides/legends/categorical/actions.js +4 -4
  120. package/src/actions/guides/legends/categorical/components.js +38 -33
  121. package/src/actions/guides/legends/categorical/layout.js +1 -1
  122. package/src/actions/guides/legends/categorical/symbols.js +27 -30
  123. package/src/actions/guides/legends/continuous/common.js +46 -8
  124. package/src/actions/guides/legends/continuous/gradient.js +89 -155
  125. package/src/actions/guides/legends/continuous/interval.js +13 -13
  126. package/src/actions/guides/legends/continuous/opacity.js +30 -79
  127. package/src/actions/guides/legends/continuous/stroke.js +22 -322
  128. package/src/actions/guides/legends/edit.js +1 -1
  129. package/src/actions/guides/legends/focused.js +5 -5
  130. package/src/actions/guides/legends/lane.js +6 -6
  131. package/src/actions/guides/legends/remove.js +4 -5
  132. package/src/actions/guides/legends/size.js +21 -82
  133. package/src/actions/guides/legends/strokeWidth.js +17 -77
  134. package/src/actions/guides/polar/axes/facade.js +4 -4
  135. package/src/actions/guides/polar/axes/labels.js +5 -5
  136. package/src/actions/guides/polar/axes/lines.js +9 -15
  137. package/src/actions/guides/polar/axes/ticks.js +9 -15
  138. package/src/actions/guides/polar/axes/titles.js +5 -5
  139. package/src/actions/guides/polar/grids.js +11 -13
  140. package/src/actions/index.js +2 -0
  141. package/src/actions/intervals/revision.js +126 -0
  142. package/src/actions/marks/arc/actions.js +3 -3
  143. package/src/actions/marks/area/actions.js +4 -4
  144. package/src/actions/marks/bar/create.js +9 -2
  145. package/src/actions/marks/bar/edit.js +13 -6
  146. package/src/actions/marks/bar/materialize.js +12 -38
  147. package/src/actions/marks/line/actions.js +3 -3
  148. package/src/actions/marks/point/create.js +9 -2
  149. package/src/actions/marks/point/edit.js +13 -6
  150. package/src/actions/marks/point/jitter.js +6 -8
  151. package/src/actions/marks/point/materialize.js +20 -26
  152. package/src/actions/marks/point/packing.js +6 -8
  153. package/src/actions/marks/rect/actions.js +26 -11
  154. package/src/actions/marks/references.js +8 -10
  155. package/src/actions/marks/remove.js +5 -49
  156. package/src/actions/marks/rule/actions.js +28 -12
  157. package/src/actions/marks/text/actions.js +30 -11
  158. package/src/actions/marks/text/layout.js +12 -20
  159. package/src/actions/marks/text/removal.js +1 -1
  160. package/src/actions/marks/text/selection.js +4 -5
  161. package/src/actions/marks/tick/actions.js +26 -11
  162. package/src/actions/primitives/createGraphics.js +5 -4
  163. package/src/actions/primitives/editGraphics.js +5 -4
  164. package/src/actions/primitives/graphicProperties.js +6 -0
  165. package/src/actions/primitives/semantic.js +10 -3
  166. package/src/actions/primitives/semanticAction.js +44 -7
  167. package/src/actions/primitives/semanticValidation/dataset.js +14 -4
  168. package/src/actions/primitives/semanticValidation/layer.js +22 -2
  169. package/src/actions/primitives/semanticValidation/scale.js +6 -0
  170. package/src/actions/regression/components.js +10 -14
  171. package/src/actions/regression/create.js +28 -8
  172. package/src/actions/regression/edit.js +50 -16
  173. package/src/actions/regression/follow.js +22 -0
  174. package/src/actions/resources/remove.js +12 -11
  175. package/src/actions/scales/channels.js +11 -11
  176. package/src/actions/scales/consumers/common.js +2 -1
  177. package/src/actions/scales/create.js +3 -2
  178. package/src/actions/scales/definitions.js +1 -1
  179. package/src/actions/scales/edit.js +2 -2
  180. package/src/actions/scales/materialize.js +1 -1
  181. package/src/actions/scales/offset.js +2 -2
  182. package/src/actions/scales/parallel.js +2 -2
  183. package/src/actions/scales/patch.js +1 -1
  184. package/src/actions/scales/preview.js +28 -1
  185. package/src/actions/scales/quantitativeColor.js +4 -5
  186. package/src/actions/selection/actions.js +25 -36
  187. package/src/actions/textMetrics/index.js +56 -0
  188. package/src/actions/theme/actions.js +2 -2
  189. package/src/actions/theme/reconcile.js +67 -121
  190. package/src/actions/titles/actions.js +8 -8
  191. package/src/actions/titles/resolve.js +3 -3
  192. package/src/actions/violinPlots/create.js +1 -1
  193. package/src/actions/violinPlots/edit.js +1 -1
  194. package/src/core/action.js +109 -36
  195. package/src/core/diagnostics.js +16 -0
  196. package/src/core/extensionRegistry.js +27 -0
  197. package/src/core/font.js +18 -0
  198. package/src/core/identifiers.js +5 -4
  199. package/src/core/immutable.js +10 -3
  200. package/src/core/optionRequirements.js +8 -0
  201. package/src/core/programState.js +6 -2
  202. package/src/core/resourceReferences.js +15 -6
  203. package/src/core/textMetricProfile.js +28 -0
  204. package/src/core/textMetrics.js +24 -3
  205. package/src/core/validation.js +22 -10
  206. package/src/diagnostics.js +1 -0
  207. package/src/grammar/bin.js +39 -10
  208. package/src/grammar/datasetSchema.js +386 -0
  209. package/src/grammar/density.js +53 -5
  210. package/src/grammar/filter.js +133 -39
  211. package/src/grammar/impute.js +3 -2
  212. package/src/grammar/interval.js +32 -1
  213. package/src/grammar/itemMissing.js +59 -0
  214. package/src/grammar/regression/derive.js +57 -16
  215. package/src/grammar/regression/index.js +1 -0
  216. package/src/grammar/regression/models.js +51 -22
  217. package/src/grammar/regression/parameters.js +56 -4
  218. package/src/grammar/scales/definition.js +7 -0
  219. package/src/grammar/scales/types.js +8 -0
  220. package/src/grammar/schemas/graphicBounds.js +11 -11
  221. package/src/grammar/schemas/semanticPath.js +3 -2
  222. package/src/grammar/sort.js +88 -0
  223. package/src/grammar/summary.js +117 -19
  224. package/src/grammar/transformTopology.js +1 -0
  225. package/src/grammar/transforms.js +21 -11
  226. package/src/inspection.js +495 -0
  227. package/src/inspectionDescriptors.js +3 -0
  228. package/src/layout/labels.js +11 -10
  229. package/src/layout/legend.js +4 -4
  230. package/src/layout/legendItems.js +6 -6
  231. package/src/layout/text.js +10 -9
  232. package/src/layout/title.js +9 -9
  233. package/src/materialization/bars/resolve.js +4 -2
  234. package/src/materialization/facetGuides/placement.js +1 -1
  235. package/src/materialization/facetGuides/preparation.js +1 -1
  236. package/src/materialization/facetHeaders.js +11 -10
  237. package/src/materialization/guides/layout.js +3 -3
  238. package/src/materialization/revisionIdentity.js +19 -0
  239. package/src/materialization/scaleGuideDependencies.js +10 -0
  240. package/src/materialization/scales/resolve.js +67 -4
  241. package/src/materialization/selection/items/common.js +1 -1
  242. package/src/materialization/text.js +6 -4
  243. package/src/materialization/typography.js +31 -0
  244. package/src/mcp/cli.js +5 -3
  245. package/src/persistence/codec.js +65 -0
  246. package/src/persistence/validation.js +185 -0
  247. package/src/persistence.js +147 -0
  248. package/src/renderers/canvas/index.js +5 -4
  249. package/src/renderers/nativeBackend.js +10 -0
  250. package/src/renderers/options.js +13 -0
  251. package/src/renderers/pdf.js +25 -54
  252. package/src/renderers/png.js +22 -17
  253. package/src/renderers/svg.js +10 -14
  254. package/src/renderers/text.js +1 -11
  255. package/src/selectors/coordinates.js +3 -1
  256. package/src/selectors/datasets.js +8 -6
  257. package/src/selectors/layers.js +6 -4
  258. package/src/selectors/markOwners.js +46 -0
  259. package/src/selectors/scales.js +6 -4
  260. package/src/theme/defaults.js +2 -1
  261. package/src/version.js +2 -0
  262. package/types/accessibility.d.ts +37 -0
  263. package/types/basic.d.ts +4 -2
  264. package/types/diagnostics.d.ts +15 -0
  265. package/types/index.d.ts +5 -0
  266. package/types/inspection.d.ts +17 -0
  267. package/types/pdf.d.ts +9 -0
  268. package/types/persistence.d.ts +7 -0
  269. package/types/png.d.ts +9 -0
  270. package/types/program.d.ts +181 -28
@@ -1,40 +1,20 @@
1
- import { action } from "../../../../core/action.js";
2
- import { isPlainObject } from "../../../../core/immutable.js";
3
- import { validateKeys, validateOptionObject } from
4
- "../../../../core/validation.js";
5
- import { formatDiscretizedIntervals } from
6
- "../../../../grammar/scales/index.js";
7
- import { inverseLerp, interpolateNumber } from
8
- "../../../../grammar/numeric.js";
9
- import { resolveLegendItemLayout } from "../../../../layout/legendItems.js";
1
+ import { action, closedAction } from "../../../../core/action.js";
2
+
10
3
  import { resolveLegendGraphicPlacement } from
11
4
  "../../../../materialization/graphicHierarchy.js";
12
- import { mapScaleConsumerValues } from
13
- "../../../../materialization/scales/map.js";
14
5
  import { findLayer } from "../../../../selectors/layers.js";
15
6
  import { DEFAULT_COLORS } from "../../../../theme/defaults.js";
16
7
  import {
17
- assertLegendBoundsInsideCanvas,
18
8
  editGraphicProperties,
19
9
  editLegendBackground,
20
- formatContinuousValues,
21
- normalizeContinuousLegend,
22
- requireResolvedLegendScale,
23
- resolveContinuousBounds,
24
10
  resolveContinuousColorLayer,
25
- resolveLegendBackgroundFromBounds,
26
- resolveLegendTextBounds,
27
- sampleContinuousValues,
28
11
  styleContinuousText,
29
12
  validateNonNegative,
30
- validatePositive
31
13
  } from "./common.js";
32
- import { DEFAULT_GRADIENT_SIZE } from "./gradient.js";
33
- import { normalizeIntervalLegend } from "./interval.js";
14
+ import { materializeGradientLegend, resolveGradientConfig, resolveGradientLegendCreation, createGradientLegendFromConfig } from "./gradient.js";
15
+ import { normalizeIntervalLegend, resolveIntervalLayout, resolveIntervalConfig } from "./interval.js";
34
16
  import { resolveEffectiveLegendBlockConfig } from "../blocks.js";
35
17
 
36
- const GRADIENT_OPTIONS = Object.freeze(["length", "thickness"]);
37
-
38
18
  function requireChannels(args, operation) {
39
19
  if (
40
20
  !Array.isArray(args.channels) ||
@@ -65,278 +45,34 @@ function sampleFill(program, target) {
65
45
  return properties?.fill ?? DEFAULT_COLORS.mark;
66
46
  }
67
47
 
68
- function resolveGradientConfig(program, stored) {
69
- const layer = resolveContinuousColorLayer(program, stored.target, "stroke");
70
- const encoding = layer.encoding.stroke;
71
- if (!["quantitative", "temporal"].includes(encoding.fieldType)) {
72
- throw new Error("Stroke gradient legend requires quantitative or temporal stroke.");
73
- }
74
- const scale = requireResolvedLegendScale(program, encoding.scale, "sequential");
75
- return {
76
- encoding,
77
- scale,
78
- config: {
79
- ...stored,
80
- target: layer.id,
81
- scale: encoding.scale,
82
- fieldType: encoding.fieldType,
83
- title: stored.inferredTitle ? encoding.field : stored.title,
84
- domain: scale.domain
85
- }
86
- };
87
- }
88
-
89
- function resolveGradientLayout(program, config, scale) {
90
- const { plot, canvas } = resolveContinuousBounds(program);
91
- const vertical = ["right", "left"].includes(config.position);
92
- const { length, thickness } = config.gradient;
93
- const titleGap = config.blockGap ?? 12;
94
- let x;
95
- let y;
96
- if (config.position === "right") {
97
- x = plot.x + plot.width + config.offset;
98
- y = plot.y + 46 + titleGap - 12;
99
- } else if (config.position === "left") {
100
- x = plot.x - config.offset - thickness;
101
- y = plot.y + 46 + titleGap - 12;
102
- } else {
103
- x = config.align === "left" ? plot.x
104
- : config.align === "right" ? plot.x + plot.width - length
105
- : plot.x + (plot.width - length) / 2;
106
- y = config.position === "top"
107
- ? plot.y - config.offset - thickness - config.labels.offset -
108
- config.labels.fontSize
109
- : plot.y + plot.height + config.offset +
110
- (config.titleVisible === false ? 0 : config.titleStyle.fontSize + titleGap);
111
- }
112
- const title = vertical
113
- ? { x, y: plot.y + 20, align: "left" }
114
- : { x: x + length / 2, y: y - titleGap - config.titleStyle.fontSize / 2,
115
- align: "center" };
116
- const values = [...sampleContinuousValues(scale.domain, config.count)];
117
- if (scale.midpoint !== undefined && !values.includes(scale.midpoint)) {
118
- values.push(scale.midpoint);
119
- values.sort((a, b) => scale.domain[1] > scale.domain[0] ? a - b : b - a);
120
- }
121
- const texts = formatContinuousValues(
122
- values,
123
- scale.domain,
124
- config.fieldType,
125
- config.labels.format
126
- );
127
- const fractions = values.map((value, index) => scale.midpoint === undefined
128
- ? index / (values.length - 1)
129
- : inverseLerp(value, ...scale.domain));
130
- const labels = vertical
131
- ? fractions.map(fraction => ({
132
- x: config.position === "right"
133
- ? x + thickness + config.labels.offset
134
- : x - config.labels.offset,
135
- y: y + length * (1 - fraction),
136
- align: config.position === "right" ? "left" : "right"
137
- }))
138
- : fractions.map(fraction => ({
139
- x: x + length * fraction,
140
- y: y + thickness + config.labels.offset + config.labels.fontSize / 2,
141
- align: "center"
142
- }));
143
- const ticks = vertical
144
- ? labels.map(label => ({
145
- x1: config.position === "right" ? x + thickness : x,
146
- y1: label.y,
147
- x2: config.position === "right" ? x + thickness + 6 : x - 6,
148
- y2: label.y
149
- }))
150
- : labels.map(label => ({
151
- x1: label.x,
152
- y1: y + thickness,
153
- x2: label.x,
154
- y2: y + thickness + 6
155
- }));
156
- const occupied = [
157
- { left: x, right: x + (vertical ? thickness : length), top: y,
158
- bottom: y + (vertical ? length : thickness) },
159
- ...ticks.map(tick => ({
160
- left: Math.min(tick.x1, tick.x2) - 0.5,
161
- right: Math.max(tick.x1, tick.x2) + 0.5,
162
- top: Math.min(tick.y1, tick.y2) - 0.5,
163
- bottom: Math.max(tick.y1, tick.y2) + 0.5
164
- })),
165
- ...labels.map((label, index) =>
166
- resolveLegendTextBounds(label, texts[index], config.labels)),
167
- ...(config.titleVisible === false ? [] : [
168
- resolveLegendTextBounds(title, config.title, config.titleStyle)
169
- ])
170
- ];
171
- assertLegendBoundsInsideCanvas(occupied, canvas, "Stroke gradient legend layout", config);
172
- return {
173
- vertical,
174
- x,
175
- y,
176
- length,
177
- thickness,
178
- values,
179
- texts,
180
- labels,
181
- ticks,
182
- title,
183
- background: resolveLegendBackgroundFromBounds(
184
- occupied,
185
- config.border,
186
- canvas,
187
- "Stroke gradient legend",
188
- config
189
- )
190
- };
191
- }
192
-
193
48
  export function resolveStrokeGradientLegendCreation(program, args = {}) {
194
49
  requireChannels(args, "Stroke gradient legend");
195
- const config = normalizeContinuousLegend(args, "gradient");
196
- if (args.gradient !== undefined && !isPlainObject(args.gradient)) {
197
- throw new TypeError("createLegend.gradient must be a plain object.");
198
- }
199
- validateKeys(args.gradient ?? {}, GRADIENT_OPTIONS, "createLegend.gradient");
200
- config.gradient = {
201
- length: args.gradient?.length ?? DEFAULT_GRADIENT_SIZE.length,
202
- thickness: args.gradient?.thickness ?? DEFAULT_GRADIENT_SIZE.thickness
203
- };
204
- config.titleVisible = true;
205
- validatePositive(config.gradient.length, "Gradient length");
206
- validatePositive(config.gradient.thickness, "Gradient thickness");
207
- return resolveGradientConfig(program, config);
50
+ return resolveGradientLegendCreation(program, args, "stroke");
208
51
  }
209
52
 
210
- export const rematerializeStrokeGradientLegend = action(
53
+ export const rematerializeStrokeGradientLegend = /* @__PURE__ */ closedAction(
211
54
  {
212
55
  op: "rematerializeStrokeGradientLegend",
213
56
  description: "Rematerialize a continuous stroke gradient legend."
214
- },
57
+ }, [],
215
58
  function (args = {}) {
216
- validateKeys(args, [], "rematerializeStrokeGradientLegend");
217
59
  const stored = this.guideConfigs.legend?.strokeGradient;
218
60
  if (stored === undefined) {
219
61
  throw new Error("Stroke gradient legend requires stored configuration.");
220
62
  }
221
- const { encoding, scale, config: currentConfig } = resolveGradientConfig(this, stored);
222
- const config = resolveEffectiveLegendBlockConfig(this, "strokeGradient", currentConfig);
223
- const layout = resolveGradientLayout(this, config, scale);
224
- const fill = sampleFill(this, config.target);
225
- const width = strokeWidth(this, config.target);
226
- const stripCount = 60;
227
- const stripSize = layout.length / stripCount;
228
- const strips = Array.from({ length: stripCount }, (_, index) => {
229
- const fraction = (index + 0.5) / stripCount;
230
- const position = layout.vertical ? 1 - fraction : fraction;
231
- const samplingScale = scale.midpoint === undefined
232
- ? { ...scale, domain: [0, 1] }
233
- : scale;
234
- const value = scale.midpoint === undefined
235
- ? position
236
- : interpolateNumber(...scale.domain, position);
237
- const [color] = mapScaleConsumerValues([value], samplingScale, "stroke");
238
- return {
239
- x: layout.x + (layout.vertical ? 0 : index * stripSize),
240
- y: layout.y + (layout.vertical ? index * stripSize : 0),
241
- width: layout.vertical ? layout.thickness : stripSize,
242
- height: layout.vertical ? stripSize : layout.thickness,
243
- fill,
244
- stroke: color,
245
- strokeWidth: width
246
- };
247
- });
248
- let next = this
249
- .editSemantic({ property: "guide.legend.stroke.scale", value: encoding.scale })
250
- .editSemantic({ property: "guide.legend.stroke.title", value: config.title })
251
- ._withLegendConfig("strokeGradient", currentConfig);
252
- next = editLegendBackground(
253
- next,
254
- "strokeGradientBackground",
255
- layout.background,
256
- config.border
257
- );
258
- next = editGraphicProperties(next, "strokeGradientStrips", {
259
- length: strips.length,
260
- x: strips.map(strip => strip.x),
261
- y: strips.map(strip => strip.y),
262
- width: strips.map(strip => strip.width),
263
- height: strips.map(strip => strip.height),
264
- fill: strips.map(strip => strip.fill),
265
- stroke: strips.map(strip => strip.stroke),
266
- strokeWidth: strips.map(strip => strip.strokeWidth)
267
- });
268
- next = editGraphicProperties(next, "strokeGradientTicks", {
269
- length: layout.ticks.length,
270
- x1: layout.ticks.map(tick => tick.x1),
271
- y1: layout.ticks.map(tick => tick.y1),
272
- x2: layout.ticks.map(tick => tick.x2),
273
- y2: layout.ticks.map(tick => tick.y2),
274
- stroke: DEFAULT_COLORS.mutedText,
275
- strokeWidth: 1
276
- });
277
- next = editGraphicProperties(next, "strokeGradientLabels", {
278
- length: layout.labels.length,
279
- x: layout.labels.map(label => label.x),
280
- y: layout.labels.map(label => label.y),
281
- text: layout.texts
282
- });
283
- next = styleContinuousText(next, "strokeGradientLabels", config.labels, {
284
- align: layout.labels[0].align
285
- });
286
- if (config.titleVisible === false) return next;
287
- next = editGraphicProperties(next, "strokeGradientTitle", {
288
- x: layout.title.x,
289
- y: layout.title.y,
290
- text: config.title
291
- });
292
- return styleContinuousText(next, "strokeGradientTitle", config.titleStyle, {
293
- align: layout.title.align
63
+ const resolved = resolveGradientConfig(this, stored, "stroke");
64
+ return materializeGradientLegend(this, resolved, {
65
+ fill: sampleFill(this, resolved.config.target),
66
+ strokeWidth: strokeWidth(this, resolved.config.target)
294
67
  });
295
68
  }
296
69
  );
297
70
 
298
71
  export function createStrokeGradientLegendFromConfig(program, config) {
299
- const resolved = resolveGradientConfig(program, config);
300
- resolveGradientLayout(program, resolved.config, resolved.scale);
301
- let next = program
302
- .editSemantic({
303
- property: "guide.legend.stroke.scale",
304
- value: resolved.encoding.scale
305
- })
306
- .editSemantic({
307
- property: "guide.legend.stroke.title",
308
- value: resolved.config.title
309
- })
310
- ._withLegendConfig("strokeGradient", resolved.config);
311
- const placement = resolveLegendGraphicPlacement(next);
312
- if (resolved.config.border !== false) {
313
- next = next.createGraphics({
314
- id: "strokeGradientBackground",
315
- type: "rect",
316
- ...placement
317
- });
318
- }
319
- next = next
320
- .createGraphics({
321
- id: "strokeGradientStrips",
322
- type: "rect",
323
- length: 0,
324
- ...resolveLegendGraphicPlacement(next, resolved.config.border === false
325
- ? {}
326
- : { after: "strokeGradientBackground" })
327
- })
328
- .createGraphics({ id: "strokeGradientTicks", type: "line", length: 0,
329
- ...resolveLegendGraphicPlacement(next) })
330
- .createGraphics({ id: "strokeGradientLabels", type: "text", length: 0,
331
- ...resolveLegendGraphicPlacement(next) });
332
- if (resolved.config.titleVisible !== false) {
333
- next = next.createGraphics({ id: "strokeGradientTitle", type: "text",
334
- ...resolveLegendGraphicPlacement(next) });
335
- }
336
- return next.rematerializeStrokeGradientLegend();
72
+ return createGradientLegendFromConfig(program, config, "stroke");
337
73
  }
338
74
 
339
- export const createStrokeGradientLegend = action(
75
+ export const createStrokeGradientLegend = /* @__PURE__ */ action(
340
76
  {
341
77
  op: "createStrokeGradientLegend",
342
78
  description: "Create a continuous stroke gradient legend."
@@ -351,49 +87,15 @@ export const createStrokeGradientLegend = action(
351
87
  );
352
88
 
353
89
  export function resolveStrokeIntervalConfig(program, stored) {
354
- const layer = resolveContinuousColorLayer(program, stored.target, "stroke");
355
- const encoding = layer.encoding.stroke;
356
- if (encoding.fieldType !== "quantitative") {
357
- throw new Error("Stroke interval legend requires quantitative stroke.");
358
- }
359
- const scale = program.resolvedScales[encoding.scale];
360
- if (!["quantize", "quantile", "threshold"].includes(scale?.type)) {
361
- throw new Error(
362
- `Stroke interval legend requires a resolved discretized scale "${encoding.scale}".`
363
- );
364
- }
365
- return { encoding, scale, config: {
366
- ...stored,
367
- target: layer.id,
368
- scale: encoding.scale,
369
- title: stored.inferredTitle ? encoding.field : stored.title,
370
- ...(stored.inferredStrokeWidth === true ? {
371
- symbol: { ...stored.symbol, strokeWidth: strokeWidth(program, layer.id) }
372
- } : {})
90
+ const resolved = resolveIntervalConfig(program, stored, "stroke");
91
+ if (stored.inferredStrokeWidth !== true) return resolved;
92
+ return { ...resolved, config: { ...resolved.config,
93
+ symbol: { ...stored.symbol, strokeWidth: strokeWidth(program, resolved.config.target) }
373
94
  } };
374
95
  }
375
96
 
376
97
  export function resolveStrokeIntervalLayout(program, config, scale) {
377
- const { plot, canvas } = resolveContinuousBounds(program);
378
- const labels = formatDiscretizedIntervals(scale.thresholds, config.labels.format);
379
- const layout = resolveLegendItemLayout(plot, config, labels, config.symbol);
380
- assertLegendBoundsInsideCanvas(
381
- layout.bounds,
382
- canvas,
383
- "Stroke interval legend layout",
384
- config
385
- );
386
- return {
387
- labels,
388
- ...layout,
389
- background: resolveLegendBackgroundFromBounds(
390
- layout.bounds,
391
- config.border,
392
- canvas,
393
- "Stroke interval legend",
394
- config
395
- )
396
- };
98
+ return resolveIntervalLayout(program, config, scale, "Stroke interval legend");
397
99
  }
398
100
 
399
101
  export function resolveStrokeIntervalLegendCreation(program, args = {}) {
@@ -411,13 +113,12 @@ export function resolveStrokeIntervalLegendCreation(program, args = {}) {
411
113
  return resolveStrokeIntervalConfig(program, config);
412
114
  }
413
115
 
414
- export const rematerializeStrokeIntervalLegend = action(
116
+ export const rematerializeStrokeIntervalLegend = /* @__PURE__ */ closedAction(
415
117
  {
416
118
  op: "rematerializeStrokeIntervalLegend",
417
119
  description: "Rematerialize a discretized stroke interval legend."
418
- },
120
+ }, [],
419
121
  function (args = {}) {
420
- validateKeys(args, [], "rematerializeStrokeIntervalLegend");
421
122
  const stored = this.guideConfigs.legend?.strokeInterval;
422
123
  if (stored === undefined) {
423
124
  throw new Error("Stroke interval legend requires stored configuration.");
@@ -500,13 +201,12 @@ export function createStrokeIntervalLegendFromConfig(program, config) {
500
201
  return next.rematerializeStrokeIntervalLegend();
501
202
  }
502
203
 
503
- export const createStrokeIntervalLegend = action(
204
+ export const createStrokeIntervalLegend = /* @__PURE__ */ closedAction(
504
205
  {
505
206
  op: "createStrokeIntervalLegend",
506
207
  description: "Create a discretized stroke interval legend."
507
- },
208
+ }, undefined,
508
209
  function (args = {}) {
509
- validateOptionObject(args, undefined, "createStrokeIntervalLegend");
510
210
  const resolved = resolveStrokeIntervalLegendCreation(this, args);
511
211
  if (this.guideConfigs.legend?.strokeInterval !== undefined) {
512
212
  throw new Error("createStrokeIntervalLegend requires a missing stroke legend.");
@@ -542,7 +542,7 @@ function editLegendContent(program, target, args) {
542
542
  return next.rematerializeLegend();
543
543
  }
544
544
 
545
- export const editLegend = action(
545
+ export const editLegend = /* @__PURE__ */ action(
546
546
  { op: "editLegend", description: "Edit one stable legend content, layout or appearance." },
547
547
  withGuideLayoutValidation(function (args = {}) {
548
548
  validateOptionObject(args, OPTIONS, "editLegend");
@@ -17,7 +17,7 @@ function makeFocusedLegendEdit({ name, options, map }) {
17
17
  );
18
18
  }
19
19
 
20
- const editLegendLayout = makeFocusedLegendEdit({
20
+ const editLegendLayout = /* @__PURE__ */ makeFocusedLegendEdit({
21
21
  name: "editLegendLayout",
22
22
  options: [
23
23
  "position", "layout", "align", "direction", "columns", "offset",
@@ -26,13 +26,13 @@ const editLegendLayout = makeFocusedLegendEdit({
26
26
  map: args => args
27
27
  });
28
28
 
29
- const editLegendLabels = makeFocusedLegendEdit({
29
+ const editLegendLabels = /* @__PURE__ */ makeFocusedLegendEdit({
30
30
  name: "editLegendLabels",
31
31
  options: ["offset", "color", "fontSize", "fontFamily", "fontWeight", "format"],
32
32
  map: ({ target, ...labels }) => ({ target, labels })
33
33
  });
34
34
 
35
- const editLegendTitle = makeFocusedLegendEdit({
35
+ const editLegendTitle = /* @__PURE__ */ makeFocusedLegendEdit({
36
36
  name: "editLegendTitle",
37
37
  options: ["title", "color", "fontSize", "fontFamily", "fontWeight"],
38
38
  map: ({ target, title, ...titleStyle }) => ({
@@ -42,13 +42,13 @@ const editLegendTitle = makeFocusedLegendEdit({
42
42
  })
43
43
  });
44
44
 
45
- const editLegendSymbols = makeFocusedLegendEdit({
45
+ const editLegendSymbols = /* @__PURE__ */ makeFocusedLegendEdit({
46
46
  name: "editLegendSymbols",
47
47
  options: ["symbol", "count", "gradient"],
48
48
  map: args => args
49
49
  });
50
50
 
51
- const editLegendBorder = makeFocusedLegendEdit({
51
+ const editLegendBorder = /* @__PURE__ */ makeFocusedLegendEdit({
52
52
  name: "editLegendBorder",
53
53
  options: ["border"],
54
54
  map: args => args
@@ -67,7 +67,7 @@ function componentIds(program, kind) {
67
67
  }
68
68
 
69
69
  function legendGraphicBounds(program, id) {
70
- let bounds = resolveConcreteGraphicBounds(program.graphicSpec, id);
70
+ let bounds = resolveConcreteGraphicBounds(program.graphicSpec, id, program.materializationConfigs.textMetrics);
71
71
  const graphic = program.graphicSpec.objects[id];
72
72
  if (!id.includes("LegendSymbol") || graphic?.type !== "line") return bounds;
73
73
  const properties = graphic.items?.map(item => item.properties) ??
@@ -126,7 +126,7 @@ function blockDescriptor(program, kind, config) {
126
126
  const labels = resolveConcreteGraphicBounds(
127
127
  program.graphicSpec,
128
128
  components.labelId
129
- );
129
+ , program.materializationConfigs.textMetrics);
130
130
  const title = components.titleId === undefined
131
131
  ? undefined
132
132
  : (() => {
@@ -134,7 +134,7 @@ function blockDescriptor(program, kind, config) {
134
134
  const titleBounds = resolveConcreteGraphicBounds(
135
135
  program.graphicSpec,
136
136
  components.titleId
137
- );
137
+ , program.materializationConfigs.textMetrics);
138
138
  return {
139
139
  x: graphic.properties.x,
140
140
  y: graphic.properties.y,
@@ -374,7 +374,7 @@ function applyHorizontalPlan(program, groups, plan) {
374
374
  return next;
375
375
  }
376
376
 
377
- export const rematerializeSideLegendLane = action(
377
+ export const rematerializeSideLegendLane = /* @__PURE__ */ action(
378
378
  {
379
379
  op: "rematerializeSideLegendLane",
380
380
  description: "Align and stack every right or left legend block."
@@ -457,7 +457,7 @@ export const rematerializeSideLegendLane = action(
457
457
  }
458
458
  );
459
459
 
460
- export const rematerializeHorizontalLegendLane = action(
460
+ export const rematerializeHorizontalLegendLane = /* @__PURE__ */ action(
461
461
  {
462
462
  op: "rematerializeHorizontalLegendLane",
463
463
  description: "Pack and align every top or bottom legend block."
@@ -479,7 +479,7 @@ export const rematerializeHorizontalLegendLane = action(
479
479
  if (entries.length === 1) {
480
480
  const [kind, config] = entries[0];
481
481
  const ids = existingIds(next, kind);
482
- const bounds = unionConcreteGraphicBounds(next.graphicSpec, ids);
482
+ const bounds = unionConcreteGraphicBounds(next.graphicSpec, ids, next.materializationConfigs.textMetrics);
483
483
  const { dx, dy } = resolveSingleHorizontalLegendPlacement({
484
484
  plot, canvas, config, bounds
485
485
  });
@@ -1,5 +1,5 @@
1
- import { action } from "../../../core/action.js";
2
- import { validateKeys } from "../../../core/validation.js";
1
+ import { closedAction } from "../../../core/action.js";
2
+
3
3
  import {
4
4
  removeLegendKinds,
5
5
  resolveCategoricalLegendRevision,
@@ -67,10 +67,9 @@ export function removeOwnedColorLegends(program, target) {
67
67
  return kinds.length === 0 ? program : removeLegendKinds(program, kinds);
68
68
  }
69
69
 
70
- export const removeLegend = action(
71
- { op: "removeLegend", description: "Remove selected legend content or every block owned by one mark." },
70
+ export const removeLegend = /* @__PURE__ */ closedAction(
71
+ { op: "removeLegend", description: "Remove selected legend content or every block owned by one mark." }, OPTIONS,
72
72
  function (args = {}) {
73
- validateKeys(args, OPTIONS, "removeLegend");
74
73
  const target = resolveLegendTarget(this, args.target, "removeLegend");
75
74
  const targetKinds = Object.entries(this.guideConfigs.legend ?? {})
76
75
  .filter(([, config]) => config?.target === target)