ggaction 0.0.13 → 0.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (359) hide show
  1. package/CHANGELOG.md +146 -0
  2. package/README.md +41 -12
  3. package/knowledge/action-cards.json +1 -1
  4. package/knowledge/intent-taxonomy.json +1 -1
  5. package/knowledge/mcp-resources.json +1 -1
  6. package/knowledge/task-packet.schema.json +1 -1
  7. package/knowledge/task-resolver.js +1 -1
  8. package/package.json +39 -7
  9. package/src/BasicChartProgram.js +3 -1
  10. package/src/ChartProgram.js +2 -0
  11. package/src/accessibility.js +157 -0
  12. package/src/actions/basic.js +2 -2
  13. package/src/actions/boxPlots/components.js +20 -12
  14. package/src/actions/boxPlots/create.js +4 -5
  15. package/src/actions/boxPlots/edit.js +34 -9
  16. package/src/actions/boxPlots/materialize.js +12 -4
  17. package/src/actions/boxPlots/options.js +48 -16
  18. package/src/actions/canvas/actions.js +3 -3
  19. package/src/actions/canvas/fitting.js +1 -1
  20. package/src/actions/categoryOrder/index.js +21 -12
  21. package/src/actions/charts/area.js +6 -2
  22. package/src/actions/charts/bar.js +3 -2
  23. package/src/actions/charts/beeswarm.js +1 -1
  24. package/src/actions/charts/density.js +7 -3
  25. package/src/actions/charts/ecdf.js +6 -3
  26. package/src/actions/charts/endpoints.js +29 -7
  27. package/src/actions/charts/heatmap.js +3 -2
  28. package/src/actions/charts/histogram.js +5 -4
  29. package/src/actions/charts/horizon.js +4 -2
  30. package/src/actions/charts/interval-regression.js +7 -4
  31. package/src/actions/charts/line.js +4 -2
  32. package/src/actions/charts/parallel.js +9 -3
  33. package/src/actions/charts/pie.js +11 -3
  34. package/src/actions/charts/polar.js +17 -6
  35. package/src/actions/charts/radar.js +9 -3
  36. package/src/actions/charts/radial.js +12 -4
  37. package/src/actions/charts/raincloud.js +2 -2
  38. package/src/actions/charts/rug-strip.js +8 -4
  39. package/src/actions/charts/scatter.js +4 -2
  40. package/src/actions/composition/actions.js +29 -24
  41. package/src/actions/coordinates/actions.js +2 -2
  42. package/src/actions/coordinates/edit.js +76 -0
  43. package/src/actions/data/basicBin2d.js +4 -5
  44. package/src/actions/data/bin.js +3 -3
  45. package/src/actions/data/bin2d.js +63 -193
  46. package/src/actions/data/bin2dMaterialize.js +4 -5
  47. package/src/actions/data/box.js +4 -4
  48. package/src/actions/data/complete.js +26 -0
  49. package/src/actions/data/computed.js +4 -4
  50. package/src/actions/data/create.js +7 -14
  51. package/src/actions/data/density.js +15 -23
  52. package/src/actions/data/derived.js +57 -31
  53. package/src/actions/data/ecdf.js +2 -2
  54. package/src/actions/data/edit.js +551 -0
  55. package/src/actions/data/filter.js +28 -22
  56. package/src/actions/data/fold.js +2 -2
  57. package/src/actions/data/gradientProfile.js +2 -2
  58. package/src/actions/data/horizon.js +2 -2
  59. package/src/actions/data/impute.js +26 -0
  60. package/src/actions/data/index.js +62 -47
  61. package/src/actions/data/interval.js +2 -2
  62. package/src/actions/data/intervalEdit.js +26 -18
  63. package/src/actions/data/normalize.js +27 -0
  64. package/src/actions/data/regression.js +21 -27
  65. package/src/actions/data/revise.js +48 -0
  66. package/src/actions/data/revisionBindings.js +125 -0
  67. package/src/actions/data/shared.js +20 -4
  68. package/src/actions/data/stack.js +2 -2
  69. package/src/actions/data/summary.js +3 -3
  70. package/src/actions/data/timeUnit.js +6 -3
  71. package/src/actions/data/window.js +3 -3
  72. package/src/actions/encodings/angle.js +1 -1
  73. package/src/actions/encodings/appearance.js +4 -4
  74. package/src/actions/encodings/barWidth.js +1 -1
  75. package/src/actions/encodings/channels.js +415 -0
  76. package/src/actions/encodings/color/index.js +3 -2
  77. package/src/actions/encodings/density.js +7 -4
  78. package/src/actions/encodings/histogram.js +4 -3
  79. package/src/actions/encodings/horizon.js +6 -8
  80. package/src/actions/encodings/index.js +4 -0
  81. package/src/actions/encodings/offset.js +22 -15
  82. package/src/actions/encodings/parallel.js +4 -5
  83. package/src/actions/encodings/pathOrder.js +6 -11
  84. package/src/actions/encodings/position/apply.js +53 -7
  85. package/src/actions/encodings/position/index.js +14 -6
  86. package/src/actions/encodings/position/policies/bar.js +9 -0
  87. package/src/actions/encodings/position/resolve.js +24 -7
  88. package/src/actions/encodings/ranged.js +5 -5
  89. package/src/actions/encodings/remove.js +13 -9
  90. package/src/actions/encodings/ruleAppearance.js +2 -24
  91. package/src/actions/encodings/seriesLayout.js +2 -1
  92. package/src/actions/encodings/shared.js +82 -14
  93. package/src/actions/encodings/stroke.js +226 -0
  94. package/src/actions/encodings/strokeDash.js +2 -2
  95. package/src/actions/encodings/text.js +1 -1
  96. package/src/actions/errorBands/create.js +34 -20
  97. package/src/actions/errorBands/edit.js +34 -137
  98. package/src/actions/errorBars/create.js +43 -20
  99. package/src/actions/errorBars/edit.js +29 -125
  100. package/src/actions/facets/actions.js +267 -73
  101. package/src/actions/facets/derive.js +99 -28
  102. package/src/actions/facets/guides.js +4 -5
  103. package/src/actions/facets/replay.js +4 -5
  104. package/src/actions/gradientPlots/components.js +15 -8
  105. package/src/actions/gradientPlots/create.js +4 -5
  106. package/src/actions/gradientPlots/edit.js +18 -9
  107. package/src/actions/gradientPlots/materialize.js +5 -3
  108. package/src/actions/gradientPlots/options.js +10 -2
  109. package/src/actions/gradientPlots/rebind.js +4 -5
  110. package/src/actions/guides/applicability.js +8 -2
  111. package/src/actions/guides/axes/axes.js +23 -4
  112. package/src/actions/guides/axes/axis.js +2 -2
  113. package/src/actions/guides/axes/edit.js +2 -2
  114. package/src/actions/guides/axes/labels.js +67 -21
  115. package/src/actions/guides/axes/lines.js +15 -9
  116. package/src/actions/guides/axes/parallel/lifecycle.js +13 -13
  117. package/src/actions/guides/axes/parallel/resolve.js +6 -2
  118. package/src/actions/guides/axes/parallel.js +5 -5
  119. package/src/actions/guides/axes/remove.js +4 -4
  120. package/src/actions/guides/axes/tickGroups.js +6 -5
  121. package/src/actions/guides/axes/ticks.js +19 -6
  122. package/src/actions/guides/axes/titles.js +21 -13
  123. package/src/actions/guides/grids/grid.js +18 -29
  124. package/src/actions/guides/grids/resolve.js +7 -2
  125. package/src/actions/guides/guides.js +20 -4
  126. package/src/actions/guides/legends/blocks.js +426 -0
  127. package/src/actions/guides/legends/categorical/actions.js +138 -52
  128. package/src/actions/guides/legends/categorical/components.js +55 -45
  129. package/src/actions/guides/legends/categorical/index.js +37 -33
  130. package/src/actions/guides/legends/categorical/layout.js +98 -16
  131. package/src/actions/guides/legends/categorical/options.js +2 -2
  132. package/src/actions/guides/legends/categorical/recipes.js +111 -20
  133. package/src/actions/guides/legends/categorical/resolve.js +12 -8
  134. package/src/actions/guides/legends/categorical/symbols.js +104 -52
  135. package/src/actions/guides/legends/continuous/common.js +76 -20
  136. package/src/actions/guides/legends/continuous/gradient.js +95 -158
  137. package/src/actions/guides/legends/continuous/index.js +2 -0
  138. package/src/actions/guides/legends/continuous/interval.js +21 -16
  139. package/src/actions/guides/legends/continuous/opacity.js +37 -83
  140. package/src/actions/guides/legends/continuous/stroke.js +225 -0
  141. package/src/actions/guides/legends/creation.js +14 -1
  142. package/src/actions/guides/legends/edit.js +159 -60
  143. package/src/actions/guides/legends/focused.js +5 -5
  144. package/src/actions/guides/legends/index.js +2 -0
  145. package/src/actions/guides/legends/lane.js +59 -24
  146. package/src/actions/guides/legends/lifecycle.js +19 -8
  147. package/src/actions/guides/legends/remove.js +17 -8
  148. package/src/actions/guides/legends/sampling.js +140 -0
  149. package/src/actions/guides/legends/size.js +100 -108
  150. package/src/actions/guides/legends/strokeWidth.js +38 -92
  151. package/src/actions/guides/legends/target.js +112 -1
  152. package/src/actions/guides/legends/transition.js +165 -16
  153. package/src/actions/guides/polar/axes/facade.js +8 -8
  154. package/src/actions/guides/polar/axes/labels.js +29 -10
  155. package/src/actions/guides/polar/axes/lines.js +19 -19
  156. package/src/actions/guides/polar/axes/shared.js +16 -0
  157. package/src/actions/guides/polar/axes/ticks.js +10 -16
  158. package/src/actions/guides/polar/axes/titles.js +6 -6
  159. package/src/actions/guides/polar/grids.js +12 -14
  160. package/src/actions/guides/polar/resolve.js +12 -8
  161. package/src/actions/index.js +16 -3
  162. package/src/actions/intervals/revision.js +126 -0
  163. package/src/actions/marks/arc/actions.js +80 -22
  164. package/src/actions/marks/area/actions.js +38 -13
  165. package/src/actions/marks/area/materialize.js +17 -1
  166. package/src/actions/marks/bar/create.js +12 -3
  167. package/src/actions/marks/bar/edit.js +21 -4
  168. package/src/actions/marks/bar/materialize.js +34 -40
  169. package/src/actions/marks/index.js +10 -1
  170. package/src/actions/marks/line/actions.js +40 -17
  171. package/src/actions/marks/line/materialize.js +29 -10
  172. package/src/actions/marks/point/create.js +9 -3
  173. package/src/actions/marks/point/edit.js +19 -7
  174. package/src/actions/marks/point/jitter.js +6 -8
  175. package/src/actions/marks/point/materialize.js +48 -22
  176. package/src/actions/marks/point/packing.js +6 -8
  177. package/src/actions/marks/rect/actions.js +36 -15
  178. package/src/actions/marks/references.js +602 -33
  179. package/src/actions/marks/remove.js +67 -41
  180. package/src/actions/marks/rule/actions.js +51 -14
  181. package/src/actions/marks/shared.js +1 -1
  182. package/src/actions/marks/text/actions.js +79 -14
  183. package/src/actions/marks/text/index.js +4 -0
  184. package/src/actions/marks/text/layout.js +157 -17
  185. package/src/actions/marks/text/removal.js +173 -0
  186. package/src/actions/marks/text/selection.js +72 -0
  187. package/src/actions/marks/tick/actions.js +25 -8
  188. package/src/actions/primitives/createGraphics.js +5 -4
  189. package/src/actions/primitives/editGraphics.js +31 -4
  190. package/src/actions/primitives/graphicProperties.js +6 -0
  191. package/src/actions/primitives/semantic.js +11 -3
  192. package/src/actions/primitives/semanticAction.js +90 -14
  193. package/src/actions/primitives/semanticValidation/dataset.js +2 -4
  194. package/src/actions/primitives/semanticValidation/guide.js +1 -1
  195. package/src/actions/primitives/semanticValidation/index.js +9 -1
  196. package/src/actions/primitives/semanticValidation/layer.js +5 -0
  197. package/src/actions/primitives/semanticValidation/scale.js +28 -7
  198. package/src/actions/regression/components.js +36 -25
  199. package/src/actions/regression/create.js +13 -6
  200. package/src/actions/regression/edit.js +24 -11
  201. package/src/actions/resources/index.js +7 -0
  202. package/src/actions/resources/remove.js +173 -0
  203. package/src/actions/scales/channels.js +212 -0
  204. package/src/actions/scales/consumers/common.js +1 -1
  205. package/src/actions/scales/consumers/families.js +3 -2
  206. package/src/actions/scales/consumers/index.js +19 -2
  207. package/src/actions/scales/consumers/seriesLayout.js +28 -14
  208. package/src/actions/scales/create.js +55 -23
  209. package/src/actions/scales/definitions.js +94 -40
  210. package/src/actions/scales/edit.js +67 -12
  211. package/src/actions/scales/editPolicy.js +81 -11
  212. package/src/actions/scales/index.js +6 -0
  213. package/src/actions/scales/materialize.js +5 -4
  214. package/src/actions/scales/offset.js +48 -0
  215. package/src/actions/scales/parallel.js +71 -0
  216. package/src/actions/scales/preview.js +111 -10
  217. package/src/actions/scales/quantitativeColor.js +4 -5
  218. package/src/actions/selection/actions.js +179 -66
  219. package/src/actions/selection/index.js +3 -0
  220. package/src/actions/textMetrics/index.js +56 -0
  221. package/src/actions/theme/actions.js +70 -15
  222. package/src/actions/theme/composition.js +238 -0
  223. package/src/actions/theme/index.js +9 -2
  224. package/src/actions/theme/reconcile.js +313 -121
  225. package/src/actions/theme/state.js +160 -0
  226. package/src/actions/titles/actions.js +8 -8
  227. package/src/actions/titles/resolve.js +3 -3
  228. package/src/actions/violinPlots/create.js +6 -3
  229. package/src/actions/violinPlots/edit.js +2 -1
  230. package/src/core/action.js +117 -35
  231. package/src/core/compositionState.js +6 -1
  232. package/src/core/diagnostics.js +16 -0
  233. package/src/core/extensionRegistry.js +27 -0
  234. package/src/core/font.js +18 -0
  235. package/src/core/identifiers.js +5 -4
  236. package/src/core/immutable.js +10 -3
  237. package/src/core/optionRequirements.js +8 -0
  238. package/src/core/programState.js +21 -1
  239. package/src/core/resourceReferences.js +341 -0
  240. package/src/core/textMetricProfile.js +28 -0
  241. package/src/core/textMetrics.js +24 -3
  242. package/src/core/validation.js +22 -10
  243. package/src/core/vocabulary.js +16 -3
  244. package/src/diagnostics.js +1 -0
  245. package/src/grammar/bars/aggregate.js +4 -1
  246. package/src/grammar/bars/geometry.js +44 -0
  247. package/src/grammar/bin.js +54 -9
  248. package/src/grammar/complete.js +279 -0
  249. package/src/grammar/computed.js +265 -64
  250. package/src/grammar/coordinates.js +37 -0
  251. package/src/grammar/density.js +239 -37
  252. package/src/grammar/displayLabels.js +57 -0
  253. package/src/grammar/facets/dependencies.js +133 -1
  254. package/src/grammar/facets/guides.js +3 -0
  255. package/src/grammar/facets/index.js +7 -47
  256. package/src/grammar/facets/scales.js +15 -18
  257. package/src/grammar/impute.js +341 -0
  258. package/src/grammar/markLabelSelection.js +57 -0
  259. package/src/grammar/markLabels.js +18 -1
  260. package/src/grammar/normalize.js +366 -0
  261. package/src/grammar/pathSeries.js +26 -8
  262. package/src/grammar/pointShapes.js +13 -4
  263. package/src/grammar/polar.js +90 -8
  264. package/src/grammar/regression/index.js +1 -0
  265. package/src/grammar/regression/parameters.js +23 -0
  266. package/src/grammar/roundedRect.js +180 -0
  267. package/src/grammar/rules.js +1 -0
  268. package/src/grammar/scales/colorConsumers.js +20 -1
  269. package/src/grammar/scales/continuous.js +2 -2
  270. package/src/grammar/scales/definition.js +27 -4
  271. package/src/grammar/scales/index.js +1 -0
  272. package/src/grammar/scales/ordinal.js +12 -3
  273. package/src/grammar/scales/policies.js +2 -2
  274. package/src/grammar/scales/size.js +456 -0
  275. package/src/grammar/scales/types.js +8 -0
  276. package/src/grammar/schemas/concreteGraphic.js +14 -2
  277. package/src/grammar/schemas/graphic.js +15 -0
  278. package/src/grammar/schemas/graphicBounds.js +190 -41
  279. package/src/grammar/schemas/semanticPath.js +7 -2
  280. package/src/grammar/statisticalReference.js +103 -0
  281. package/src/grammar/strokeStyle.js +77 -0
  282. package/src/grammar/summary.js +78 -16
  283. package/src/grammar/timeUnit.js +216 -5
  284. package/src/grammar/transformTopology.js +4 -0
  285. package/src/grammar/transforms.js +394 -28
  286. package/src/grammar/weightedStatistics.js +374 -0
  287. package/src/grammar/window.js +221 -16
  288. package/src/layout/aspect.js +80 -0
  289. package/src/layout/facets.js +59 -5
  290. package/src/layout/labels.js +351 -7
  291. package/src/layout/legend.js +4 -4
  292. package/src/layout/legendItems.js +6 -6
  293. package/src/layout/text.js +10 -9
  294. package/src/layout/title.js +9 -9
  295. package/src/materialization/bars/aggregate.js +25 -2
  296. package/src/materialization/bars/histogram.js +84 -24
  297. package/src/materialization/bars/ranged.js +19 -3
  298. package/src/materialization/bars/resolve.js +7 -4
  299. package/src/materialization/composition.js +3 -1
  300. package/src/materialization/coordinateBounds.js +204 -0
  301. package/src/materialization/dependencies.js +36 -0
  302. package/src/materialization/facetGuides/legacyCategorical.js +3 -0
  303. package/src/materialization/facetGuides/placement.js +9 -3
  304. package/src/materialization/facetGuides/preparation.js +13 -8
  305. package/src/materialization/facetHeaders.js +347 -0
  306. package/src/materialization/facets.js +15 -65
  307. package/src/materialization/guides/layout.js +3 -3
  308. package/src/materialization/guides/resources.js +26 -0
  309. package/src/materialization/legends.js +1 -1
  310. package/src/materialization/marks/index.js +11 -5
  311. package/src/materialization/marks/policies.js +2 -1
  312. package/src/materialization/rect.js +25 -12
  313. package/src/materialization/rectConfig.js +5 -1
  314. package/src/materialization/scaleGuideDependencies.js +10 -0
  315. package/src/materialization/scales/map.js +5 -3
  316. package/src/materialization/scales/policies/binnedPosition.js +2 -2
  317. package/src/materialization/scales/policies/offset.js +50 -8
  318. package/src/materialization/scales/policies/series.js +4 -1
  319. package/src/materialization/scales/resolve.js +22 -6
  320. package/src/materialization/selection/items/arc.js +28 -4
  321. package/src/materialization/selection/items/bar.js +30 -3
  322. package/src/materialization/selection/items/common.js +20 -5
  323. package/src/materialization/selection/items/rect.js +28 -4
  324. package/src/materialization/selection/path.js +1 -1
  325. package/src/materialization/selection/point.js +3 -1
  326. package/src/materialization/text.js +265 -14
  327. package/src/materialization/typography.js +31 -0
  328. package/src/mcp/cli.js +5 -3
  329. package/src/persistence/codec.js +65 -0
  330. package/src/persistence/validation.js +185 -0
  331. package/src/persistence.js +74 -0
  332. package/src/renderers/canvas/circle.js +2 -3
  333. package/src/renderers/canvas/index.js +5 -4
  334. package/src/renderers/canvas/line.js +2 -3
  335. package/src/renderers/canvas/native.js +4 -4
  336. package/src/renderers/canvas/path.js +2 -3
  337. package/src/renderers/canvas/rect.js +2 -3
  338. package/src/renderers/canvas/stroke.js +11 -0
  339. package/src/renderers/nativeBackend.js +10 -0
  340. package/src/renderers/options.js +13 -0
  341. package/src/renderers/pdf.js +25 -54
  342. package/src/renderers/png.js +22 -17
  343. package/src/renderers/svg.js +29 -14
  344. package/src/renderers/text.js +1 -11
  345. package/src/selectors/coordinates.js +3 -1
  346. package/src/selectors/datasets.js +38 -5
  347. package/src/selectors/layers.js +10 -4
  348. package/src/selectors/markOwners.js +46 -0
  349. package/src/selectors/scales.js +6 -4
  350. package/src/theme/defaults.js +103 -1
  351. package/src/version.js +2 -0
  352. package/types/accessibility.d.ts +37 -0
  353. package/types/basic.d.ts +10 -2
  354. package/types/diagnostics.d.ts +15 -0
  355. package/types/index.d.ts +90 -0
  356. package/types/pdf.d.ts +9 -0
  357. package/types/persistence.d.ts +7 -0
  358. package/types/png.d.ts +9 -0
  359. package/types/program.d.ts +944 -118
@@ -8,6 +8,7 @@ import { planEncodingRematerialization } from "../../materialization/encodings.j
8
8
  import { findSemanticScale } from "../../selectors/scales.js";
9
9
  import { validateOptionObject } from "../../core/validation.js";
10
10
  import { getMarkGraphicTypes, getPositionChannelDefinition } from "../../core/vocabulary.js";
11
+ import { DEFAULT_TICK_COUNT } from "../guides/tickValues.js";
11
12
 
12
13
  export function validateOptions(args, supported, operation) {
13
14
  validateOptionObject(args, supported, operation);
@@ -105,33 +106,100 @@ export function rebindPositionGuides(
105
106
  const direction = definition.gridDirection;
106
107
  const grid = program.semanticSpec.guides.grid?.[direction];
107
108
  const ownsAxis = axis?.scale === previousScale;
109
+ const refreshesReboundAxis = axis?.scale === nextScale;
108
110
  const ownsGrid = grid?.scale === previousScale;
109
- if (!ownsAxis && !ownsGrid) return program;
111
+ if (!ownsAxis && !ownsGrid && !refreshesReboundAxis) return program;
110
112
 
111
113
  const remaining = program.semanticSpec.layers.some(layer =>
112
114
  layer.id !== target && !isSourceOwnedText(layer) && layer.encoding?.[channel]?.scale === previousScale
113
115
  );
114
- if (remaining) {
116
+ if ((ownsAxis || ownsGrid) && remaining) {
115
117
  throw new Error(
116
118
  `Cannot rebind ${channel} guides from shared scale "${previousScale}" while it has other consumers.`
117
119
  );
118
120
  }
119
121
 
120
122
  let next = program;
121
- if (ownsAxis) {
122
- next = next.editSemantic({
123
- property: `guide.axis.${definition.guideChannel}.scale`,
124
- value: nextScale
125
- });
126
- for (const component of ["line", "ticks", "labels", "title"]) {
127
- const config = next.guideConfigs.axis?.[definition.guideChannel]?.[component];
128
- if (config?.scale === previousScale) {
129
- next = next._withGuideConfig(definition.guideChannel, component, {
130
- ...config,
131
- scale: nextScale
132
- });
123
+ if (ownsAxis || refreshesReboundAxis) {
124
+ const previousAxisConfig = next.guideConfigs.axis?.[definition.guideChannel];
125
+ const previousTicks = previousAxisConfig?.ticks;
126
+ const previousLabels = previousAxisConfig?.labels;
127
+ const labelsFollowTicks = previousTicks !== undefined &&
128
+ previousLabels !== undefined &&
129
+ previousTicks.mode === previousLabels.mode &&
130
+ (previousTicks.mode === "count"
131
+ ? previousTicks.count === previousLabels.count
132
+ : previousTicks.values?.length === previousLabels.values?.length &&
133
+ previousTicks.values.every(
134
+ (value, index) => Object.is(value, previousLabels.values[index])
135
+ ));
136
+ if (ownsAxis) {
137
+ next = next.editSemantic({
138
+ property: `guide.axis.${definition.guideChannel}.scale`,
139
+ value: nextScale
140
+ });
141
+ for (const component of ["line", "ticks", "labels", "title"]) {
142
+ const config = next.guideConfigs.axis?.[definition.guideChannel]?.[component];
143
+ if (config?.scale === previousScale) {
144
+ next = next._withGuideConfig(definition.guideChannel, component, {
145
+ ...config,
146
+ scale: nextScale
147
+ });
148
+ }
133
149
  }
134
150
  }
151
+ const resolved = next.resolvedScales[nextScale] ??
152
+ findSemanticScale(next, nextScale);
153
+ const discrete = ["ordinal", "band", "point"].includes(resolved?.type);
154
+ let ticks = next.guideConfigs.axis?.[definition.guideChannel]?.ticks;
155
+ if (ticks !== undefined && discrete && ticks.mode !== "values") {
156
+ ticks = {
157
+ ...ticks,
158
+ mode: "values",
159
+ values: resolved.domain,
160
+ inferredValues: true
161
+ };
162
+ delete ticks.count;
163
+ next = next._withGuideConfig(definition.guideChannel, "ticks", ticks);
164
+ } else if (
165
+ ticks !== undefined &&
166
+ !discrete &&
167
+ ticks.mode === "values" &&
168
+ ticks.inferredValues === true
169
+ ) {
170
+ ticks = {
171
+ ...ticks,
172
+ mode: "count",
173
+ count: DEFAULT_TICK_COUNT,
174
+ inferredValues: true
175
+ };
176
+ delete ticks.values;
177
+ next = next._withGuideConfig(definition.guideChannel, "ticks", ticks);
178
+ } else if (
179
+ ticks !== undefined &&
180
+ discrete &&
181
+ ticks.inferredValues === true
182
+ ) {
183
+ ticks = { ...ticks, values: resolved.domain };
184
+ next = next._withGuideConfig(definition.guideChannel, "ticks", ticks);
185
+ }
186
+ if (previousLabels !== undefined && labelsFollowTicks && ticks !== undefined) {
187
+ const labels = {
188
+ ...next.guideConfigs.axis[definition.guideChannel].labels,
189
+ mode: ticks.mode,
190
+ inferredValues: ticks.inferredValues === true,
191
+ ...(ticks.mode === "values"
192
+ ? { values: ticks.values }
193
+ : { count: ticks.count })
194
+ };
195
+ if (ticks.mode === "values") delete labels.count;
196
+ else delete labels.values;
197
+ next = next._withGuideConfig(
198
+ definition.guideChannel,
199
+ "labels",
200
+ labels
201
+ );
202
+ }
135
203
  }
136
204
  if (ownsGrid) {
137
205
  next = next
@@ -0,0 +1,226 @@
1
+ import { action } from "../../core/action.js";
2
+ import {
3
+ readNominalField,
4
+ readQuantitativeField,
5
+ readScaleField,
6
+ readTemporalField,
7
+ resolveTemporalUnit,
8
+ validateCategoricalFieldType
9
+ } from "../../grammar/scales/index.js";
10
+ import { validateContinuousColorConsumer } from
11
+ "../../grammar/scales/colorConsumers.js";
12
+ import { validatePathSeriesAppearance } from "../../grammar/pathSeries.js";
13
+ import { validateRuleStroke } from "../../grammar/ruleAppearance.js";
14
+ import { assertEncodingSelectionCompatibility } from
15
+ "../../materialization/selection/compatibility.js";
16
+ import {
17
+ resolveColorScaleDefinition,
18
+ resolveQuantitativeColorScaleDefinition
19
+ } from "../scales/definitions.js";
20
+ import { applyTemporalUnit } from "./temporal.js";
21
+ import {
22
+ applyEncodingScale,
23
+ rematerializeEncoding,
24
+ resolveReassignmentScaleOptions,
25
+ resolveTarget,
26
+ setEncodingProperties,
27
+ validateOptions
28
+ } from "./shared.js";
29
+
30
+ const OPTIONS = Object.freeze([
31
+ "target", "value", "field", "fieldType", "temporalUnit", "scale"
32
+ ]);
33
+ const MARK_TYPES = Object.freeze([
34
+ "point", "line", "area", "bar", "rect", "arc", "rule", "tick"
35
+ ]);
36
+
37
+ function withoutConstantStroke(program, layer) {
38
+ const config = { ...program.markConfigs[layer.id] };
39
+ if (layer.mark.type === "bar") {
40
+ const appearance = { ...config.barAppearance };
41
+ delete appearance.stroke;
42
+ config.barAppearance = appearance;
43
+ } else {
44
+ delete config.stroke;
45
+ if (layer.mark.type === "area") delete config.strokeFromFill;
46
+ }
47
+ return program._withMarkConfig(layer.id, config);
48
+ }
49
+
50
+ function withConstantStroke(program, layer, value) {
51
+ const config = { ...program.markConfigs[layer.id] };
52
+ if (layer.mark.type === "bar") {
53
+ const appearance = { ...config.barAppearance, stroke: value };
54
+ if (config.barAppearance?.stroke === false && appearance.strokeWidth === undefined) {
55
+ appearance.strokeWidth = 0.5;
56
+ }
57
+ config.barAppearance = appearance;
58
+ } else {
59
+ const restores = config.stroke === false;
60
+ config.stroke = value;
61
+ if (layer.mark.type === "area") delete config.strokeFromFill;
62
+ if (
63
+ (restores || ["point", "area"].includes(layer.mark.type)) &&
64
+ config.strokeWidth === undefined
65
+ ) {
66
+ config.strokeWidth = 1;
67
+ }
68
+ }
69
+ return program._withMarkConfig(layer.id, config);
70
+ }
71
+
72
+ function hasStrokeLegend(program, target) {
73
+ return Object.values(program.guideConfigs.legend ?? {}).some(config =>
74
+ config?.target === target && (
75
+ config.channel === "stroke" || config.channels?.includes("stroke")
76
+ )
77
+ );
78
+ }
79
+
80
+ function validateStrokeField(dataset, layer, args, scale, temporalUnit) {
81
+ const { field, fieldType } = args;
82
+ validateContinuousColorConsumer(layer, args, scale, { channel: "stroke" });
83
+ if (["line", "area"].includes(layer.mark.type)) {
84
+ validatePathSeriesAppearance(dataset.values, {
85
+ ...layer,
86
+ encoding: {
87
+ ...layer.encoding,
88
+ stroke: { field, fieldType, temporalUnit, scale: scale.id }
89
+ }
90
+ });
91
+ }
92
+ if (Object.hasOwn(scale, "unknown")) {
93
+ readScaleField(dataset.values, field, fieldType, {
94
+ allowUnknown: true,
95
+ temporalUnit
96
+ });
97
+ } else if (fieldType === "temporal") {
98
+ readTemporalField(dataset.values, field, temporalUnit);
99
+ } else {
100
+ readQuantitativeField(dataset.values, field);
101
+ }
102
+ }
103
+
104
+ export const encodeStroke = /* @__PURE__ */ action(
105
+ {
106
+ op: "encodeStroke",
107
+ description: "Assign constant or field-driven graphical stroke color."
108
+ },
109
+ function (args = {}) {
110
+ validateOptions(args, OPTIONS, "encodeStroke");
111
+ const hasValue = Object.hasOwn(args, "value");
112
+ const hasField = Object.hasOwn(args, "field");
113
+ if (hasValue === hasField) {
114
+ throw new Error("encodeStroke requires exactly one of value or field.");
115
+ }
116
+ if (hasValue && (
117
+ args.fieldType !== undefined ||
118
+ args.temporalUnit !== undefined ||
119
+ args.scale !== undefined
120
+ )) {
121
+ throw new Error(
122
+ "Constant stroke does not accept fieldType, temporalUnit, or scale."
123
+ );
124
+ }
125
+ const { id: target, dataset, layer } = resolveTarget(
126
+ this,
127
+ args.target,
128
+ MARK_TYPES,
129
+ "stroke-capable mark"
130
+ );
131
+
132
+ if (hasValue) {
133
+ const value = validateRuleStroke(args.value);
134
+ if (
135
+ layer.mark.type === "rule" &&
136
+ layer.encoding?.stroke === undefined &&
137
+ !hasStrokeLegend(this, target)
138
+ ) {
139
+ return this
140
+ ._withMarkConfig(target, { ...this.markConfigs[target], stroke: value })
141
+ .rematerializeRuleMark({ id: target });
142
+ }
143
+ assertEncodingSelectionCompatibility(this, target, ["stroke"]);
144
+ let next = hasStrokeLegend(this, target)
145
+ ? this.removeLegend({ target, channels: ["stroke"] })
146
+ : this;
147
+ if (layer.encoding?.stroke !== undefined) {
148
+ next = next.editSemantic({
149
+ property: `layer[${target}].encoding.stroke`,
150
+ remove: true
151
+ });
152
+ }
153
+ next = withConstantStroke(next, layer, value);
154
+ return rematerializeEncoding(next, target, "stroke", undefined, layer);
155
+ }
156
+
157
+ const fieldType = args.fieldType ?? "nominal";
158
+ const previous = layer.encoding?.stroke;
159
+ const temporalUnit = resolveTemporalUnit(args, fieldType, previous);
160
+ const requestedScale = resolveReassignmentScaleOptions(
161
+ previous,
162
+ args.scale ?? {}
163
+ );
164
+ let scale;
165
+ if (["nominal", "ordinal"].includes(fieldType)) {
166
+ validateCategoricalFieldType(fieldType);
167
+ if (temporalUnit !== undefined) {
168
+ throw new Error("Categorical stroke does not support temporalUnit.");
169
+ }
170
+ scale = resolveColorScaleDefinition(this, requestedScale, "stroke");
171
+ if (Object.hasOwn(scale, "unknown")) {
172
+ readScaleField(dataset.values, args.field, fieldType, {
173
+ allowUnknown: true
174
+ });
175
+ } else {
176
+ readNominalField(dataset.values, args.field);
177
+ }
178
+ if (["line", "area"].includes(layer.mark.type)) {
179
+ validatePathSeriesAppearance(dataset.values, {
180
+ ...layer,
181
+ encoding: {
182
+ ...layer.encoding,
183
+ stroke: {
184
+ field: args.field,
185
+ fieldType,
186
+ scale: scale.id
187
+ }
188
+ }
189
+ });
190
+ }
191
+ } else if (["quantitative", "temporal"].includes(fieldType)) {
192
+ scale = resolveQuantitativeColorScaleDefinition(
193
+ this,
194
+ fieldType,
195
+ requestedScale,
196
+ "stroke"
197
+ );
198
+ validateStrokeField(
199
+ dataset,
200
+ layer,
201
+ { ...args, fieldType },
202
+ scale,
203
+ temporalUnit
204
+ );
205
+ } else {
206
+ throw new Error(`Unsupported stroke field type "${fieldType}".`);
207
+ }
208
+
209
+ let next = withoutConstantStroke(this, layer);
210
+ next = applyTemporalUnit(next, target, "stroke", temporalUnit, previous);
211
+ next = setEncodingProperties(next, target, "stroke", {
212
+ field: args.field,
213
+ fieldType,
214
+ scale: scale.id
215
+ });
216
+ next = applyEncodingScale(next, scale, requestedScale, {
217
+ reassignment: previous?.scale === scale.id,
218
+ allowTypeChange: true
219
+ });
220
+ return rematerializeEncoding(next, target, "stroke", scale.id, layer);
221
+ }
222
+ );
223
+
224
+ export function registerStrokeEncodingAction(ProgramClass) {
225
+ ProgramClass.prototype.encodeStroke = encodeStroke;
226
+ }
@@ -20,7 +20,7 @@ const STROKE_DASH_ENCODING_OPTIONS = Object.freeze([
20
20
  "field", "value", "target", "fieldType", "scale"
21
21
  ]);
22
22
 
23
- const clearStrokeDashEncoding = action(
23
+ const clearStrokeDashEncoding = /* @__PURE__ */ action(
24
24
  {
25
25
  op: "clearStrokeDashEncoding",
26
26
  description: "Remove the current semantic stroke-dash assignment."
@@ -58,7 +58,7 @@ function reconcileLegendAfterDashRemoval(program, target) {
58
58
  .rematerializeLegend();
59
59
  }
60
60
 
61
- const encodeStrokeDash = action(
61
+ const encodeStrokeDash = /* @__PURE__ */ action(
62
62
  {
63
63
  op: "encodeStrokeDash",
64
64
  description: "Encode a nominal field as line stroke dash."
@@ -25,7 +25,7 @@ function validateTextField(rows, field, format) {
25
25
  if (!found) throw new Error(`Unknown text field "${field}".`);
26
26
  }
27
27
 
28
- export const encodeText = action(
28
+ export const encodeText = /* @__PURE__ */ action(
29
29
  {
30
30
  op: "encodeText",
31
31
  description: "Assign field, constant, or source semantic text content."
@@ -1,11 +1,13 @@
1
- import { action } from "../../core/action.js";
1
+ import { editGraphicProperties } from "../primitives/graphicProperties.js";
2
+ import { action, closedAction } from "../../core/action.js";
2
3
  import { isPlainObject } from "../../core/immutable.js";
3
- import {
4
- validateKeys,
5
- validateNonEmptyString,
6
- } from "../../core/validation.js";
4
+ import { validateNonEmptyString } from "../../core/validation.js";
7
5
  import { DEFAULT_COLORS } from "../../theme/defaults.js";
8
6
  import { validateCurveInterpolation } from "../../grammar/curveCommands.js";
7
+ import {
8
+ requestedStrokeDetails,
9
+ STROKE_STYLE_PROPERTIES
10
+ } from "../../grammar/strokeStyle.js";
9
11
  import { createResolvedIntervalData } from "../data/intervalEdit.js";
10
12
  import { resolveIntervalComposite } from "../intervals/resolve.js";
11
13
  import { errorBandBoundaries, resolveBoundaryAppearance } from "./edit.js";
@@ -21,6 +23,7 @@ const OPTIONS = Object.freeze([
21
23
  "fill",
22
24
  "opacity",
23
25
  "curve",
26
+ ...STROKE_STYLE_PROPERTIES,
24
27
  "boundaries"
25
28
  ]);
26
29
 
@@ -77,7 +80,7 @@ function positionOptions({ target, field, fieldType, coordinate, scale, temporal
77
80
  ...(temporalUnit === undefined ? {} : { temporalUnit }) };
78
81
  }
79
82
 
80
- export const createErrorBandBoundary = action(
83
+ export const createErrorBandBoundary = /* @__PURE__ */ action(
81
84
  {
82
85
  op: "createErrorBandBoundary",
83
86
  description: "Create one lower or upper error-band boundary line."
@@ -96,11 +99,24 @@ export const createErrorBandBoundary = action(
96
99
  strokeWidth,
97
100
  strokeDash,
98
101
  opacity,
99
- curve
102
+ curve,
103
+ lineCap,
104
+ lineJoin,
105
+ miterLimit
100
106
  } = {}) {
101
107
  const vertical = orientation === "vertical";
102
108
  let next = this
103
- .createLineMark({ id, data, strokeWidth, curve })
109
+ .createLineMark({
110
+ id,
111
+ data,
112
+ strokeWidth,
113
+ curve,
114
+ ...requestedStrokeDetails(Object.fromEntries([
115
+ ["lineCap", lineCap],
116
+ ["lineJoin", lineJoin],
117
+ ["miterLimit", miterLimit]
118
+ ].filter(([, value]) => value !== undefined)), "Error-band boundary")
119
+ })
104
120
  .encodeY(positionOptions({
105
121
  target: id,
106
122
  field: vertical ? bound : position.field,
@@ -120,14 +136,11 @@ export const createErrorBandBoundary = action(
120
136
  if (groupBy !== undefined) {
121
137
  next = next.encodeGroup({ target: id, field: groupBy });
122
138
  }
123
- return next
124
- .editGraphics({ target: id, property: "stroke", value: stroke })
125
- .editGraphics({
126
- target: id,
127
- property: "strokeDash",
128
- value: next.graphicSpec.objects[id].items.map(() => strokeDash)
129
- })
130
- .editGraphics({ target: id, property: "opacity", value: opacity });
139
+ return editGraphicProperties(next, id, {
140
+ stroke: stroke,
141
+ strokeDash: next.graphicSpec.objects[id].items.map(() => strokeDash),
142
+ opacity: opacity
143
+ });
131
144
  }
132
145
  );
133
146
 
@@ -153,13 +166,13 @@ function rangeArgs(resolved) {
153
166
  };
154
167
  }
155
168
 
156
- export const createErrorBand = action(
169
+ export const createErrorBand = /* @__PURE__ */ closedAction(
157
170
  {
158
171
  op: "createErrorBand",
159
172
  description: "Create a statistical or explicit interval band."
160
- },
173
+ }, OPTIONS,
161
174
  function (args = {}) {
162
- validateKeys(args, OPTIONS, "createErrorBand");
175
+ const strokeDetails = requestedStrokeDetails(args, "createErrorBand");
163
176
  const resolved = resolveErrorBand(this, args);
164
177
  const curve = validateCurveInterpolation(args.curve ?? "linear");
165
178
  const boundaries = resolveBoundaries(args.boundaries, curve);
@@ -170,7 +183,8 @@ export const createErrorBand = action(
170
183
  data: resolved.dataId,
171
184
  ...(args.fill === undefined ? {} : { fill: args.fill }),
172
185
  ...(args.opacity === undefined ? {} : { opacity: args.opacity }),
173
- ...(Object.hasOwn(args, "curve") ? { curve } : {})
186
+ ...(Object.hasOwn(args, "curve") ? { curve } : {}),
187
+ ...strokeDetails
174
188
  });
175
189
  const vertical = resolved.orientation === "vertical";
176
190
  next = next[vertical ? "encodeX" : "encodeY"](positionArgs(resolved));