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,473 @@
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 { validateUserId } from "../../core/identifiers.js";
5
+ import { isNominalValue } from "../../grammar/scales/fields.js";
6
+ import {
7
+ validateNonEmptyString,
8
+ validateNonNegativeFinite,
9
+ validateOptionObject
10
+ } from "../../core/validation.js";
11
+ import { findDataset } from "../../selectors/datasets.js";
12
+ import { findSemanticScale } from "../../selectors/scales.js";
13
+ import {
14
+ applyFacadeGuides,
15
+ normalizeAppearance,
16
+ normalizeFieldEncoding,
17
+ normalizeGuides,
18
+ omitUndefinedOptions,
19
+ resolveFacadeData,
20
+ resolveFacadeId,
21
+ validateFacadeOptions
22
+ } from "./shared.js";
23
+
24
+ const COMMON_OPTIONS = Object.freeze([
25
+ "id", "data", "coordinate", "category", "orientation", "summary", "guides", "labels"
26
+ ]);
27
+ const DOT_OPTIONS = Object.freeze([...COMMON_OPTIONS, "value", "point"]);
28
+ const LOLLIPOP_OPTIONS = Object.freeze([...DOT_OPTIONS, "baseline", "stem"]);
29
+ const DUMBBELL_OPTIONS = Object.freeze([
30
+ ...COMMON_OPTIONS, "start", "end", "startPoint", "endPoint", "connector"
31
+ ]);
32
+ const CATEGORY_OPTIONS = Object.freeze(["field", "fieldType", "scale"]);
33
+ const VALUE_OPTIONS = Object.freeze(["field", "fieldType", "scale"]);
34
+ const POINT_OPTIONS = Object.freeze([
35
+ "shape", "fill", "opacity", "stroke", "strokeWidth", "radius",
36
+ ...STROKE_STYLE_PROPERTIES
37
+ ]);
38
+ const RULE_OPTIONS = Object.freeze([
39
+ "stroke", "strokeWidth", "strokeDash", "opacity", ...STROKE_STYLE_PROPERTIES
40
+ ]);
41
+ const LABEL_OPTIONS = Object.freeze([
42
+ "endpoint", "field", "value", "content", "normalizeBy", "format", "layout",
43
+ "fill", "opacity", "fontSize", "fontFamily", "fontWeight", "align", "baseline",
44
+ "rotation", "dx", "dy"
45
+ ]);
46
+ const SUMMARY_OPERATIONS = Object.freeze(["mean", "median", "sum", "min", "max"]);
47
+ const EDIT_OPTIONS = Object.freeze([
48
+ "target", "data", "coordinate", "category", "value", "start", "end",
49
+ "orientation", "summary", "baseline"
50
+ ]);
51
+
52
+ function normalizeCategory(value, operation) {
53
+ const category = normalizeFieldEncoding(value, `${operation} category`);
54
+ validateOptionObject(category, CATEGORY_OPTIONS, `${operation} category`);
55
+ validateNonEmptyString(category.field, `${operation} category field`);
56
+ const fieldType = category.fieldType ?? "nominal";
57
+ if (!['nominal', 'ordinal'].includes(fieldType)) {
58
+ throw new TypeError(`${operation} category must be nominal or ordinal.`);
59
+ }
60
+ return { ...omitUndefinedOptions(category), fieldType };
61
+ }
62
+
63
+ function normalizeValue(value, operation, role) {
64
+ const result = normalizeFieldEncoding(value, `${operation} ${role}`);
65
+ validateOptionObject(result, VALUE_OPTIONS, `${operation} ${role}`);
66
+ validateNonEmptyString(result.field, `${operation} ${role} field`);
67
+ if (result.fieldType !== undefined && result.fieldType !== "quantitative") {
68
+ throw new TypeError(`${operation} ${role} must be quantitative.`);
69
+ }
70
+ return { ...omitUndefinedOptions(result), fieldType: "quantitative" };
71
+ }
72
+
73
+ function normalizeOrientation(value, operation) {
74
+ const orientation = value ?? "horizontal";
75
+ if (!['horizontal', 'vertical'].includes(orientation)) {
76
+ throw new Error(`${operation} orientation must be horizontal or vertical.`);
77
+ }
78
+ return orientation;
79
+ }
80
+
81
+ function normalizeSummary(value, operation) {
82
+ if (value === undefined || value === false) return false;
83
+ if (!SUMMARY_OPERATIONS.includes(value)) {
84
+ throw new Error(`${operation} summary must be false or ${SUMMARY_OPERATIONS.join(", ")}.`);
85
+ }
86
+ return value;
87
+ }
88
+
89
+ function sameOptionValue(left, right) {
90
+ if (Object.is(left, right)) return true;
91
+ if (Array.isArray(left) || Array.isArray(right)) {
92
+ return Array.isArray(left) && Array.isArray(right)
93
+ && left.length === right.length
94
+ && left.every((value, index) => sameOptionValue(value, right[index]));
95
+ }
96
+ if (!isPlainObject(left) || !isPlainObject(right)) return false;
97
+ const leftKeys = Object.keys(left).sort();
98
+ const rightKeys = Object.keys(right).sort();
99
+ return leftKeys.length === rightKeys.length
100
+ && leftKeys.every((key, index) => key === rightKeys[index]
101
+ && sameOptionValue(left[key], right[key]));
102
+ }
103
+
104
+ function validateRows(program, data, category, measures, operation) {
105
+ const dataset = findDataset(program, data);
106
+ if (dataset?.values === undefined || dataset.values.length === 0) {
107
+ throw new Error(`${operation} requires at least one source row.`);
108
+ }
109
+ dataset.values.forEach((row, index) => {
110
+ if (!isNominalValue(row?.[category])) {
111
+ throw new TypeError(`${operation} category field "${category}" has an invalid value at row ${index}.`);
112
+ }
113
+ for (const field of measures) {
114
+ if (!Number.isFinite(row?.[field])) {
115
+ throw new TypeError(`${operation} ${field} must be finite at row ${index}.`);
116
+ }
117
+ }
118
+ });
119
+ }
120
+
121
+ function outputField(id, role) {
122
+ return `__${id}_${role}`;
123
+ }
124
+
125
+ function prepareData(program, { id, data, category, measures, summary, operation }) {
126
+ validateRows(program, data, category.field, measures.map(item => item.field), operation);
127
+ if (summary === false) return { program, data, measures };
128
+ const derived = `${id}SummaryData`;
129
+ const outputs = measures.map((item, index) => ({
130
+ ...item,
131
+ field: outputField(id, item.role ?? `value${index + 1}`)
132
+ }));
133
+ return {
134
+ program: program.createSummaryData({
135
+ id: derived,
136
+ source: data,
137
+ groupBy: category.field,
138
+ aggregates: measures.map((item, index) => ({
139
+ op: summary,
140
+ field: item.field,
141
+ as: outputs[index].field
142
+ }))
143
+ }),
144
+ data: derived,
145
+ measures: outputs
146
+ };
147
+ }
148
+
149
+ function positionBindings(id, category, value, orientation, coordinate) {
150
+ const categoryScale = { ...(category.scale ?? {}), id: category.scale?.id ?? `${id}Category` };
151
+ const valueScale = { ...(value.scale ?? {}), id: value.scale?.id ?? `${id}Value` };
152
+ const categoryBinding = {
153
+ field: category.field,
154
+ fieldType: category.fieldType,
155
+ scale: categoryScale,
156
+ ...(coordinate === undefined ? {} : { coordinate })
157
+ };
158
+ const valueBinding = {
159
+ field: value.field,
160
+ fieldType: "quantitative",
161
+ scale: valueScale,
162
+ ...(coordinate === undefined ? {} : { coordinate })
163
+ };
164
+ return orientation === "horizontal"
165
+ ? { x: valueBinding, y: categoryBinding }
166
+ : { x: categoryBinding, y: valueBinding };
167
+ }
168
+
169
+ function createPoint(program, { id, data, bindings, appearance }) {
170
+ const { radius, ...style } = appearance;
171
+ let next = program
172
+ .createPointMark({ id, data, ...style })
173
+ .encodeX({ target: id, ...bindings.x })
174
+ .encodeY({ target: id, ...bindings.y });
175
+ return radius === undefined
176
+ ? next
177
+ : next.encodePointRadius({ target: id, value: radius });
178
+ }
179
+
180
+ function normalizePoint(value, operation, role = "point") {
181
+ const point = normalizeAppearance(value, POINT_OPTIONS, `${operation} ${role}`);
182
+ if (point.radius !== undefined) {
183
+ validateNonNegativeFinite(point.radius, `${operation} ${role} radius`);
184
+ }
185
+ return point;
186
+ }
187
+
188
+ function normalizeRule(value, operation, role) {
189
+ return normalizeAppearance(value, RULE_OPTIONS, `${operation} ${role}`);
190
+ }
191
+
192
+ function createConnector(program, { id, data, bindings, end, appearance }) {
193
+ let next = program
194
+ .createRuleMark({ id, data, ...appearance })
195
+ .encodeX({ target: id, ...bindings.x })
196
+ .encodeY({ target: id, ...bindings.y });
197
+ const valueChannel = bindings.x.fieldType === "quantitative" ? "x" : "y";
198
+ return next[valueChannel === "x" ? "encodeX2" : "encodeY2"]({
199
+ target: id,
200
+ ...end,
201
+ fieldType: "quantitative"
202
+ });
203
+ }
204
+
205
+ function normalizeLabels(value, operation, allowEndpoint) {
206
+ if (value === undefined || value === false) return false;
207
+ if (!isPlainObject(value)) {
208
+ throw new TypeError(`${operation} labels must be false or a plain object.`);
209
+ }
210
+ validateOptionObject(value, LABEL_OPTIONS, `${operation} labels`);
211
+ if (!allowEndpoint && Object.hasOwn(value, "endpoint")) {
212
+ throw new Error(`${operation} labels.endpoint is only available for Dumbbell endpoints.`);
213
+ }
214
+ return { ...value };
215
+ }
216
+
217
+ function normalizeDumbbellGuides(value, orientation, operation) {
218
+ const guides = normalizeGuides(value, operation);
219
+ if (guides === false || guides.axes === false) return guides;
220
+ const channel = orientation === "horizontal" ? "x" : "y";
221
+ const axes = guides.axes ?? {};
222
+ const axis = axes[channel];
223
+ if (axis === false || isPlainObject(axis) && Object.hasOwn(axis, "title")) {
224
+ return guides;
225
+ }
226
+ return {
227
+ ...guides,
228
+ axes: {
229
+ ...axes,
230
+ [channel]: { ...(axis ?? {}), title: false }
231
+ }
232
+ };
233
+ }
234
+
235
+ function labelOptions(labels, defaultField) {
236
+ const { endpoint: _endpoint, ...options } = labels;
237
+ const hasContent = ["field", "value", "content"].some(key => Object.hasOwn(options, key));
238
+ return { ...options, ...(hasContent ? {} : { field: defaultField }) };
239
+ }
240
+
241
+ function attachLabels(program, id, labels, defaultField) {
242
+ if (labels === false) return program;
243
+ return program.createMarkLabels({
244
+ id: `${id}Label`,
245
+ source: id,
246
+ ...labelOptions(labels, defaultField)
247
+ });
248
+ }
249
+
250
+ function finish(program, { id, source, data, kind, roles, summary, orientation, options }) {
251
+ return program._withMarkConfig(id, {
252
+ ...program.markConfigs[id],
253
+ endpointPlot: { kind, source, data, roles, summary, orientation, options }
254
+ });
255
+ }
256
+
257
+ export const createDotPlot = action(
258
+ { op: "createDotPlot", description: "Create raw or explicitly summarized categorical dots." },
259
+ function (args = {}) {
260
+ const operation = "createDotPlot";
261
+ validateFacadeOptions(args, DOT_OPTIONS, operation);
262
+ const id = resolveFacadeId(this, args.id, { defaultId: "dotPlot", operation });
263
+ const source = resolveFacadeData(this, args.data, operation);
264
+ const category = normalizeCategory(args.category, operation);
265
+ const value = normalizeValue(args.value, operation, "value");
266
+ const orientation = normalizeOrientation(args.orientation, operation);
267
+ const summary = normalizeSummary(args.summary, operation);
268
+ const point = normalizePoint(args.point, operation);
269
+ const guides = normalizeGuides(args.guides, operation);
270
+ const labels = normalizeLabels(args.labels, operation, false);
271
+ const prepared = prepareData(this, {
272
+ id, data: source, category, measures: [{ ...value, role: "value" }], summary, operation
273
+ });
274
+ const finalValue = prepared.measures[0];
275
+ const bindings = positionBindings(id, category, finalValue, orientation, args.coordinate);
276
+ let next = createPoint(prepared.program, { id, data: prepared.data, bindings, appearance: point });
277
+ next = attachLabels(next, id, labels, finalValue.field);
278
+ next = applyFacadeGuides(next, guides, id, guides);
279
+ return finish(next, {
280
+ id, source, data: prepared.data, kind: "dot", roles: { category, value }, summary, orientation,
281
+ options: { id, data: source, coordinate: args.coordinate, category, value, orientation,
282
+ summary, point, guides, labels }
283
+ });
284
+ }
285
+ );
286
+
287
+ export const createLollipopPlot = action(
288
+ { op: "createLollipopPlot", description: "Create categorical value points with baseline stems." },
289
+ function (args = {}) {
290
+ const operation = "createLollipopPlot";
291
+ validateFacadeOptions(args, LOLLIPOP_OPTIONS, operation);
292
+ const id = resolveFacadeId(this, args.id, { defaultId: "lollipopPlot", operation });
293
+ const source = resolveFacadeData(this, args.data, operation);
294
+ const category = normalizeCategory(args.category, operation);
295
+ const value = normalizeValue(args.value, operation, "value");
296
+ const orientation = normalizeOrientation(args.orientation, operation);
297
+ const summary = normalizeSummary(args.summary, operation);
298
+ const baseline = args.baseline ?? 0;
299
+ if (!Number.isFinite(baseline)) throw new TypeError(`${operation} baseline must be finite.`);
300
+ const point = normalizePoint(args.point, operation);
301
+ const stem = normalizeRule(args.stem, operation, "stem");
302
+ const guides = normalizeGuides(args.guides, operation);
303
+ const labels = normalizeLabels(args.labels, operation, false);
304
+ const prepared = prepareData(this, {
305
+ id, data: source, category, measures: [{ ...value, role: "value" }], summary, operation
306
+ });
307
+ const finalValue = prepared.measures[0];
308
+ const bindings = positionBindings(id, category, finalValue, orientation, args.coordinate);
309
+ const stemId = `${id}Stem`;
310
+ let next = prepared.program;
311
+ next = createConnector(next, {
312
+ id: stemId,
313
+ data: prepared.data,
314
+ bindings,
315
+ end: { datum: baseline },
316
+ appearance: stem
317
+ });
318
+ next = createPoint(next, { id, data: prepared.data, bindings, appearance: point });
319
+ next = attachLabels(next, id, labels, finalValue.field);
320
+ next = applyFacadeGuides(next, guides, id, guides);
321
+ return finish(next, {
322
+ id, source, data: prepared.data, kind: "lollipop",
323
+ roles: { category, value, baseline, stemId }, summary, orientation,
324
+ options: { id, data: source, coordinate: args.coordinate, category, value, orientation,
325
+ summary, baseline, point, stem, guides, labels }
326
+ });
327
+ }
328
+ );
329
+
330
+ export const createDumbbellPlot = action(
331
+ { op: "createDumbbellPlot", description: "Create two categorical endpoints joined by a connector." },
332
+ function (args = {}) {
333
+ const operation = "createDumbbellPlot";
334
+ validateFacadeOptions(args, DUMBBELL_OPTIONS, operation);
335
+ const id = resolveFacadeId(this, args.id, { defaultId: "dumbbellPlot", operation });
336
+ const source = resolveFacadeData(this, args.data, operation);
337
+ const category = normalizeCategory(args.category, operation);
338
+ const start = normalizeValue(args.start, operation, "start");
339
+ const end = normalizeValue(args.end, operation, "end");
340
+ if (start.field === end.field) throw new Error(`${operation} start and end fields must be distinct.`);
341
+ if (start.scale !== undefined && end.scale !== undefined
342
+ && !sameOptionValue(start.scale, end.scale)) {
343
+ throw new Error(`${operation} start and end must use the same quantitative scale.`);
344
+ }
345
+ const orientation = normalizeOrientation(args.orientation, operation);
346
+ const summary = normalizeSummary(args.summary, operation);
347
+ const startPoint = normalizePoint(args.startPoint, operation, "startPoint");
348
+ const endPoint = normalizePoint(args.endPoint, operation, "endPoint");
349
+ const connector = normalizeRule(args.connector, operation, "connector");
350
+ const guides = normalizeDumbbellGuides(args.guides, orientation, operation);
351
+ const labels = normalizeLabels(args.labels, operation, true);
352
+ const sharedScale = start.scale ?? end.scale;
353
+ const prepared = prepareData(this, {
354
+ id, data: source, category,
355
+ measures: [
356
+ { ...start, scale: sharedScale, role: "start" },
357
+ { ...end, scale: sharedScale, role: "end" }
358
+ ],
359
+ summary, operation
360
+ });
361
+ const [finalStart, finalEnd] = prepared.measures;
362
+ const startBindings = positionBindings(id, category, finalStart, orientation, args.coordinate);
363
+ const endBindings = positionBindings(id, category, finalEnd, orientation, args.coordinate);
364
+ const connectorId = `${id}Connector`;
365
+ const startId = `${id}Start`;
366
+ let next = createConnector(prepared.program, {
367
+ id: connectorId,
368
+ data: prepared.data,
369
+ bindings: startBindings,
370
+ end: { field: finalEnd.field },
371
+ appearance: connector
372
+ });
373
+ next = createPoint(next, {
374
+ id: startId, data: prepared.data, bindings: startBindings, appearance: startPoint
375
+ });
376
+ next = createPoint(next, {
377
+ id, data: prepared.data, bindings: endBindings, appearance: endPoint
378
+ });
379
+ if (labels !== false) {
380
+ const endpoint = labels.endpoint ?? "end";
381
+ if (!['start', 'end', 'both'].includes(endpoint)) {
382
+ throw new Error(`${operation} labels.endpoint must be start, end, or both.`);
383
+ }
384
+ for (const [role, target] of [["start", startId], ["end", id]]) {
385
+ if (endpoint !== "both" && endpoint !== role) continue;
386
+ next = next.createMarkLabels({
387
+ id: endpoint === "both" ? `${target}Label` : `${id}Label`,
388
+ source: target,
389
+ ...labelOptions(labels, role === "start" ? finalStart.field : finalEnd.field)
390
+ });
391
+ }
392
+ }
393
+ next = applyFacadeGuides(next, guides, id, guides);
394
+ return finish(next, {
395
+ id, source, data: prepared.data, kind: "dumbbell",
396
+ roles: { category, start, end, startId, connectorId }, summary, orientation,
397
+ options: { id, data: source, coordinate: args.coordinate, category, start, end, orientation,
398
+ summary, startPoint, endPoint, connector, guides, labels }
399
+ });
400
+ }
401
+ );
402
+
403
+ function resolveEndpointOwner(program, requested) {
404
+ const eligible = program.semanticSpec.layers.filter(
405
+ layer => program.markConfigs[layer.id]?.endpointPlot !== undefined
406
+ );
407
+ if (requested !== undefined) {
408
+ const id = validateUserId(requested, "Endpoint-plot owner id");
409
+ const owner = eligible.find(layer => layer.id === id);
410
+ if (owner === undefined) throw new Error(`Unknown endpoint-plot owner "${id}".`);
411
+ return owner;
412
+ }
413
+ const current = eligible.find(layer => layer.id === program.context.currentMark);
414
+ if (current !== undefined) return current;
415
+ if (eligible.length === 1) return eligible[0];
416
+ if (eligible.length === 0) throw new Error("editEndpointPlot requires an endpoint plot.");
417
+ throw new Error("editEndpointPlot target is ambiguous; provide target.");
418
+ }
419
+
420
+ const KIND_METHOD = Object.freeze({
421
+ dot: "createDotPlot",
422
+ lollipop: "createLollipopPlot",
423
+ dumbbell: "createDumbbellPlot"
424
+ });
425
+ const KIND_ROLES = Object.freeze({
426
+ dot: new Set(["data", "coordinate", "category", "value", "orientation", "summary"]),
427
+ lollipop: new Set([
428
+ "data", "coordinate", "category", "value", "orientation", "summary", "baseline"
429
+ ]),
430
+ dumbbell: new Set([
431
+ "data", "coordinate", "category", "start", "end", "orientation", "summary"
432
+ ])
433
+ });
434
+
435
+ function reviseRequestedScales(program, ownerId, revised, changes) {
436
+ let next = program;
437
+ for (const role of ["category", "value", "start", "end"]) {
438
+ if (!changes.includes(role) || !isPlainObject(revised[role]?.scale)) continue;
439
+ const requested = revised[role].scale;
440
+ const fallback = role === "category" ? `${ownerId}Category` : `${ownerId}Value`;
441
+ const id = requested.id ?? fallback;
442
+ if (findSemanticScale(next, id) === undefined) continue;
443
+ const { id: _id, ...definition } = requested;
444
+ void _id;
445
+ if (Object.keys(definition).length > 0) {
446
+ next = next.editScale({ id, ...definition });
447
+ }
448
+ }
449
+ return next;
450
+ }
451
+
452
+ export const editEndpointPlot = action(
453
+ { op: "editEndpointPlot", description: "Atomically revise an endpoint plot's semantic roles." },
454
+ function (args = {}) {
455
+ validateFacadeOptions(args, EDIT_OPTIONS, "editEndpointPlot");
456
+ const changes = Object.keys(args).filter(key => key !== "target");
457
+ if (changes.length === 0) {
458
+ throw new Error("editEndpointPlot requires at least one endpoint-plot option.");
459
+ }
460
+ const owner = resolveEndpointOwner(this, args.target);
461
+ const current = this.markConfigs[owner.id].endpointPlot;
462
+ const allowed = KIND_ROLES[current.kind];
463
+ const invalid = changes.find(key => !allowed.has(key));
464
+ if (invalid !== undefined) {
465
+ throw new Error(`editEndpointPlot ${invalid} is not available for ${current.kind} plots.`);
466
+ }
467
+ const revised = { ...current.options };
468
+ for (const key of changes) revised[key] = args[key];
469
+ let next = this.removeMark({ target: owner.id });
470
+ next = reviseRequestedScales(next, owner.id, revised, changes);
471
+ return next[KIND_METHOD[current.kind]](revised);
472
+ }
473
+ );
@@ -5,6 +5,7 @@ import {
5
5
  validateOptionObject
6
6
  } from "../../core/validation.js";
7
7
  import { requireDataset } from "../../selectors/datasets.js";
8
+ import { RECT_STYLE_PROPERTIES } from "../../grammar/roundedRect.js";
8
9
  import {
9
10
  applyFacadeGuides,
10
11
  normalizeAppearance,
@@ -22,7 +23,7 @@ const OPTIONS = [
22
23
  "id", "data", "coordinate", "x", "y", "bin", "color", "rect", "guides"
23
24
  ];
24
25
  const RECT_OPTIONS = [
25
- "opacity", "stroke", "strokeWidth"
26
+ "opacity", "stroke", "strokeWidth", ...RECT_STYLE_PROPERTIES
26
27
  ];
27
28
  const BIN_OPTIONS = [
28
29
  "bins", "extent", "includeEmpty"
@@ -158,7 +159,7 @@ export const createHeatmap = action(
158
159
  .encodeX(positionArgs(x, { target: id, coordinate: args.coordinate }))
159
160
  .encodeY(positionArgs(y, { target: id, coordinate: args.coordinate }))
160
161
  .encodeColor(targetArgs(color, id));
161
- return applyFacadeGuides(next, guides);
162
+ return applyFacadeGuides(next, guides, id);
162
163
  }
163
164
 
164
165
  const x = normalizePosition(args.x, "x");
@@ -190,6 +191,6 @@ export const createHeatmap = action(
190
191
  fieldType: "quantitative",
191
192
  ...color
192
193
  });
193
- return applyFacadeGuides(next, resolvedGuides);
194
+ return applyFacadeGuides(next, resolvedGuides, id, guides);
194
195
  }
195
196
  );
@@ -1,5 +1,6 @@
1
1
  import { action } from "../../core/action.js";
2
2
  import { validateNonEmptyString } from "../../core/validation.js";
3
+ import { RECT_STYLE_PROPERTIES } from "../../grammar/roundedRect.js";
3
4
  import {
4
5
  applyFacadeGuides,
5
6
  normalizeAppearance,
@@ -13,13 +14,13 @@ import {
13
14
 
14
15
  const OPTIONS = Object.freeze([
15
16
  "id", "data", "coordinate", "field", "maxBins", "binStep",
16
- "binBoundaries", "stack", "xScale", "yScale", "color", "bar", "guides"
17
+ "binBoundaries", "stack", "xScale", "yScale", "weight", "color", "bar", "guides"
17
18
  ]);
18
19
  const BAR_OPTIONS = Object.freeze([
19
- "fill", "opacity", "stroke", "strokeWidth"
20
+ "fill", "opacity", "stroke", "strokeWidth", ...RECT_STYLE_PROPERTIES
20
21
  ]);
21
22
  const HISTOGRAM_OPTIONS = Object.freeze([
22
- "maxBins", "binStep", "binBoundaries", "stack", "xScale", "yScale"
23
+ "maxBins", "binStep", "binBoundaries", "stack", "xScale", "yScale", "weight"
23
24
  ]);
24
25
 
25
26
  export const createHistogram = action(
@@ -53,6 +54,6 @@ export const createHistogram = action(
53
54
  ...histogram
54
55
  });
55
56
  if (color !== undefined) next = next.encodeColor(targetArgs(color, id));
56
- return applyFacadeGuides(next, guides);
57
+ return applyFacadeGuides(next, guides, id);
57
58
  }
58
59
  );
@@ -0,0 +1,73 @@
1
+ import { action } from "../../core/action.js";
2
+ import { STROKE_STYLE_PROPERTIES } from "../../grammar/strokeStyle.js";
3
+ import { validateNonEmptyString, validateOptionObject, validateUnitInterval } from "../../core/validation.js";
4
+ import {
5
+ applyFacadeGuides, normalizeAppearance, normalizeFieldEncoding, normalizeGuides,
6
+ omitUndefinedOptions, resolveFacadeData, resolveFacadeId, validateFacadeOptions
7
+ } from "./shared.js";
8
+
9
+ const OPERATION = "createHorizonPlot";
10
+ const OPTIONS = ["id", "data", "coordinate", "x", "y", "groupBy", "bands", "baseline", "extent",
11
+ "resolve", "missing", "overflow", "palette", "area", "guides"];
12
+
13
+ function position(value, role) {
14
+ const label = `${OPERATION} ${role}`;
15
+ const field = normalizeFieldEncoding(value, label);
16
+ validateOptionObject(field, ["field", "fieldType", "scale", ...(role === "x" ? ["temporalUnit"] : [])], label);
17
+ validateNonEmptyString(field.field, `${label} field`);
18
+ if (field.fieldType === null || field.temporalUnit === null) throw new TypeError(`${label} type/unit cannot be null.`);
19
+ if (field.scale !== undefined) validateOptionObject(field.scale, undefined, `${label}.scale`);
20
+ return { ...omitUndefinedOptions(field),
21
+ ...(field.scale === undefined ? {} : { scale: omitUndefinedOptions(field.scale) }) };
22
+ }
23
+
24
+ function guideOptions(value) {
25
+ const guides = normalizeGuides(value, OPERATION);
26
+ if (guides === false) return false;
27
+ validateOptionObject(guides, ["axes", "grid", "legend"], `${OPERATION} guides`);
28
+ for (const [family, forbidden, supported] of [
29
+ ["axes", "y", ["coordinate", "x", "y"]], ["grid", "horizontal", ["horizontal", "vertical"]]
30
+ ]) {
31
+ const options = guides[family];
32
+ if (options === undefined || options === false) continue;
33
+ validateOptionObject(options, supported, `${OPERATION} guides.${family}`);
34
+ if (options[forbidden] !== undefined && options[forbidden] !== false) {
35
+ throw new Error(`${OPERATION} guides.${family}.${forbidden} only accepts false.`);
36
+ }
37
+ }
38
+ if (guides.legend !== undefined && guides.legend !== false) {
39
+ throw new Error(`${OPERATION} guides.legend only accepts false.`);
40
+ }
41
+ return guides;
42
+ }
43
+
44
+ export const createHorizonPlot = action({
45
+ op: OPERATION, description: "Create a signed, folded horizon area with original-x guides."
46
+ }, function (args = {}) {
47
+ validateFacadeOptions(args, OPTIONS, OPERATION);
48
+ const id = resolveFacadeId(this, args.id, { defaultId: "horizonPlot", operation: OPERATION });
49
+ const data = resolveFacadeData(this, args.data, OPERATION);
50
+ const x = position(args.x, "x");
51
+ const y = position(args.y, "y");
52
+ const { id: requestedId, data: requestedData, coordinate, x: requestedX, y: requestedY,
53
+ area: requestedArea, guides: requestedGuides, ...horizon } = omitUndefinedOptions(args);
54
+ const { opacity, ...area } = omitUndefinedOptions(normalizeAppearance(requestedArea,
55
+ ["opacity", "stroke", "strokeWidth", "curve", ...STROKE_STYLE_PROPERTIES],
56
+ `${OPERATION} area`));
57
+ for (const [key, value] of Object.entries({ ...horizon, ...area })) {
58
+ if (value === null) throw new TypeError(`${OPERATION} ${key} cannot be null.`);
59
+ }
60
+ if (horizon.palette !== undefined) {
61
+ validateOptionObject(horizon.palette, ["positive", "negative"], `${OPERATION} palette`);
62
+ if (Object.values(horizon.palette).some(value => value === null)) {
63
+ throw new TypeError(`${OPERATION} palette entries cannot be null.`);
64
+ }
65
+ }
66
+ if (opacity !== undefined) validateUnitInterval(opacity, `${OPERATION} area.opacity`);
67
+ const guides = guideOptions(requestedGuides);
68
+ let next = this.createAreaMark({ id, data, ...area });
69
+ if (coordinate !== undefined) next = next.createCoordinate({ id: coordinate, type: "cartesian", layers: [id] });
70
+ next = next.encodeHorizon({ ...horizon, target: id, x, y });
71
+ if (opacity !== undefined) next = next.editAreaMark({ target: id, opacity });
72
+ return applyFacadeGuides(next, guides, id, requestedGuides ?? {});
73
+ });
@@ -1,15 +1,52 @@
1
+ import { createRosePlot, createRadialBarPlot } from "./radial.js";
2
+ import { createAreaPlot } from "./area.js";
1
3
  import { createHeatmap } from "./heatmap.js";
2
4
  import { createBarPlot } from "./bar.js";
3
5
  import { createHistogram } from "./histogram.js";
4
6
  import { createLinePlot } from "./line.js";
5
7
  import { createScatterPlot } from "./scatter.js";
6
8
  import { createParallelCoordinates } from "./parallel.js";
9
+ import { createPiePlot } from "./pie.js";
10
+ import { createDensityPlot } from "./density.js";
11
+ import { createHorizonPlot } from "./horizon.js";
12
+ import { createPolarLinePlot, createPolarScatterPlot } from "./polar.js";
13
+ import { createRadarPlot } from "./radar.js";
14
+ import { createRugPlot, createStripPlot } from "./rug-strip.js";
15
+ import { createIntervalPlot, createRegressionPlot } from "./interval-regression.js";
16
+ import {
17
+ createDotPlot, createLollipopPlot, createDumbbellPlot, editEndpointPlot
18
+ } from "./endpoints.js";
19
+ import { createECDFPlot, editECDFPlot } from "./ecdf.js";
20
+ import { createBeeswarmPlot } from "./beeswarm.js";
21
+ import { createRaincloudPlot, editRaincloudPlot } from "./raincloud.js";
7
22
 
8
23
  export function registerChartActions(ProgramClass) {
24
+ ProgramClass.prototype.createRosePlot = createRosePlot;
25
+ ProgramClass.prototype.createRadialBarPlot = createRadialBarPlot;
26
+ ProgramClass.prototype.createAreaPlot = createAreaPlot;
9
27
  ProgramClass.prototype.createBarPlot = createBarPlot;
10
28
  ProgramClass.prototype.createHeatmap = createHeatmap;
11
29
  ProgramClass.prototype.createHistogram = createHistogram;
12
30
  ProgramClass.prototype.createScatterPlot = createScatterPlot;
13
31
  ProgramClass.prototype.createLinePlot = createLinePlot;
14
32
  ProgramClass.prototype.createParallelCoordinates = createParallelCoordinates;
33
+ ProgramClass.prototype.createPiePlot = createPiePlot;
34
+ ProgramClass.prototype.createDensityPlot = createDensityPlot;
35
+ ProgramClass.prototype.createHorizonPlot = createHorizonPlot;
36
+ ProgramClass.prototype.createPolarScatterPlot = createPolarScatterPlot;
37
+ ProgramClass.prototype.createPolarLinePlot = createPolarLinePlot;
38
+ ProgramClass.prototype.createRadarPlot = createRadarPlot;
39
+ ProgramClass.prototype.createRugPlot = createRugPlot;
40
+ ProgramClass.prototype.createStripPlot = createStripPlot;
41
+ ProgramClass.prototype.createIntervalPlot = createIntervalPlot;
42
+ ProgramClass.prototype.createRegressionPlot = createRegressionPlot;
43
+ ProgramClass.prototype.createDotPlot = createDotPlot;
44
+ ProgramClass.prototype.createLollipopPlot = createLollipopPlot;
45
+ ProgramClass.prototype.createDumbbellPlot = createDumbbellPlot;
46
+ ProgramClass.prototype.editEndpointPlot = editEndpointPlot;
47
+ ProgramClass.prototype.createECDFPlot = createECDFPlot;
48
+ ProgramClass.prototype.editECDFPlot = editECDFPlot;
49
+ ProgramClass.prototype.createBeeswarmPlot = createBeeswarmPlot;
50
+ ProgramClass.prototype.createRaincloudPlot = createRaincloudPlot;
51
+ ProgramClass.prototype.editRaincloudPlot = editRaincloudPlot;
15
52
  }