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,305 @@
1
+ import { action } from "../../core/action.js";
2
+ import { STROKE_STYLE_PROPERTIES } from "../../grammar/strokeStyle.js";
3
+ import { isPlainObject } from "../../core/immutable.js";
4
+ import { validateNonEmptyString, validateOptionObject } from
5
+ "../../core/validation.js";
6
+ import { normalizeGroupFields } from "../../grammar/pathSeries.js";
7
+ import { readNominalField, readQuantitativeField } from
8
+ "../../grammar/scales/fields.js";
9
+ import { findDataset } from "../../selectors/datasets.js";
10
+ import {
11
+ normalizeAppearance,
12
+ normalizeEncoding,
13
+ normalizeFieldEncoding,
14
+ normalizeGuides,
15
+ normalizeStrokeDashEncoding,
16
+ omitUndefinedOptions,
17
+ resolveFacadeData,
18
+ resolveFacadeId,
19
+ validateFacadeOptions
20
+ } from "./shared.js";
21
+
22
+ const OPTIONS = Object.freeze([
23
+ "id", "data", "coordinate", "category", "value", "wide", "groupBy",
24
+ "order", "color", "strokeDash", "line", "guides"
25
+ ]);
26
+ const CATEGORY_OPTIONS = Object.freeze(["field", "fieldType", "scale"]);
27
+ const VALUE_OPTIONS = Object.freeze(["field", "fieldType", "scale"]);
28
+ const CATEGORY_SCALE_OPTIONS = Object.freeze([
29
+ "id", "type", "domain", "range", "reverse", "paddingInner", "paddingOuter",
30
+ "padding", "align"
31
+ ]);
32
+ const WIDE_OPTIONS = Object.freeze(["fields", "as"]);
33
+ const WIDE_AS_OPTIONS = Object.freeze(["key", "value"]);
34
+ const LINE_OPTIONS = Object.freeze([
35
+ "strokeWidth", "curve", "stroke", "opacity", "closed",
36
+ ...STROKE_STYLE_PROPERTIES
37
+ ]);
38
+
39
+ function normalizeCategory(value, operation) {
40
+ const category = normalizeFieldEncoding(value, `${operation} category`);
41
+ validateOptionObject(category, CATEGORY_OPTIONS, `${operation} category`);
42
+ validateNonEmptyString(category.field, `${operation} category field`);
43
+ const fieldType = category.fieldType ?? "nominal";
44
+ if (!["nominal", "ordinal"].includes(fieldType)) {
45
+ throw new TypeError(`${operation} category must be nominal or ordinal.`);
46
+ }
47
+ if (category.scale !== undefined) {
48
+ validateOptionObject(
49
+ category.scale,
50
+ CATEGORY_SCALE_OPTIONS,
51
+ `${operation} category.scale`
52
+ );
53
+ if (
54
+ category.scale.type !== undefined &&
55
+ !["band", "point"].includes(category.scale.type)
56
+ ) {
57
+ throw new TypeError(`${operation} category.scale.type must be band or point.`);
58
+ }
59
+ }
60
+ return {
61
+ ...omitUndefinedOptions(category),
62
+ fieldType,
63
+ ...(category.scale === undefined
64
+ ? {}
65
+ : { scale: omitUndefinedOptions(category.scale) })
66
+ };
67
+ }
68
+
69
+ function normalizeValue(value, operation) {
70
+ const result = normalizeFieldEncoding(value, `${operation} value`);
71
+ validateOptionObject(result, VALUE_OPTIONS, `${operation} value`);
72
+ validateNonEmptyString(result.field, `${operation} value field`);
73
+ if (result.fieldType !== undefined && result.fieldType !== "quantitative") {
74
+ throw new TypeError(`${operation} value must be quantitative.`);
75
+ }
76
+ return { ...omitUndefinedOptions(result), fieldType: "quantitative" };
77
+ }
78
+
79
+ function normalizeWide(value, id, operation) {
80
+ if (!isPlainObject(value)) {
81
+ throw new TypeError(`${operation} wide must be a plain object.`);
82
+ }
83
+ validateOptionObject(value, WIDE_OPTIONS, `${operation} wide`);
84
+ if (!Array.isArray(value.fields) || value.fields.length < 3) {
85
+ throw new Error(`${operation} wide.fields must contain at least three fields.`);
86
+ }
87
+ const fields = [...value.fields];
88
+ for (const field of fields) validateNonEmptyString(field, `${operation} wide field`);
89
+ if (new Set(fields).size !== fields.length) {
90
+ throw new Error(`${operation} wide.fields must be unique.`);
91
+ }
92
+ const aliases = value.as === undefined ? {} : value.as;
93
+ if (!isPlainObject(aliases)) {
94
+ throw new TypeError(`${operation} wide.as must be a plain object.`);
95
+ }
96
+ validateOptionObject(aliases, WIDE_AS_OPTIONS, `${operation} wide.as`);
97
+ const key = aliases.key ?? `${id}Dimension`;
98
+ const foldedValue = aliases.value ?? `${id}Value`;
99
+ validateNonEmptyString(key, `${operation} wide.as.key`);
100
+ validateNonEmptyString(foldedValue, `${operation} wide.as.value`);
101
+ if (key === foldedValue || fields.includes(key) || fields.includes(foldedValue)) {
102
+ throw new Error(`${operation} wide aliases must be distinct from selected fields.`);
103
+ }
104
+ return { fields, as: { key, value: foldedValue } };
105
+ }
106
+
107
+ function orderedCategories(rows, field, requested, operation) {
108
+ const observed = [...new Set(readNominalField(rows, field))];
109
+ if (!Array.isArray(requested) && requested !== undefined) {
110
+ throw new TypeError(`${operation} order must be an array.`);
111
+ }
112
+ const order = requested === undefined ? observed : [...requested];
113
+ if (order.length < 3 || new Set(order).size !== order.length) {
114
+ throw new Error(`${operation} order must contain at least three distinct categories.`);
115
+ }
116
+ const observedSet = new Set(observed);
117
+ if (
118
+ order.length !== observed.length ||
119
+ order.some(value => !observedSet.has(value))
120
+ ) {
121
+ throw new Error(`${operation} order must contain every category exactly once.`);
122
+ }
123
+ return order;
124
+ }
125
+
126
+ function seriesKey(row, fields) {
127
+ return JSON.stringify(fields.map(field => row[field]));
128
+ }
129
+
130
+ function radarSeriesFields(groupBy, color, strokeDash, operation) {
131
+ if (groupBy.length > 0) return groupBy;
132
+ const colorField = color?.field;
133
+ const dashField = strokeDash?.field;
134
+ if (
135
+ colorField !== undefined && dashField !== undefined &&
136
+ colorField !== dashField
137
+ ) {
138
+ throw new Error(
139
+ `${operation} color and strokeDash fields must match unless groupBy defines the series.`
140
+ );
141
+ }
142
+ const field = colorField ?? dashField;
143
+ return field === undefined ? [] : [field];
144
+ }
145
+
146
+ function validateRadarRows(rows, { category, value, groupBy, order }, operation) {
147
+ readQuantitativeField(rows, value);
148
+ for (const field of groupBy) readNominalField(rows, field);
149
+ const expected = new Set(order);
150
+ const series = new Map();
151
+ for (const row of rows) {
152
+ const key = seriesKey(row, groupBy);
153
+ const categories = series.get(key) ?? new Set();
154
+ const current = row[category];
155
+ if (!expected.has(current)) {
156
+ throw new Error(`${operation} found a category outside order.`);
157
+ }
158
+ if (categories.has(current)) {
159
+ throw new Error(`${operation} requires one row per series and category.`);
160
+ }
161
+ categories.add(current);
162
+ series.set(key, categories);
163
+ }
164
+ if (series.size === 0) throw new Error(`${operation} requires radar rows.`);
165
+ for (const categories of series.values()) {
166
+ if (
167
+ categories.size !== order.length ||
168
+ order.some(value => !categories.has(value))
169
+ ) {
170
+ throw new Error(`${operation} requires every category in every series.`);
171
+ }
172
+ }
173
+ }
174
+
175
+ function scaleWithOrder(category, order, operation) {
176
+ const scale = category.scale ?? {};
177
+ if (
178
+ scale.domain !== undefined && scale.domain !== "auto" &&
179
+ (!Array.isArray(scale.domain) ||
180
+ scale.domain.length !== order.length ||
181
+ scale.domain.some((value, index) => value !== order[index]))
182
+ ) {
183
+ throw new Error(`${operation} category.scale.domain conflicts with order.`);
184
+ }
185
+ return { ...category, scale: { ...scale, domain: order } };
186
+ }
187
+
188
+ export const createRadarPlot = action(
189
+ {
190
+ op: "createRadarPlot",
191
+ description: "Create a closed Radar plot from long rows or an explicit wide fold."
192
+ },
193
+ function (args = {}) {
194
+ const operation = "createRadarPlot";
195
+ validateFacadeOptions(args, OPTIONS, operation);
196
+ const hasWide = args.wide !== undefined;
197
+ const hasCategory = args.category !== undefined;
198
+ const hasValue = args.value !== undefined;
199
+ if (
200
+ (hasWide && (hasCategory || hasValue)) ||
201
+ (!hasWide && (!hasCategory || !hasValue))
202
+ ) {
203
+ throw new Error(
204
+ `${operation} requires category and value, or wide, but not both.`
205
+ );
206
+ }
207
+ const longForm = !hasWide;
208
+ const id = resolveFacadeId(this, args.id, {
209
+ defaultId: "radarPlot",
210
+ operation
211
+ });
212
+ const source = resolveFacadeData(this, args.data, operation);
213
+ const sourceRows = findDataset(this, source).values;
214
+ const groupBy = args.groupBy === undefined
215
+ ? []
216
+ : normalizeGroupFields(args.groupBy);
217
+ const color = normalizeEncoding(args.color, `${operation} color`);
218
+ const strokeDash = normalizeStrokeDashEncoding(
219
+ args.strokeDash,
220
+ `${operation} strokeDash`
221
+ );
222
+ const seriesFields = radarSeriesFields(
223
+ groupBy,
224
+ color,
225
+ strokeDash,
226
+ operation
227
+ );
228
+ const line = normalizeAppearance(args.line, LINE_OPTIONS, `${operation} line`);
229
+ if (line.closed === false) {
230
+ throw new Error(`${operation} requires a closed line.`);
231
+ }
232
+ const guides = normalizeGuides(args.guides, operation);
233
+
234
+ let next = this;
235
+ let data = source;
236
+ let category;
237
+ let radarValue;
238
+ let order;
239
+ if (longForm) {
240
+ category = normalizeCategory(args.category, operation);
241
+ radarValue = normalizeValue(args.value, operation);
242
+ order = orderedCategories(sourceRows, category.field, args.order, operation);
243
+ validateRadarRows(sourceRows, {
244
+ category: category.field,
245
+ value: radarValue.field,
246
+ groupBy: seriesFields,
247
+ order
248
+ }, operation);
249
+ } else {
250
+ const wide = normalizeWide(args.wide, id, operation);
251
+ if (sourceRows.length > 1 && groupBy.length === 0) {
252
+ throw new Error(`${operation} wide rows require groupBy as series identity.`);
253
+ }
254
+ for (const field of wide.fields) readQuantitativeField(sourceRows, field);
255
+ for (const field of groupBy) readNominalField(sourceRows, field);
256
+ if (!Array.isArray(args.order) && args.order !== undefined) {
257
+ throw new TypeError(`${operation} order must be an array.`);
258
+ }
259
+ order = args.order === undefined ? wide.fields : [...args.order];
260
+ if (
261
+ order.length !== wide.fields.length ||
262
+ new Set(order).size !== order.length ||
263
+ order.some(field => !wide.fields.includes(field))
264
+ ) {
265
+ throw new Error(
266
+ `${operation} order must contain every wide field exactly once.`
267
+ );
268
+ }
269
+ data = `${id}FoldData`;
270
+ next = next.createFoldData({
271
+ id: data,
272
+ source,
273
+ fields: wide.fields,
274
+ as: wide.as
275
+ });
276
+ const foldedRows = findDataset(next, data).values;
277
+ category = { field: wide.as.key, fieldType: "nominal" };
278
+ radarValue = { field: wide.as.value, fieldType: "quantitative" };
279
+ validateRadarRows(foldedRows, {
280
+ category: category.field,
281
+ value: radarValue.field,
282
+ groupBy: seriesFields,
283
+ order
284
+ }, operation);
285
+ }
286
+
287
+ category = scaleWithOrder(category, order, operation);
288
+ next = next.createPolarLinePlot({
289
+ id,
290
+ data,
291
+ ...(args.coordinate === undefined ? {} : { coordinate: args.coordinate }),
292
+ theta: category,
293
+ radius: radarValue,
294
+ ...(groupBy.length === 0 ? {} : { groupBy }),
295
+ ...(color === undefined ? {} : { color }),
296
+ ...(strokeDash === undefined ? {} : { strokeDash }),
297
+ line: { ...line, closed: true },
298
+ guides
299
+ });
300
+ return next._withMarkConfig(id, {
301
+ ...next.markConfigs[id],
302
+ compositionRole: "radar"
303
+ });
304
+ }
305
+ );
@@ -0,0 +1,50 @@
1
+ import { action } from "../../core/action.js";
2
+ import { STROKE_STYLE_PROPERTIES } from "../../grammar/strokeStyle.js";
3
+ import {
4
+ applyFacadeGuides, normalizeAppearance, normalizeArcCategory, normalizeCategoryAggregate,
5
+ normalizeCategoricalColor, normalizeCategoricalGuides, omitUndefinedOptions,
6
+ resolveFacadeData, resolveFacadeId, validateFacadeOptions
7
+ } from "./shared.js";
8
+
9
+ const OPTIONS = ["id", "data", "coordinate", "category", "value", "aggregate", "radiusScale", "color", "arc", "guides"];
10
+ const ARC_OPTIONS = [
11
+ "innerRadius", "padAngle", "fill", "opacity", "stroke", "strokeWidth",
12
+ ...STROKE_STYLE_PROPERTIES
13
+ ];
14
+ const RADIUS_OPTIONS = ["id", "type", "domain", "range", "zero", "nice", "reverse", "clamp"];
15
+
16
+ function createMeasuredPlot(program, args, { operation, defaultId, mapping }) {
17
+ validateFacadeOptions(args, OPTIONS, operation);
18
+ const id = resolveFacadeId(program, args.id, { defaultId, operation });
19
+ const data = resolveFacadeData(program, args.data, operation);
20
+ const category = normalizeArcCategory(args.category, operation);
21
+ const aggregate = normalizeCategoryAggregate(args, operation);
22
+ const arc = omitUndefinedOptions(normalizeAppearance(args.arc, ARC_OPTIONS, `${operation} arc`));
23
+ if (arc.padAngle !== undefined && arc.padAngle !== 0) throw new Error(`${operation} requires padAngle 0.`);
24
+ const radiusScale = args.radiusScale === undefined ? undefined
25
+ : omitUndefinedOptions(normalizeAppearance(args.radiusScale, RADIUS_OPTIONS, `${operation} radiusScale`));
26
+ const color = args.color === false ? undefined
27
+ : normalizeCategoricalColor(args.color === undefined ? category.field : args.color, `${operation} color`);
28
+ if (color !== undefined && arc.fill !== undefined) {
29
+ throw new Error(`${operation} arc.fill cannot be combined with color; use color:false.`);
30
+ }
31
+ const guides = normalizeCategoricalGuides(args.guides, operation, color);
32
+ let next = program.createArcMark({ id, data, ...arc }).encodeTheta({
33
+ ...category, target: id, ...(args.coordinate === undefined ? {} : { coordinate: args.coordinate })
34
+ }).encodeR({ target: id, aggregate, mapping,
35
+ ...(aggregate === "sum" ? { field: args.value } : {}),
36
+ ...(radiusScale === undefined ? {} : { scale: radiusScale }) });
37
+ if (color !== undefined) next = next.encodeColor({ ...color, target: id });
38
+ next = applyFacadeGuides(next, guides, id, args.guides ?? {});
39
+ return next._withMarkConfig(id, {
40
+ ...next.markConfigs[id],
41
+ compositionRole: "rose"
42
+ });
43
+ }
44
+
45
+ export const createRosePlot = action({ op: "createRosePlot", description: "Create equal-angle sectors with area proportional to category count or sum." }, function (args = {}) {
46
+ return createMeasuredPlot(this, args, { operation: "createRosePlot", defaultId: "rosePlot", mapping: "area" });
47
+ });
48
+ export const createRadialBarPlot = action({ op: "createRadialBarPlot", description: "Create equal-angle sectors with radial length proportional to category count or sum." }, function (args = {}) {
49
+ return createMeasuredPlot(this, args, { operation: "createRadialBarPlot", defaultId: "radialBarPlot", mapping: "radius-length" });
50
+ });