ggaction 0.0.12 → 0.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) hide show
  1. package/CHANGELOG.md +127 -0
  2. package/README.md +20 -11
  3. package/knowledge/action-card.schema.json +1 -1
  4. package/knowledge/action-cards.json +1 -1
  5. package/knowledge/action-cards.schema.json +1 -1
  6. package/knowledge/extension-authoring.md +2 -1
  7. package/knowledge/intent-taxonomy.json +1 -1
  8. package/knowledge/mcp-resources.json +1 -1
  9. package/knowledge/task-resolver.js +1 -1
  10. package/package.json +10 -3
  11. package/src/actions/basic.js +4 -2
  12. package/src/actions/boxPlots/components.js +14 -4
  13. package/src/actions/boxPlots/edit.js +31 -5
  14. package/src/actions/boxPlots/materialize.js +13 -4
  15. package/src/actions/boxPlots/options.js +48 -16
  16. package/src/actions/canvas/actions.js +3 -1
  17. package/src/actions/canvas/fitting.js +211 -0
  18. package/src/actions/canvas/index.js +2 -0
  19. package/src/actions/categoryOrder/index.js +18 -6
  20. package/src/actions/charts/area.js +51 -0
  21. package/src/actions/charts/bar.js +13 -6
  22. package/src/actions/charts/beeswarm.js +70 -0
  23. package/src/actions/charts/density.js +48 -0
  24. package/src/actions/charts/ecdf.js +186 -0
  25. package/src/actions/charts/endpoints.js +473 -0
  26. package/src/actions/charts/heatmap.js +4 -3
  27. package/src/actions/charts/histogram.js +5 -4
  28. package/src/actions/charts/horizon.js +73 -0
  29. package/src/actions/charts/index.js +37 -0
  30. package/src/actions/charts/interval-regression.js +172 -0
  31. package/src/actions/charts/line.js +8 -6
  32. package/src/actions/charts/parallel.js +8 -2
  33. package/src/actions/charts/pie.js +52 -0
  34. package/src/actions/charts/polar.js +134 -0
  35. package/src/actions/charts/radar.js +305 -0
  36. package/src/actions/charts/radial.js +50 -0
  37. package/src/actions/charts/raincloud.js +422 -0
  38. package/src/actions/charts/rug-strip.js +283 -0
  39. package/src/actions/charts/scatter.js +11 -3
  40. package/src/actions/charts/shared.js +69 -10
  41. package/src/actions/composition/actions.js +129 -3
  42. package/src/actions/coordinates/actions.js +1 -1
  43. package/src/actions/coordinates/edit.js +76 -0
  44. package/src/actions/data/bin.js +29 -0
  45. package/src/actions/data/bin2d.js +57 -185
  46. package/src/actions/data/complete.js +26 -0
  47. package/src/actions/data/computed.js +24 -0
  48. package/src/actions/data/create.js +2 -2
  49. package/src/actions/data/density.js +12 -20
  50. package/src/actions/data/derived.js +111 -15
  51. package/src/actions/data/ecdf.js +38 -0
  52. package/src/actions/data/edit.js +539 -0
  53. package/src/actions/data/filter.js +226 -30
  54. package/src/actions/data/fold.js +24 -0
  55. package/src/actions/data/impute.js +26 -0
  56. package/src/actions/data/index.js +38 -0
  57. package/src/actions/data/interval.js +2 -0
  58. package/src/actions/data/intervalEdit.js +86 -4
  59. package/src/actions/data/normalize.js +27 -0
  60. package/src/actions/data/regression.js +19 -21
  61. package/src/actions/data/revisionBindings.js +145 -0
  62. package/src/actions/data/shared.js +20 -4
  63. package/src/actions/data/stack.js +26 -0
  64. package/src/actions/data/summary.js +26 -0
  65. package/src/actions/data/timeUnit.js +4 -1
  66. package/src/actions/data/window.js +1 -1
  67. package/src/actions/encodings/appearance.js +32 -13
  68. package/src/actions/encodings/barWidth.js +8 -0
  69. package/src/actions/encodings/basic.js +2 -0
  70. package/src/actions/encodings/channels.js +415 -0
  71. package/src/actions/encodings/color/continuous.js +19 -59
  72. package/src/actions/encodings/color/index.js +30 -23
  73. package/src/actions/encodings/color/layout.js +3 -97
  74. package/src/actions/encodings/color/policy.js +5 -8
  75. package/src/actions/encodings/density/resolve.js +37 -5
  76. package/src/actions/encodings/density.js +124 -34
  77. package/src/actions/encodings/histogram.js +3 -2
  78. package/src/actions/encodings/horizon/resolve.js +8 -4
  79. package/src/actions/encodings/horizon.js +18 -8
  80. package/src/actions/encodings/index.js +6 -0
  81. package/src/actions/encodings/offset.js +30 -29
  82. package/src/actions/encodings/parallel.js +2 -8
  83. package/src/actions/encodings/position/apply.js +63 -11
  84. package/src/actions/encodings/position/index.js +52 -6
  85. package/src/actions/encodings/position/policies/arc.js +14 -0
  86. package/src/actions/encodings/position/policies/area.js +4 -42
  87. package/src/actions/encodings/position/policies/bar.js +57 -13
  88. package/src/actions/encodings/position/resolve.js +88 -26
  89. package/src/actions/encodings/ranged.js +123 -97
  90. package/src/actions/encodings/remove.js +44 -34
  91. package/src/actions/encodings/ruleAppearance.js +24 -32
  92. package/src/actions/encodings/seriesLayout.js +105 -0
  93. package/src/actions/encodings/shared.js +107 -37
  94. package/src/actions/encodings/stroke.js +226 -0
  95. package/src/actions/encodings/strokeDash.js +9 -6
  96. package/src/actions/encodings/temporal.js +7 -0
  97. package/src/actions/encodings/text.js +30 -15
  98. package/src/actions/errorBands/create.js +41 -8
  99. package/src/actions/errorBands/edit.js +360 -25
  100. package/src/actions/errorBars/create.js +56 -17
  101. package/src/actions/errorBars/edit.js +372 -21
  102. package/src/actions/facets/actions.js +619 -41
  103. package/src/actions/facets/derive.js +139 -25
  104. package/src/actions/gradientPlots/components.js +43 -13
  105. package/src/actions/gradientPlots/edit.js +14 -4
  106. package/src/actions/gradientPlots/materialize.js +8 -8
  107. package/src/actions/gradientPlots/options.js +10 -2
  108. package/src/actions/guides/applicability.js +28 -19
  109. package/src/actions/guides/axes/axes.js +4 -3
  110. package/src/actions/guides/axes/axis.js +18 -18
  111. package/src/actions/guides/axes/components.js +43 -0
  112. package/src/actions/guides/axes/edit.js +16 -51
  113. package/src/actions/guides/axes/labels.js +184 -28
  114. package/src/actions/guides/axes/lines.js +13 -7
  115. package/src/actions/guides/axes/parallel/lifecycle.js +117 -0
  116. package/src/actions/guides/axes/parallel/policy.js +137 -0
  117. package/src/actions/guides/axes/parallel/resolve.js +50 -25
  118. package/src/actions/guides/axes/parallel.js +93 -217
  119. package/src/actions/guides/axes/policy.js +21 -67
  120. package/src/actions/guides/axes/tickGroups.js +15 -8
  121. package/src/actions/guides/axes/ticks.js +11 -6
  122. package/src/actions/guides/axes/titles.js +32 -11
  123. package/src/actions/guides/basic.js +2 -0
  124. package/src/actions/guides/facade.js +106 -0
  125. package/src/actions/guides/facadeAxes.js +200 -0
  126. package/src/actions/guides/facadeLegend.js +63 -0
  127. package/src/actions/guides/grids/grid.js +37 -33
  128. package/src/actions/guides/grids/resolve.js +17 -4
  129. package/src/actions/guides/guides.js +54 -31
  130. package/src/actions/guides/legends/blocks.js +426 -0
  131. package/src/actions/guides/legends/categorical/actions.js +309 -279
  132. package/src/actions/guides/legends/categorical/components.js +21 -17
  133. package/src/actions/guides/legends/categorical/index.js +37 -33
  134. package/src/actions/guides/legends/categorical/layout.js +123 -354
  135. package/src/actions/guides/legends/categorical/options.js +32 -16
  136. package/src/actions/guides/legends/categorical/recipes.js +114 -9
  137. package/src/actions/guides/legends/categorical/resolve.js +30 -15
  138. package/src/actions/guides/legends/categorical/symbols.js +87 -33
  139. package/src/actions/guides/legends/continuous/common.js +99 -26
  140. package/src/actions/guides/legends/continuous/gradient.js +120 -89
  141. package/src/actions/guides/legends/continuous/index.js +8 -2
  142. package/src/actions/guides/legends/continuous/interval.js +79 -95
  143. package/src/actions/guides/legends/continuous/opacity.js +113 -89
  144. package/src/actions/guides/legends/continuous/stroke.js +525 -0
  145. package/src/actions/guides/legends/creation.js +40 -0
  146. package/src/actions/guides/legends/edit.js +321 -90
  147. package/src/actions/guides/legends/focused.js +2 -2
  148. package/src/actions/guides/legends/index.js +2 -0
  149. package/src/actions/guides/legends/lane.js +149 -102
  150. package/src/actions/guides/legends/lifecycle.js +143 -0
  151. package/src/actions/guides/legends/remove.js +43 -78
  152. package/src/actions/guides/legends/sampling.js +140 -0
  153. package/src/actions/guides/legends/size.js +213 -116
  154. package/src/actions/guides/legends/strokeWidth.js +121 -73
  155. package/src/actions/guides/legends/target.js +123 -0
  156. package/src/actions/guides/legends/transition.js +192 -0
  157. package/src/actions/guides/polar/axes/facade.js +81 -66
  158. package/src/actions/guides/polar/axes/labels.js +28 -8
  159. package/src/actions/guides/polar/axes/lines.js +12 -5
  160. package/src/actions/guides/polar/axes/shared.js +22 -0
  161. package/src/actions/guides/polar/axes/ticks.js +6 -5
  162. package/src/actions/guides/polar/axes/titles.js +6 -5
  163. package/src/actions/guides/polar/grids.js +8 -8
  164. package/src/actions/guides/polar/resolve.js +12 -8
  165. package/src/actions/guides/reuse.js +44 -0
  166. package/src/actions/guides/tickValues.js +19 -21
  167. package/src/actions/index.js +15 -2
  168. package/src/actions/intervals/resolve.js +35 -15
  169. package/src/actions/marks/arc/actions.js +78 -20
  170. package/src/actions/marks/area/actions.js +51 -12
  171. package/src/actions/marks/area/materialize.js +22 -3
  172. package/src/actions/marks/bar/create.js +11 -2
  173. package/src/actions/marks/bar/edit.js +20 -3
  174. package/src/actions/marks/bar/materialize.js +33 -9
  175. package/src/actions/marks/index.js +14 -0
  176. package/src/actions/marks/line/actions.js +71 -113
  177. package/src/actions/marks/line/materialize.js +57 -56
  178. package/src/actions/marks/point/create.js +8 -2
  179. package/src/actions/marks/point/edit.js +29 -21
  180. package/src/actions/marks/point/index.js +1 -0
  181. package/src/actions/marks/point/jitter.js +3 -0
  182. package/src/actions/marks/point/materialize.js +154 -16
  183. package/src/actions/marks/point/packing.js +68 -0
  184. package/src/actions/marks/rect/actions.js +33 -12
  185. package/src/actions/marks/references.js +658 -0
  186. package/src/actions/marks/remove.js +87 -4
  187. package/src/actions/marks/rule/actions.js +77 -9
  188. package/src/actions/marks/rule/appearance.js +26 -0
  189. package/src/actions/marks/shared.js +13 -13
  190. package/src/actions/marks/text/actions.js +296 -13
  191. package/src/actions/marks/text/index.js +4 -0
  192. package/src/actions/marks/text/layout.js +156 -8
  193. package/src/actions/marks/text/removal.js +173 -0
  194. package/src/actions/marks/text/selection.js +73 -0
  195. package/src/actions/marks/tick/actions.js +22 -5
  196. package/src/actions/primitives/createGraphics.js +3 -1
  197. package/src/actions/primitives/editGraphics.js +26 -0
  198. package/src/actions/primitives/semantic.js +3 -2
  199. package/src/actions/primitives/semanticAction.js +120 -10
  200. package/src/actions/primitives/semanticValidation/guide.js +10 -0
  201. package/src/actions/primitives/semanticValidation/index.js +9 -1
  202. package/src/actions/primitives/semanticValidation/layer.js +23 -1
  203. package/src/actions/primitives/semanticValidation/scale.js +38 -8
  204. package/src/actions/regression/components.js +26 -11
  205. package/src/actions/regression/create.js +11 -3
  206. package/src/actions/regression/edit.js +40 -13
  207. package/src/actions/regression/resolve.js +1 -1
  208. package/src/actions/resources/index.js +7 -0
  209. package/src/actions/resources/remove.js +172 -0
  210. package/src/actions/scales/channels.js +212 -0
  211. package/src/actions/scales/consumers/common.js +21 -54
  212. package/src/actions/scales/consumers/families.js +28 -14
  213. package/src/actions/scales/consumers/index.js +42 -4
  214. package/src/actions/scales/consumers/seriesLayout.js +57 -12
  215. package/src/actions/scales/create.js +58 -26
  216. package/src/actions/scales/definitions.js +109 -39
  217. package/src/actions/scales/edit.js +101 -34
  218. package/src/actions/scales/editPolicy.js +95 -35
  219. package/src/actions/scales/index.js +6 -0
  220. package/src/actions/scales/materialize.js +14 -49
  221. package/src/actions/scales/offset.js +48 -0
  222. package/src/actions/scales/parallel.js +71 -0
  223. package/src/actions/scales/patch.js +1 -1
  224. package/src/actions/scales/preview.js +140 -0
  225. package/src/actions/scales/quantitativeColor.js +9 -5
  226. package/src/actions/selection/actions.js +154 -30
  227. package/src/actions/selection/index.js +3 -0
  228. package/src/actions/theme/actions.js +98 -0
  229. package/src/actions/theme/composition.js +238 -0
  230. package/src/actions/theme/index.js +16 -0
  231. package/src/actions/theme/reconcile.js +1130 -0
  232. package/src/actions/theme/state.js +160 -0
  233. package/src/actions/titles/actions.js +11 -10
  234. package/src/actions/titles/resolve.js +1 -40
  235. package/src/actions/violinPlots/create.js +90 -43
  236. package/src/actions/violinPlots/edit.js +192 -0
  237. package/src/actions/violinPlots/index.js +2 -0
  238. package/src/core/ChartProgram.js +7 -0
  239. package/src/core/action.js +32 -2
  240. package/src/core/compositionState.js +141 -8
  241. package/src/core/programState.js +20 -0
  242. package/src/core/resourceReferences.js +332 -0
  243. package/src/core/vocabulary.js +16 -3
  244. package/src/grammar/aggregate.js +66 -23
  245. package/src/grammar/arcs.js +58 -1
  246. package/src/grammar/areaEndpoints.js +46 -0
  247. package/src/grammar/areaSeries.js +87 -41
  248. package/src/grammar/bars/aggregate.js +37 -13
  249. package/src/grammar/bars/geometry.js +44 -0
  250. package/src/grammar/bars/policy.js +9 -7
  251. package/src/grammar/bin.js +226 -0
  252. package/src/grammar/categoryOrder.js +34 -0
  253. package/src/grammar/complete.js +279 -0
  254. package/src/grammar/computed.js +351 -0
  255. package/src/grammar/coordinates.js +37 -0
  256. package/src/grammar/density.js +239 -37
  257. package/src/grammar/displayLabels.js +57 -0
  258. package/src/grammar/ecdf.js +198 -0
  259. package/src/grammar/facets/dependencies.js +133 -1
  260. package/src/grammar/facets/guides.js +3 -0
  261. package/src/grammar/facets/index.js +123 -52
  262. package/src/grammar/facets/scales.js +15 -18
  263. package/src/grammar/fold.js +123 -0
  264. package/src/grammar/horizon.js +6 -4
  265. package/src/grammar/impute.js +340 -0
  266. package/src/grammar/interval.js +37 -20
  267. package/src/grammar/lineSeries.js +20 -63
  268. package/src/grammar/markFilter.js +22 -4
  269. package/src/grammar/markLabelSelection.js +57 -0
  270. package/src/grammar/markLabels.js +110 -0
  271. package/src/grammar/normalize.js +366 -0
  272. package/src/grammar/parallelAxisTitles.js +16 -0
  273. package/src/grammar/pathSeries.js +109 -0
  274. package/src/grammar/pointPacking.js +183 -0
  275. package/src/grammar/pointShapes.js +13 -4
  276. package/src/grammar/polar.js +90 -8
  277. package/src/grammar/positionDatum.js +12 -0
  278. package/src/grammar/rects.js +9 -1
  279. package/src/grammar/regression/derive.js +4 -0
  280. package/src/grammar/regression/index.js +8 -2
  281. package/src/grammar/regression/models.js +22 -7
  282. package/src/grammar/regression/parameters.js +63 -13
  283. package/src/grammar/rotation.js +29 -0
  284. package/src/grammar/roundedRect.js +180 -0
  285. package/src/grammar/rules.js +8 -21
  286. package/src/grammar/scales/color.js +23 -1
  287. package/src/grammar/scales/colorConsumers.js +57 -0
  288. package/src/grammar/scales/continuous.js +2 -2
  289. package/src/grammar/scales/definition.js +42 -5
  290. package/src/grammar/scales/discretized.js +11 -5
  291. package/src/grammar/scales/fields.js +38 -8
  292. package/src/grammar/scales/index.js +1 -0
  293. package/src/grammar/scales/mapping.js +2 -0
  294. package/src/grammar/scales/ordinal.js +12 -3
  295. package/src/grammar/scales/policies.js +2 -2
  296. package/src/grammar/scales/radial.js +55 -0
  297. package/src/grammar/scales/size.js +456 -0
  298. package/src/grammar/scales/types.js +1 -0
  299. package/src/grammar/schemas/concreteGraphic.js +14 -2
  300. package/src/grammar/schemas/graphic.js +15 -0
  301. package/src/grammar/schemas/graphicBounds.js +179 -30
  302. package/src/grammar/schemas/semanticPath.js +23 -4
  303. package/src/grammar/stack.js +170 -0
  304. package/src/grammar/statisticalReference.js +103 -0
  305. package/src/grammar/statistics/confidenceInterval.js +101 -0
  306. package/src/grammar/strokeStyle.js +77 -0
  307. package/src/grammar/summary.js +245 -0
  308. package/src/grammar/text.js +22 -16
  309. package/src/grammar/timeUnit.js +234 -13
  310. package/src/grammar/transformTopology.js +10 -0
  311. package/src/grammar/transforms.js +418 -16
  312. package/src/grammar/valueFormat.js +112 -0
  313. package/src/grammar/weightedStatistics.js +374 -0
  314. package/src/grammar/window.js +232 -21
  315. package/src/layout/aspect.js +80 -0
  316. package/src/layout/facets.js +100 -12
  317. package/src/layout/guideCollisions.js +12 -0
  318. package/src/layout/labels.js +343 -0
  319. package/src/layout/legend.js +2 -10
  320. package/src/layout/legendItems.js +102 -0
  321. package/src/layout/legendLane.js +100 -11
  322. package/src/materialization/bars/aggregate.js +56 -147
  323. package/src/materialization/bars/histogram.js +104 -20
  324. package/src/materialization/bars/ranged.js +19 -3
  325. package/src/materialization/bars/resolve.js +7 -4
  326. package/src/materialization/categorySlotOffset.js +40 -0
  327. package/src/materialization/composition.js +3 -1
  328. package/src/materialization/compositionSnapshot.js +1 -1
  329. package/src/materialization/coordinateBounds.js +204 -0
  330. package/src/materialization/dependencies.js +93 -16
  331. package/src/materialization/encodings.js +1 -1
  332. package/src/materialization/facetGuides/legacyCategorical.js +3 -0
  333. package/src/materialization/facetGuides/placement.js +8 -2
  334. package/src/materialization/facetGuides/preparation.js +12 -7
  335. package/src/materialization/facetHeaders.js +346 -0
  336. package/src/materialization/facets.js +23 -66
  337. package/src/materialization/guides/layout.js +57 -0
  338. package/src/materialization/guides/resources.js +28 -2
  339. package/src/materialization/legends.js +11 -2
  340. package/src/materialization/marks/capabilities.js +20 -5
  341. package/src/materialization/marks/grouping.js +52 -0
  342. package/src/materialization/marks/index.js +68 -27
  343. package/src/materialization/marks/policies.js +4 -1
  344. package/src/materialization/rect.js +55 -42
  345. package/src/materialization/rectConfig.js +5 -1
  346. package/src/materialization/rowEncoding.js +13 -13
  347. package/src/materialization/scaleGuideDependencies.js +8 -5
  348. package/src/materialization/scales/map.js +6 -3
  349. package/src/materialization/scales/policies/arc.js +42 -0
  350. package/src/materialization/scales/policies/binnedPosition.js +7 -4
  351. package/src/materialization/scales/policies/offset.js +50 -8
  352. package/src/materialization/scales/policies/series.js +5 -1
  353. package/src/materialization/scales/resolve.js +65 -17
  354. package/src/materialization/selection/compatibility.js +11 -0
  355. package/src/materialization/selection/items/arc.js +28 -4
  356. package/src/materialization/selection/items/bar.js +42 -71
  357. package/src/materialization/selection/items/common.js +27 -6
  358. package/src/materialization/selection/items/path.js +3 -13
  359. package/src/materialization/selection/items/rect.js +35 -8
  360. package/src/materialization/selection/path.js +1 -1
  361. package/src/materialization/selection/point.js +3 -1
  362. package/src/materialization/text.js +324 -14
  363. package/src/mcp/adapter.js +2 -2
  364. package/src/renderers/canvas/circle.js +2 -3
  365. package/src/renderers/canvas/line.js +2 -3
  366. package/src/renderers/canvas/native.js +4 -4
  367. package/src/renderers/canvas/path.js +2 -3
  368. package/src/renderers/canvas/rect.js +2 -3
  369. package/src/renderers/canvas/stroke.js +11 -0
  370. package/src/renderers/svg.js +19 -0
  371. package/src/selectors/datasets.js +44 -0
  372. package/src/selectors/layers.js +7 -2
  373. package/src/selectors/scales.js +6 -0
  374. package/src/theme/defaults.js +140 -0
  375. package/types/basic.d.ts +17 -2
  376. package/types/index.d.ts +211 -0
  377. package/types/program.d.ts +2182 -204
  378. package/src/materialization/bars/grouped.js +0 -144
@@ -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 = 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
+ }
@@ -11,10 +11,10 @@ import {
11
11
  resolveReassignmentScaleOptions,
12
12
  resolveTarget,
13
13
  setEncodingProperties,
14
- validateLineSeriesCompatibility,
15
14
  validateOptions
16
15
  } from "./shared.js";
17
16
  import { findLayer } from "../../selectors/layers.js";
17
+ import { validatePathSeriesAppearance } from "../../grammar/pathSeries.js";
18
18
 
19
19
  const STROKE_DASH_ENCODING_OPTIONS = Object.freeze([
20
20
  "field", "value", "target", "fieldType", "scale"
@@ -81,6 +81,9 @@ const encodeStrokeDash = action(
81
81
  );
82
82
  if (hasValue) {
83
83
  normalizeStrokeDashPattern(args.value);
84
+ validatePathSeriesAppearance(dataset.values, {
85
+ ...layer, encoding: { ...layer.encoding, strokeDash: { datum: args.value } }
86
+ });
84
87
  let next = layer.encoding?.strokeDash === undefined
85
88
  ? this
86
89
  : this.clearStrokeDashEncoding({ target });
@@ -89,13 +92,13 @@ const encodeStrokeDash = action(
89
92
  value: args.value
90
93
  });
91
94
  next = reconcileLegendAfterDashRemoval(next, target);
92
- return layer.mark.type === "rule"
93
- ? next.rematerializeRuleMark({ id: target })
94
- : next.rematerializeLineMark({ id: target });
95
+ return rematerializeEncoding(next, target, "strokeDash", undefined, layer);
95
96
  }
96
97
 
97
98
  const fieldType = validateNominalFieldType(args.fieldType ?? "nominal");
98
- validateLineSeriesCompatibility(layer, "strokeDash", args.field);
99
+ validatePathSeriesAppearance(dataset.values, {
100
+ ...layer, encoding: { ...layer.encoding, strokeDash: { field: args.field, fieldType } }
101
+ });
99
102
  const previous = layer.encoding?.strokeDash;
100
103
  const requestedScale = previous?.field === args.field
101
104
  ? resolveReassignmentScaleOptions(previous, args.scale ?? {})
@@ -120,7 +123,7 @@ const encodeStrokeDash = action(
120
123
  reassignment: previous?.scale === scale.id
121
124
  });
122
125
 
123
- return rematerializeEncoding(next, target, "strokeDash", scale.id);
126
+ return rematerializeEncoding(next, target, "strokeDash", scale.id, layer);
124
127
  }
125
128
  );
126
129
 
@@ -0,0 +1,7 @@
1
+ export function applyTemporalUnit(program, target, channel, unit, previous) {
2
+ if (unit === previous?.temporalUnit) return program;
3
+ return program.editSemantic({
4
+ property: `layer[${target}].encoding.${channel}.temporalUnit`,
5
+ ...(unit === undefined ? { remove: true } : { value: unit })
6
+ });
7
+ }
@@ -1,6 +1,8 @@
1
1
  import { action } from "../../core/action.js";
2
2
  import { formatTextValue, validateTextFormat } from "../../grammar/text.js";
3
+ import { normalizeMarkLabelContent, resolveMarkLabelValues } from "../../grammar/markLabels.js";
3
4
  import { canMaterializeText } from "../../materialization/marks/index.js";
5
+ import { resolveMarkItems } from "../../materialization/selection/policies/index.js";
4
6
  import { findLayer } from "../../selectors/layers.js";
5
7
  import {
6
8
  resolveTarget,
@@ -8,7 +10,7 @@ import {
8
10
  validateOptions
9
11
  } from "./shared.js";
10
12
 
11
- const OPTIONS = Object.freeze(["target", "field", "value", "format"]);
13
+ const OPTIONS = Object.freeze(["target", "field", "value", "content", "normalizeBy", "format"]);
12
14
 
13
15
  function validateTextField(rows, field, format) {
14
16
  if (typeof field !== "string" || field.length === 0) {
@@ -26,14 +28,18 @@ function validateTextField(rows, field, format) {
26
28
  export const encodeText = action(
27
29
  {
28
30
  op: "encodeText",
29
- description: "Assign field-driven or constant text content."
31
+ description: "Assign field, constant, or source semantic text content."
30
32
  },
31
33
  function (args = {}) {
32
34
  validateOptions(args, OPTIONS, "encodeText");
33
35
  const hasField = Object.hasOwn(args, "field");
34
36
  const hasValue = Object.hasOwn(args, "value");
35
- if (hasField === hasValue) {
36
- throw new Error("encodeText requires exactly one of field or value.");
37
+ const hasContent = Object.hasOwn(args, "content");
38
+ if ([hasField, hasValue, hasContent].filter(Boolean).length !== 1) {
39
+ throw new Error("encodeText requires exactly one of field, value, or content.");
40
+ }
41
+ if (Object.hasOwn(args, "normalizeBy") && (!hasContent || args.content !== "share")) {
42
+ throw new Error("Text normalizeBy is only supported with share content.");
37
43
  }
38
44
  const { id: target, dataset, layer } = resolveTarget(
39
45
  this,
@@ -43,23 +49,32 @@ export const encodeText = action(
43
49
  );
44
50
  const previous = layer.encoding?.text;
45
51
  const format = validateTextFormat(args.format ?? previous?.format ?? "auto");
52
+ const content = hasContent
53
+ ? normalizeMarkLabelContent(findLayer(this, layer.source), args)
54
+ : undefined;
46
55
  if (hasField) validateTextField(dataset.values, args.field, format);
47
- else if (formatTextValue(args.value, format) === undefined) {
56
+ else if (hasValue && formatTextValue(args.value, format) === undefined) {
48
57
  throw new Error("encodeText value must produce non-empty text.");
49
58
  }
50
59
 
60
+ const properties = {
61
+ ...(hasContent ? content : { [hasField ? "field" : "datum"]: hasField ? args.field : args.value }),
62
+ format
63
+ };
64
+ const proposed = { ...layer, encoding: { ...layer.encoding, text: properties } };
65
+ if (hasContent && canMaterializeText(this, proposed)) {
66
+ const source = findLayer(this, layer.source);
67
+ for (const value of resolveMarkLabelValues(source, resolveMarkItems(this, source.id), content)) {
68
+ formatTextValue(value, format);
69
+ }
70
+ }
51
71
  let next = this;
52
- const alternate = hasField ? "datum" : "field";
53
- if (Object.hasOwn(previous ?? {}, alternate)) {
54
- next = next.editSemantic({
55
- property: `layer[${target}].encoding.text.${alternate}`,
56
- remove: true
57
- });
72
+ for (const property of ["field", "datum", "content", "normalizeBy"]) {
73
+ if (Object.hasOwn(previous ?? {}, property) && !Object.hasOwn(properties, property)) {
74
+ next = next.editSemantic({ property: `layer[${target}].encoding.text.${property}`, remove: true });
75
+ }
58
76
  }
59
- next = setEncodingProperties(next, target, "text", {
60
- [hasField ? "field" : "datum"]: hasField ? args.field : args.value,
61
- format
62
- });
77
+ next = setEncodingProperties(next, target, "text", properties);
63
78
  const updated = findLayer(next, target);
64
79
  return canMaterializeText(next, updated)
65
80
  ? next.rematerializeTextMark({ id: target })
@@ -2,9 +2,14 @@ import { action } from "../../core/action.js";
2
2
  import { isPlainObject } from "../../core/immutable.js";
3
3
  import {
4
4
  validateKeys,
5
+ validateNonEmptyString,
5
6
  } from "../../core/validation.js";
6
7
  import { DEFAULT_COLORS } from "../../theme/defaults.js";
7
8
  import { validateCurveInterpolation } from "../../grammar/curveCommands.js";
9
+ import {
10
+ requestedStrokeDetails,
11
+ STROKE_STYLE_PROPERTIES
12
+ } from "../../grammar/strokeStyle.js";
8
13
  import { createResolvedIntervalData } from "../data/intervalEdit.js";
9
14
  import { resolveIntervalComposite } from "../intervals/resolve.js";
10
15
  import { errorBandBoundaries, resolveBoundaryAppearance } from "./edit.js";
@@ -20,10 +25,11 @@ const OPTIONS = Object.freeze([
20
25
  "fill",
21
26
  "opacity",
22
27
  "curve",
28
+ ...STROKE_STYLE_PROPERTIES,
23
29
  "boundaries"
24
30
  ]);
25
31
 
26
- const ERROR_BAND_POLICY = Object.freeze({
32
+ export const ERROR_BAND_POLICY = Object.freeze({
27
33
  operation: "createErrorBand",
28
34
  resourceLabel: "error-band",
29
35
  defaultId: "errorBand",
@@ -40,8 +46,8 @@ const ERROR_BAND_POLICY = Object.freeze({
40
46
  "createErrorBand cannot infer the interval axis when both positions are quantitative; provide an interval option."
41
47
  });
42
48
 
43
- function resolveErrorBand(program, args) {
44
- const resolved = resolveIntervalComposite(program, args, ERROR_BAND_POLICY);
49
+ export function resolveErrorBand(program, args, policy = ERROR_BAND_POLICY) {
50
+ const resolved = resolveIntervalComposite(program, args, policy);
45
51
  if (resolved.groupField === resolved.position.field) {
46
52
  throw new Error(
47
53
  "createErrorBand groupBy must differ from the independent position field."
@@ -71,8 +77,9 @@ function resolveBoundaries(value, areaCurve) {
71
77
  });
72
78
  }
73
79
 
74
- function positionOptions({ target, field, fieldType, coordinate, scale }) {
75
- return { target, field, fieldType, coordinate, scale: { id: scale } };
80
+ function positionOptions({ target, field, fieldType, coordinate, scale, temporalUnit }) {
81
+ return { target, field, fieldType, coordinate, scale: { id: scale },
82
+ ...(temporalUnit === undefined ? {} : { temporalUnit }) };
76
83
  }
77
84
 
78
85
  export const createErrorBandBoundary = action(
@@ -94,15 +101,29 @@ export const createErrorBandBoundary = action(
94
101
  strokeWidth,
95
102
  strokeDash,
96
103
  opacity,
97
- curve
104
+ curve,
105
+ lineCap,
106
+ lineJoin,
107
+ miterLimit
98
108
  } = {}) {
99
109
  const vertical = orientation === "vertical";
100
110
  let next = this
101
- .createLineMark({ id, data, strokeWidth, curve })
111
+ .createLineMark({
112
+ id,
113
+ data,
114
+ strokeWidth,
115
+ curve,
116
+ ...requestedStrokeDetails(Object.fromEntries([
117
+ ["lineCap", lineCap],
118
+ ["lineJoin", lineJoin],
119
+ ["miterLimit", miterLimit]
120
+ ].filter(([, value]) => value !== undefined)), "Error-band boundary")
121
+ })
102
122
  .encodeY(positionOptions({
103
123
  target: id,
104
124
  field: vertical ? bound : position.field,
105
125
  fieldType: vertical ? "quantitative" : position.fieldType,
126
+ temporalUnit: vertical ? undefined : position.temporalUnit,
106
127
  coordinate,
107
128
  scale: vertical ? intervalScale : positionScale
108
129
  }))
@@ -110,6 +131,7 @@ export const createErrorBandBoundary = action(
110
131
  target: id,
111
132
  field: vertical ? position.field : bound,
112
133
  fieldType: vertical ? position.fieldType : "quantitative",
134
+ temporalUnit: vertical ? position.temporalUnit : undefined,
113
135
  coordinate,
114
136
  scale: vertical ? positionScale : intervalScale
115
137
  }));
@@ -132,6 +154,7 @@ function positionArgs(resolved) {
132
154
  target: resolved.id,
133
155
  field: resolved.position.field,
134
156
  fieldType: resolved.position.fieldType,
157
+ ...(resolved.position.temporalUnit === undefined ? {} : { temporalUnit: resolved.position.temporalUnit }),
135
158
  coordinate: resolved.coordinate,
136
159
  scale: resolved.position.scale
137
160
  };
@@ -155,16 +178,19 @@ export const createErrorBand = action(
155
178
  },
156
179
  function (args = {}) {
157
180
  validateKeys(args, OPTIONS, "createErrorBand");
181
+ const strokeDetails = requestedStrokeDetails(args, "createErrorBand");
158
182
  const resolved = resolveErrorBand(this, args);
159
183
  const curve = validateCurveInterpolation(args.curve ?? "linear");
160
184
  const boundaries = resolveBoundaries(args.boundaries, curve);
185
+ if (args.fill !== undefined) validateNonEmptyString(args.fill, "Error-band fill");
161
186
  let next = createResolvedIntervalData(this, resolved);
162
187
  next = next.createAreaMark({
163
188
  id: resolved.id,
164
189
  data: resolved.dataId,
165
190
  ...(args.fill === undefined ? {} : { fill: args.fill }),
166
191
  ...(args.opacity === undefined ? {} : { opacity: args.opacity }),
167
- ...(Object.hasOwn(args, "curve") ? { curve } : {})
192
+ ...(Object.hasOwn(args, "curve") ? { curve } : {}),
193
+ ...strokeDetails
168
194
  });
169
195
  const vertical = resolved.orientation === "vertical";
170
196
  next = next[vertical ? "encodeX" : "encodeY"](positionArgs(resolved));
@@ -216,6 +242,13 @@ export const createErrorBand = action(
216
242
  return next._withMarkConfig(resolved.id, {
217
243
  ...next.markConfigs[resolved.id],
218
244
  errorBand: {
245
+ ...(args.fill === undefined ? {} : { fill: args.fill }),
246
+ source: resolved.source,
247
+ intervalMode: resolved.interval.mode,
248
+ ...(resolved.interval.mode === "statistical"
249
+ ? { intervalField: resolved.interval.field }
250
+ : { centerField: resolved.fields.center }),
251
+ transformGroupBy: resolved.groupBy,
219
252
  data: resolved.dataId,
220
253
  orientation: resolved.orientation,
221
254
  position: resolved.position,