ggaction 0.0.16 → 0.0.18

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 (299) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/README.md +3 -2
  3. package/knowledge/action-card.schema.json +1 -1
  4. package/knowledge/action-cards.json +1 -1
  5. package/knowledge/intent-taxonomy.json +1 -1
  6. package/knowledge/mcp-resources.json +1 -1
  7. package/knowledge/task-resolver.js +1 -1
  8. package/package.json +10 -4
  9. package/src/BasicChartProgram.js +1 -1
  10. package/src/accessibility.js +3 -2
  11. package/src/actions/boxPlots/create.js +14 -4
  12. package/src/actions/boxPlots/edit.js +39 -33
  13. package/src/actions/boxPlots/materialize.js +7 -3
  14. package/src/actions/boxPlots/options.js +37 -11
  15. package/src/actions/canvas/actions.js +70 -95
  16. package/src/actions/canvas/fitting.js +16 -1
  17. package/src/actions/canvas/index.js +3 -0
  18. package/src/actions/canvas/plotBorder.js +28 -0
  19. package/src/actions/charts/area.js +2 -2
  20. package/src/actions/charts/bar.js +34 -7
  21. package/src/actions/charts/ecdf.js +4 -18
  22. package/src/actions/charts/endpoints.js +4 -18
  23. package/src/actions/charts/heatmap.js +4 -4
  24. package/src/actions/charts/index.js +2 -0
  25. package/src/actions/charts/interval-regression.js +2 -2
  26. package/src/actions/charts/line.js +1 -1
  27. package/src/actions/charts/pie.js +3 -47
  28. package/src/actions/charts/radial.js +34 -12
  29. package/src/actions/charts/scatter.js +16 -2
  30. package/src/actions/charts/shared.js +2 -2
  31. package/src/actions/charts/text.js +35 -0
  32. package/src/actions/composition/actions.js +6 -2
  33. package/src/actions/data/bin.js +1 -1
  34. package/src/actions/data/box.js +2 -1
  35. package/src/actions/data/create.js +18 -5
  36. package/src/actions/data/density.js +1 -1
  37. package/src/actions/data/derived.js +15 -1
  38. package/src/actions/data/edit.js +20 -7
  39. package/src/actions/data/filter.js +15 -4
  40. package/src/actions/data/index.js +3 -0
  41. package/src/actions/data/interval.js +2 -0
  42. package/src/actions/data/regression.js +6 -2
  43. package/src/actions/data/revise.js +37 -5
  44. package/src/actions/data/shared.js +33 -4
  45. package/src/actions/data/sort.js +21 -0
  46. package/src/actions/data/stack.js +3 -1
  47. package/src/actions/data/summary.js +1 -1
  48. package/src/actions/encodings/angle.js +4 -1
  49. package/src/actions/encodings/appearance.js +43 -31
  50. package/src/actions/encodings/barWidth.js +36 -11
  51. package/src/actions/encodings/channels.js +0 -1
  52. package/src/actions/encodings/color/continuous.js +5 -2
  53. package/src/actions/encodings/color/index.js +5 -2
  54. package/src/actions/encodings/color/policy.js +10 -3
  55. package/src/actions/encodings/density/resolve.js +1 -6
  56. package/src/actions/encodings/offset.js +4 -1
  57. package/src/actions/encodings/position/policies/arc.js +12 -10
  58. package/src/actions/encodings/position/policies/area.js +3 -3
  59. package/src/actions/encodings/position/policies/bar.js +15 -6
  60. package/src/actions/encodings/position/policies/line.js +17 -6
  61. package/src/actions/encodings/position/resolve.js +9 -3
  62. package/src/actions/encodings/ranged.js +23 -1
  63. package/src/actions/encodings/remove.js +3 -2
  64. package/src/actions/encodings/ruleAppearance.js +4 -1
  65. package/src/actions/encodings/seriesLayout.js +1 -0
  66. package/src/actions/encodings/stroke.js +4 -1
  67. package/src/actions/encodings/strokeDash.js +6 -1
  68. package/src/actions/encodings/text.js +12 -4
  69. package/src/actions/errorBands/create.js +2 -2
  70. package/src/actions/errorBands/edit.js +2 -1
  71. package/src/actions/facets/actions.js +158 -240
  72. package/src/actions/gradientPlots/resolve.js +2 -17
  73. package/src/actions/guides/applicability.js +1 -1
  74. package/src/actions/guides/axes/axis.js +16 -4
  75. package/src/actions/guides/axes/edit.js +4 -4
  76. package/src/actions/guides/axes/labels.js +120 -46
  77. package/src/actions/guides/axes/lines.js +6 -5
  78. package/src/actions/guides/axes/parallel/lifecycle.js +1 -1
  79. package/src/actions/guides/axes/parallel/policy.js +8 -7
  80. package/src/actions/guides/axes/remove.js +3 -2
  81. package/src/actions/guides/axes/tickGroups.js +5 -2
  82. package/src/actions/guides/axes/ticks.js +6 -3
  83. package/src/actions/guides/axes/titles.js +71 -26
  84. package/src/actions/guides/basic.js +2 -0
  85. package/src/actions/guides/facadeLegend.js +10 -2
  86. package/src/actions/guides/grids/grid.js +7 -1
  87. package/src/actions/guides/grids/resolve.js +59 -12
  88. package/src/actions/guides/legends/blocks.js +3 -1
  89. package/src/actions/guides/legends/categorical/actions.js +70 -17
  90. package/src/actions/guides/legends/categorical/components.js +12 -8
  91. package/src/actions/guides/legends/categorical/index.js +6 -0
  92. package/src/actions/guides/legends/categorical/layout.js +37 -8
  93. package/src/actions/guides/legends/categorical/options.js +31 -20
  94. package/src/actions/guides/legends/categorical/resolve.js +2 -2
  95. package/src/actions/guides/legends/categorical/symbols.js +18 -12
  96. package/src/actions/guides/legends/categorical/validation.js +1 -6
  97. package/src/actions/guides/legends/continuous/colorSymbols.js +1 -0
  98. package/src/actions/guides/legends/continuous/common.js +1 -447
  99. package/src/actions/guides/legends/continuous/gradient.js +47 -34
  100. package/src/actions/guides/legends/continuous/interval.js +4 -4
  101. package/src/actions/guides/legends/continuous/opacity.js +19 -12
  102. package/src/actions/guides/legends/edit.js +1 -604
  103. package/src/actions/guides/legends/focused.js +2 -2
  104. package/src/actions/guides/legends/group.js +1 -0
  105. package/src/actions/guides/legends/lane.js +1 -516
  106. package/src/actions/guides/legends/remove.js +3 -0
  107. package/src/actions/guides/legends/sampling.js +1 -1
  108. package/src/actions/guides/legends/size.js +51 -43
  109. package/src/actions/guides/legends/strokeWidth.js +11 -29
  110. package/src/actions/guides/legends/target.js +3 -3
  111. package/src/actions/guides/legends/transition.js +21 -7
  112. package/src/actions/guides/polar/axes/labels.js +4 -2
  113. package/src/actions/guides/polar/axes/titles.js +7 -5
  114. package/src/actions/guides/polar/resolve.js +6 -14
  115. package/src/actions/index.js +16 -16
  116. package/src/actions/marks/arc/actions.js +3 -10
  117. package/src/actions/marks/area/actions.js +8 -5
  118. package/src/actions/marks/area/materialize.js +8 -9
  119. package/src/actions/marks/bar/create.js +17 -4
  120. package/src/actions/marks/bar/edit.js +18 -7
  121. package/src/actions/marks/bar/materialize.js +9 -6
  122. package/src/actions/marks/line/actions.js +11 -4
  123. package/src/actions/marks/line/materialize.js +9 -5
  124. package/src/actions/marks/point/create.js +8 -1
  125. package/src/actions/marks/point/edit.js +13 -6
  126. package/src/actions/marks/point/materialize.js +4 -2
  127. package/src/actions/marks/rect/actions.js +26 -11
  128. package/src/actions/marks/remove.js +2 -0
  129. package/src/actions/marks/rule/actions.js +31 -12
  130. package/src/actions/marks/text/actions.js +42 -11
  131. package/src/actions/marks/text/layout.js +3 -0
  132. package/src/actions/marks/tick/actions.js +23 -8
  133. package/src/actions/primitives/semanticAction.js +37 -3
  134. package/src/actions/primitives/semanticValidation/dataset.js +13 -1
  135. package/src/actions/primitives/semanticValidation/layer.js +26 -2
  136. package/src/actions/primitives/semanticValidation/scale.js +6 -0
  137. package/src/actions/regression/create.js +26 -5
  138. package/src/actions/regression/edit.js +50 -15
  139. package/src/actions/regression/follow.js +22 -0
  140. package/src/actions/regression/resolve.js +2 -6
  141. package/src/actions/scales/channels.js +20 -15
  142. package/src/actions/scales/consumers/common.js +2 -1
  143. package/src/actions/scales/consumers/families.js +16 -5
  144. package/src/actions/scales/create.js +10 -4
  145. package/src/actions/scales/definitions.js +1 -425
  146. package/src/actions/scales/edit.js +3 -3
  147. package/src/actions/scales/editPolicy.js +9 -3
  148. package/src/actions/scales/materialize.js +1 -1
  149. package/src/actions/scales/parallel.js +1 -1
  150. package/src/actions/scales/patch.js +1 -1
  151. package/src/actions/scales/preview.js +49 -2
  152. package/src/actions/scales/quantitativeColor.js +3 -3
  153. package/src/actions/selection/actions.js +1 -13
  154. package/src/actions/theme/reconcile.js +83 -27
  155. package/src/actions/theme/state.js +12 -0
  156. package/src/actions/titles/actions.js +5 -18
  157. package/src/actions/titles/resolve.js +41 -19
  158. package/src/actions/violinPlots/edit.js +3 -20
  159. package/src/core/action.js +3 -2
  160. package/src/core/compositionState.js +4 -1
  161. package/src/core/font.js +9 -2
  162. package/src/core/optionRequirements.js +1 -0
  163. package/src/core/programState.js +5 -1
  164. package/src/core/resourceReferences.js +3 -0
  165. package/src/core/textMetricProfile.js +3 -2
  166. package/src/core/textMetrics.js +41 -4
  167. package/src/core/validation.js +26 -0
  168. package/src/core/vocabulary.js +4 -0
  169. package/src/grammar/aggregate.js +4 -18
  170. package/src/grammar/arcs.js +1 -386
  171. package/src/grammar/areaPaint.js +10 -0
  172. package/src/grammar/areaSeries.js +18 -10
  173. package/src/grammar/bars/geometry.js +30 -3
  174. package/src/grammar/bars/policy.js +12 -1
  175. package/src/grammar/bin.js +41 -22
  176. package/src/grammar/bin2d.js +1 -6
  177. package/src/grammar/boxPlot.js +56 -9
  178. package/src/grammar/categoryOrder.js +1 -6
  179. package/src/grammar/complete.js +15 -19
  180. package/src/grammar/computed.js +92 -25
  181. package/src/grammar/curveCommands.js +29 -16
  182. package/src/grammar/datasetSchema.js +382 -0
  183. package/src/grammar/density.js +53 -5
  184. package/src/grammar/displayLabels.js +28 -0
  185. package/src/grammar/ecdf.js +2 -9
  186. package/src/grammar/facets/dependencies.js +6 -2
  187. package/src/grammar/facets/guides.js +5 -0
  188. package/src/grammar/facets/index.js +2 -5
  189. package/src/grammar/filter.js +133 -39
  190. package/src/grammar/fold.js +2 -12
  191. package/src/grammar/gradientProfile.js +1 -6
  192. package/src/grammar/impute.js +21 -48
  193. package/src/grammar/interval.js +34 -10
  194. package/src/grammar/itemMissing.js +59 -0
  195. package/src/grammar/legendGradient.js +18 -0
  196. package/src/grammar/legendGroups.js +1 -0
  197. package/src/grammar/legendTitleStyle.js +22 -0
  198. package/src/grammar/lineSeries.js +32 -17
  199. package/src/grammar/markLabels.js +5 -2
  200. package/src/grammar/normalize.js +4 -54
  201. package/src/grammar/numeric.js +1 -290
  202. package/src/grammar/parallelCoordinates.js +1 -6
  203. package/src/grammar/parseTime.js +41 -0
  204. package/src/grammar/pathCommands.js +11 -4
  205. package/src/grammar/polar.js +1 -184
  206. package/src/grammar/positionCompatibility.js +3 -3
  207. package/src/grammar/regression/derive.js +63 -16
  208. package/src/grammar/regression/index.js +1 -0
  209. package/src/grammar/regression/models.js +116 -59
  210. package/src/grammar/regression/parameters.js +69 -6
  211. package/src/grammar/roundedRect.js +42 -54
  212. package/src/grammar/scales/appearance.js +1 -206
  213. package/src/grammar/scales/color.js +1 -382
  214. package/src/grammar/scales/colorConsumers.js +5 -2
  215. package/src/grammar/scales/definition.js +1 -190
  216. package/src/grammar/scales/discretized.js +1 -195
  217. package/src/grammar/scales/size.js +1 -456
  218. package/src/grammar/scales/temporal.js +2 -13
  219. package/src/grammar/scales/transformed.js +1 -383
  220. package/src/grammar/scales/types.js +1 -1
  221. package/src/grammar/scales/validation.js +1 -66
  222. package/src/grammar/schemas/concreteGraphic.js +11 -0
  223. package/src/grammar/schemas/graphic.js +3 -0
  224. package/src/grammar/schemas/semanticPath.js +5 -3
  225. package/src/grammar/sort.js +88 -0
  226. package/src/grammar/stack.js +2 -10
  227. package/src/grammar/statisticalReference.js +1 -6
  228. package/src/grammar/summary.js +120 -45
  229. package/src/grammar/text.js +19 -0
  230. package/src/grammar/timeUnit.js +16 -7
  231. package/src/grammar/transformKeys.js +47 -0
  232. package/src/grammar/transformTopology.js +1 -0
  233. package/src/grammar/transforms.js +25 -12
  234. package/src/grammar/valueFormat.js +17 -5
  235. package/src/grammar/weightedStatistics.js +1 -6
  236. package/src/grammar/window.js +4 -58
  237. package/src/inspection.js +495 -0
  238. package/src/inspectionDescriptors.js +3 -0
  239. package/src/layout/canvas.js +1 -110
  240. package/src/layout/facets.js +1 -244
  241. package/src/layout/labels.js +1 -0
  242. package/src/layout/legend.js +13 -0
  243. package/src/layout/legendGroup.js +1 -0
  244. package/src/layout/legendItems.js +59 -15
  245. package/src/layout/legendLane.js +1 -441
  246. package/src/layout/legendText.js +1 -0
  247. package/src/layout/title.js +35 -13
  248. package/src/materialization/bars/aggregate.js +11 -5
  249. package/src/materialization/bars/histogram.js +4 -1
  250. package/src/materialization/bars/ranged.js +19 -6
  251. package/src/materialization/bars/resolve.js +26 -3
  252. package/src/materialization/coordinateBounds.js +8 -0
  253. package/src/materialization/dependencies.js +4 -44
  254. package/src/materialization/facetGuides/layout.js +1 -0
  255. package/src/materialization/facetGuides/placement.js +19 -8
  256. package/src/materialization/facetGuides/preparation.js +15 -7
  257. package/src/materialization/facetHeaders.js +1 -347
  258. package/src/materialization/facets.js +1 -185
  259. package/src/materialization/graphicHierarchy.js +1 -1
  260. package/src/materialization/guides/layout.js +1 -57
  261. package/src/materialization/guides/resources.js +1 -123
  262. package/src/materialization/layout.js +53 -0
  263. package/src/materialization/legendGroups.js +1 -0
  264. package/src/materialization/legendText.js +1 -0
  265. package/src/materialization/legends.js +1 -1
  266. package/src/materialization/marks/capabilities.js +5 -6
  267. package/src/materialization/rect.js +1 -1
  268. package/src/materialization/revisionIdentity.js +19 -0
  269. package/src/materialization/scaleGuideDependencies.js +1 -1
  270. package/src/materialization/scales/map.js +3 -0
  271. package/src/materialization/scales/policies/arc.js +8 -7
  272. package/src/materialization/scales/policies/bar.js +19 -7
  273. package/src/materialization/scales/policies/series.js +2 -1
  274. package/src/materialization/scales/resolve.js +72 -7
  275. package/src/materialization/selection/items/arc.js +1 -1
  276. package/src/materialization/selection/items/bar.js +7 -6
  277. package/src/materialization/selection/items/path.js +7 -6
  278. package/src/materialization/selection/items/rect.js +2 -2
  279. package/src/materialization/text.js +17 -6
  280. package/src/materialization/textLines.js +15 -0
  281. package/src/persistence/codec.js +37 -6
  282. package/src/persistence/validation.js +47 -0
  283. package/src/persistence.js +94 -8
  284. package/src/renderers/canvas/circle.js +6 -3
  285. package/src/renderers/canvas/fill.js +7 -1
  286. package/src/renderers/canvas/index.js +2 -1
  287. package/src/renderers/canvas/line.js +1 -0
  288. package/src/renderers/canvas/path.js +1 -1
  289. package/src/renderers/canvas/rect.js +11 -8
  290. package/src/renderers/canvas/stroke.js +2 -1
  291. package/src/renderers/canvas/text.js +5 -3
  292. package/src/renderers/svg.js +4 -1
  293. package/src/selectors/layers.js +19 -0
  294. package/src/theme/defaults.js +8 -2
  295. package/src/version.js +1 -1
  296. package/types/basic.d.ts +1 -0
  297. package/types/index.d.ts +7 -0
  298. package/types/inspection.d.ts +17 -0
  299. package/types/program.d.ts +494 -112
@@ -1,5 +1,5 @@
1
1
  import { cloneAndFreeze } from "../../core/immutable.js";
2
- import { utcTimestamp, validTimestamp } from "../timeUnit.js";
2
+ import { utcTimestamp, validTimestamp, offsetUtcTimeUnit } from "../timeUnit.js";
3
3
 
4
4
  const TIME_UNITS = [
5
5
  { span: 2 * 365 * 24 * 60 * 60 * 1000, unit: "year" },
@@ -56,17 +56,6 @@ function floorUtc(timestamp, unit) {
56
56
  return Math.floor(timestamp / 1000) * 1000;
57
57
  }
58
58
 
59
- function offsetUtc(timestamp, unit) {
60
- const date = new Date(timestamp);
61
- if (unit === "year") return utcTimestamp(date.getUTCFullYear() + 1);
62
- if (unit === "month") {
63
- return utcTimestamp(date.getUTCFullYear(), date.getUTCMonth() + 1);
64
- }
65
- if (unit === "day") return timestamp + 24 * 60 * 60 * 1000;
66
- if (unit === "hour") return timestamp + 60 * 60 * 1000;
67
- if (unit === "minute") return timestamp + 60 * 1000;
68
- return timestamp + 1000;
69
- }
70
59
 
71
60
  export function niceTimeDomain(domain) {
72
61
  const [minimum, maximum] = requireTimeDomain(domain);
@@ -74,7 +63,7 @@ export function niceTimeDomain(domain) {
74
63
  const unit = TIME_UNITS.find(item => maximum - minimum >= item.span).unit;
75
64
  const start = floorUtc(minimum, unit);
76
65
  const endFloor = floorUtc(maximum, unit);
77
- const end = endFloor === maximum ? maximum : offsetUtc(endFloor, unit);
66
+ const end = endFloor === maximum ? maximum : offsetUtcTimeUnit(endFloor, unit);
78
67
  if (![start, end].every(validTimestamp)) {
79
68
  throw new RangeError("Nice time domain exceeds the supported date range.");
80
69
  }
@@ -1,383 +1 @@
1
- import { cloneAndFreeze, isPlainObject } from "../../core/immutable.js";
2
- import { MAX_GENERATED_ITEMS } from "../../core/validation.js";
3
- import { interpolateNumber, inverseLerp, numericExtent } from "../numeric.js";
4
- import { niceTicks } from "../ticks.js";
5
- import { niceLinearDomain } from "./continuous.js";
6
- import {
7
- SCALE_ROLES,
8
- validateScaleTypeForRole
9
- } from "./types.js";
10
- import { validatePair } from "./validation.js";
11
- const TRANSFORMED_TYPES = Object.freeze(["log", "pow", "sqrt", "symlog"]);
12
-
13
- function positiveFinite(value, label) {
14
- if (!Number.isFinite(value) || value <= 0) {
15
- throw new RangeError(`${label} must be a positive finite number.`);
16
- }
17
- return value;
18
- }
19
-
20
- export function normalizeTransformParameters(type, options = {}) {
21
- if (!TRANSFORMED_TYPES.includes(type) && type !== "linear") {
22
- throw new Error(`Scale type "${type}" is not a quantitative transform.`);
23
- }
24
- if (!isPlainObject(options)) {
25
- throw new TypeError("Scale transform options must be a plain object.");
26
- }
27
- const allowed = type === "log"
28
- ? ["base"]
29
- : type === "pow"
30
- ? ["exponent"]
31
- : type === "symlog" ? ["constant"] : [];
32
- for (const key of Object.keys(options)) {
33
- if (!allowed.includes(key)) {
34
- throw new Error(`Scale type "${type}" does not support ${key}.`);
35
- }
36
- }
37
- if (type === "log") {
38
- const base = positiveFinite(options.base ?? 10, "Log scale base");
39
- if (base === 1) throw new RangeError("Log scale base must not equal 1.");
40
- return cloneAndFreeze({ base });
41
- }
42
- if (type === "pow") {
43
- return cloneAndFreeze({
44
- exponent: positiveFinite(options.exponent ?? 1, "Power scale exponent")
45
- });
46
- }
47
- if (type === "sqrt") return cloneAndFreeze({ exponent: 0.5 });
48
- if (type === "symlog") {
49
- return cloneAndFreeze({
50
- constant: positiveFinite(options.constant ?? 1, "Symlog scale constant")
51
- });
52
- }
53
- return cloneAndFreeze({});
54
- }
55
-
56
- function signedTransformProportion(value, start, end, transform) {
57
- const transformed = [start, end, value].map(item =>
58
- Math.sign(item) * transform(Math.abs(item))
59
- );
60
- return transformed[0] !== transformed[1] &&
61
- transformed.every(Number.isFinite)
62
- ? inverseLerp(transformed[2], transformed[0], transformed[1])
63
- : undefined;
64
- }
65
-
66
- function powerProportion(value, domain, exponent) {
67
- const [start, end] = domain;
68
- const direct = signedTransformProportion(
69
- value, start, end, magnitude => magnitude ** exponent
70
- );
71
- if (direct !== undefined) return direct;
72
- const scale = Math.max(Math.abs(start), Math.abs(end));
73
- const normalized = signedTransformProportion(
74
- value,
75
- start,
76
- end,
77
- magnitude => magnitude === 0 ? 0 : Math.exp(exponent * (
78
- Math.log(magnitude) - Math.log(scale)
79
- ))
80
- );
81
- if (normalized !== undefined) return normalized;
82
-
83
- if (
84
- start !== 0 &&
85
- end !== 0 &&
86
- value !== 0 &&
87
- Math.sign(start) === Math.sign(end)
88
- ) {
89
- const endLogRatio = Math.log(Math.abs(end)) - Math.log(Math.abs(start));
90
- const valueLogRatio = Math.log(Math.abs(value)) - Math.log(Math.abs(start));
91
- if (Math.abs(exponent * endLogRatio) < 1e-8) {
92
- return valueLogRatio / endLogRatio;
93
- }
94
- const proportion = Math.expm1(exponent * valueLogRatio) /
95
- Math.expm1(exponent * endLogRatio);
96
- if (Number.isFinite(proportion)) return proportion;
97
- }
98
-
99
- return inverseLerp(value, start, end);
100
- }
101
-
102
- function logOnePlusRatio(value, constant) {
103
- if (value === 0) return 0;
104
- const ratio = Math.abs(value) / constant;
105
- if (Number.isFinite(ratio)) return Math.sign(value) * Math.log1p(ratio);
106
- const logRatio = Math.log(Math.abs(value)) - Math.log(constant);
107
- return Math.sign(value) * (
108
- Math.max(0, logRatio) + Math.log1p(Math.exp(-Math.abs(logRatio)))
109
- );
110
- }
111
-
112
- function transformedProportion(value, domain, type, parameters) {
113
- const [start, end] = domain;
114
- if (type === "linear") return inverseLerp(value, start, end);
115
- if (type === "log") {
116
- const logarithm = Math.log10(parameters.base);
117
- const direct = signedTransformProportion(
118
- value, start, end, magnitude => Math.log10(magnitude) / logarithm
119
- );
120
- return direct ?? signedTransformProportion(
121
- value, start, end, Math.log
122
- ) ?? inverseLerp(value, start, end);
123
- }
124
- if (type === "pow" || type === "sqrt") {
125
- return powerProportion(value, domain, parameters.exponent);
126
- }
127
-
128
- const transformedStart = logOnePlusRatio(start, parameters.constant);
129
- const transformedEnd = logOnePlusRatio(end, parameters.constant);
130
- if (transformedStart === transformedEnd) {
131
- return inverseLerp(value, start, end);
132
- }
133
- const proportion = inverseLerp(
134
- logOnePlusRatio(value, parameters.constant),
135
- transformedStart,
136
- transformedEnd
137
- );
138
- return Number.isFinite(proportion) ? proportion : inverseLerp(value, start, end);
139
- }
140
-
141
- export function validateTransformedDomain(type, domain, options = {}) {
142
- validateScaleTypeForRole(type, SCALE_ROLES.quantitativePosition);
143
- const validated = validatePair(domain, "Transformed scale domain");
144
- if (validated[0] === validated[1]) {
145
- throw new RangeError("Transformed scale domain values must be distinct.");
146
- }
147
- if (
148
- type === "log" &&
149
- (validated.includes(0) || Math.sign(validated[0]) !== Math.sign(validated[1]))
150
- ) {
151
- throw new RangeError(
152
- "Log scale domain must be strictly positive or strictly negative."
153
- );
154
- }
155
- normalizeTransformParameters(type, options);
156
- return validated;
157
- }
158
-
159
- function radixPower(radix, exponent, logarithm) {
160
- const direct = Number.isFinite(radix) ? radix ** exponent : Number.NaN;
161
- if (Number.isFinite(direct) && direct > 0) return direct;
162
- const transformed = exponent * logarithm;
163
- if (transformed > Math.log(Number.MAX_VALUE)) return Number.POSITIVE_INFINITY;
164
- if (transformed < Math.log(Number.MIN_VALUE)) return 0;
165
- return Math.exp(transformed);
166
- }
167
-
168
- function logDomainInfo(domain, base) {
169
- const low = Math.min(...domain);
170
- const high = Math.max(...domain);
171
- const negative = high < 0;
172
- return [
173
- base < 1 ? -Math.log(base) : Math.log(base),
174
- base < 1 ? 1 / base : base,
175
- domain[0] > domain[1],
176
- negative,
177
- negative ? Math.abs(high) : low,
178
- negative ? Math.abs(low) : high
179
- ];
180
- }
181
-
182
- function niceLogDomain(domain, base) {
183
- const [logarithm, radix, reversed, negative, minimum, maximum] =
184
- logDomainInfo(domain, base);
185
- let lower = radixPower(
186
- radix,
187
- Math.floor(Math.log(minimum) / logarithm),
188
- logarithm
189
- );
190
- let upper = radixPower(
191
- radix,
192
- Math.ceil(Math.log(maximum) / logarithm),
193
- logarithm
194
- );
195
- if (!Number.isFinite(lower) || lower <= 0 || lower > minimum) lower = minimum;
196
- if (!Number.isFinite(upper) || upper < maximum) upper = maximum;
197
- const result = negative ? [-upper, -lower] : [lower, upper];
198
- return cloneAndFreeze(reversed ? result.reverse() : result);
199
- }
200
-
201
- function padAutomaticTransformedDomain(type, value, options) {
202
- const parameters = normalizeTransformParameters(type, options);
203
- if (value === 0) {
204
- if (type === "log") {
205
- throw new RangeError(
206
- "Log scale domain must be strictly positive or strictly negative."
207
- );
208
- }
209
- return [-1, 1];
210
- }
211
- const magnitude = Math.abs(value);
212
- let lower = magnitude / 2;
213
- let upper = magnitude * 2;
214
- if (type === "pow" || type === "sqrt") {
215
- upper = magnitude * (2 - 2 ** -parameters.exponent) **
216
- (1 / parameters.exponent);
217
- } else if (type === "symlog") {
218
- const center = Math.abs(logOnePlusRatio(value, parameters.constant));
219
- if (center === 0) return [-1, 1];
220
- upper = parameters.constant * Math.expm1(
221
- 2 * center - logOnePlusRatio(lower, parameters.constant)
222
- );
223
- }
224
- if (!(lower < magnitude && upper > magnitude && Number.isFinite(upper))) {
225
- lower = magnitude / 2;
226
- upper = Number.isFinite(magnitude * 2) ? magnitude * 2 : magnitude;
227
- }
228
- if (type === "log" && lower === 0) lower = magnitude;
229
- return value > 0
230
- ? [lower, upper]
231
- : [-upper, lower === 0 ? 0 : -lower];
232
- }
233
-
234
- export function resolveTransformedDomain({
235
- type,
236
- domain = "auto",
237
- values,
238
- nice = false,
239
- zero = false,
240
- ...options
241
- }) {
242
- validateScaleTypeForRole(type, SCALE_ROLES.quantitativePosition);
243
- if (!Array.isArray(values) || !values.every(Number.isFinite)) {
244
- throw new TypeError("Transformed scale values must be finite numbers.");
245
- }
246
- if (typeof nice !== "boolean" || typeof zero !== "boolean") {
247
- throw new TypeError("Scale nice and zero must be booleans.");
248
- }
249
- if (type === "log" && zero) {
250
- throw new Error('Scale type "log" does not support zero.');
251
- }
252
- const explicit = domain !== "auto";
253
- if (explicit) return validateTransformedDomain(type, domain, options);
254
- if (values.length === 0) {
255
- throw new Error("Cannot infer an automatic scale domain from no values.");
256
- }
257
- let resolved = numericExtent(values);
258
- if (zero) resolved = [Math.min(0, resolved[0]), Math.max(0, resolved[1])];
259
- if (resolved[0] === resolved[1]) {
260
- resolved = padAutomaticTransformedDomain(type, resolved[0], options);
261
- }
262
- validateTransformedDomain(type, resolved, options);
263
- if (nice) {
264
- resolved = type === "log"
265
- ? niceLogDomain(resolved, normalizeTransformParameters(type, options).base)
266
- : niceLinearDomain(resolved);
267
- }
268
- return validateTransformedDomain(type, resolved, options);
269
- }
270
-
271
- export function resolveMappingRange(range, { reverse = false } = {}) {
272
- const validated = validatePair(range, "Scale range");
273
- if (typeof reverse !== "boolean") {
274
- throw new TypeError("Scale reverse must be a boolean.");
275
- }
276
- return reverse
277
- ? cloneAndFreeze([...validated].reverse())
278
- : validated;
279
- }
280
-
281
- export function mapTransformedValues(
282
- values,
283
- domain,
284
- range,
285
- { type = "linear", clamp = false, reverse = false, ...options } = {}
286
- ) {
287
- if (!Array.isArray(values)) {
288
- throw new TypeError("Scale values must be an array.");
289
- }
290
- if (typeof clamp !== "boolean") {
291
- throw new TypeError("Scale clamp must be a boolean.");
292
- }
293
- const hasUnknown = Object.hasOwn(options, "unknown");
294
- const { unknown, ...transformOptions } = options;
295
- const parameters = normalizeTransformParameters(type, transformOptions);
296
- const validatedDomain = validateTransformedDomain(type, domain, transformOptions);
297
- const resolvedRange = resolveMappingRange(range, { reverse });
298
- const span = resolvedRange[1] - resolvedRange[0];
299
- return cloneAndFreeze(values.map(value => {
300
- const valid = Number.isFinite(value) && !(type === "log" && (
301
- value === 0 || Math.sign(value) !== Math.sign(validatedDomain[0])
302
- ));
303
- if (!valid) {
304
- if (hasUnknown) return unknown;
305
- throw new TypeError(`Scale type "${type}" received an invalid value.`);
306
- }
307
- let proportion = transformedProportion(
308
- value,
309
- validatedDomain,
310
- type,
311
- parameters
312
- );
313
- if (clamp) proportion = Math.max(0, Math.min(1, proportion));
314
- const direct = resolvedRange[0] + proportion * span;
315
- return Number.isFinite(span) && Number.isFinite(direct) && (
316
- proportion !== 1 ||
317
- Math.abs(direct - resolvedRange[1]) <=
318
- Number.EPSILON * Math.abs(resolvedRange[1])
319
- )
320
- ? direct
321
- : interpolateNumber(resolvedRange[0], resolvedRange[1], proportion);
322
- }));
323
- }
324
-
325
- export function transformedTicks(type, domain, count, options = {}) {
326
- const validated = validateTransformedDomain(type, domain, options);
327
- if (!Number.isInteger(count) || count <= 0) {
328
- throw new RangeError("Tick count must be a positive integer.");
329
- }
330
- if (type !== "log") return niceTicks(validated, count);
331
- const { base } = normalizeTransformParameters(type, options);
332
- const [
333
- logarithm,
334
- radix,
335
- reversed,
336
- negative,
337
- minimumMagnitude,
338
- maximumMagnitude
339
- ] = logDomainInfo(validated, base);
340
- const start = Math.ceil(
341
- Math.log(minimumMagnitude) / logarithm - 1e-12
342
- );
343
- const end = Math.floor(
344
- Math.log(maximumMagnitude) / logarithm + 1e-12
345
- );
346
- const available = end - start + 1;
347
- const tickCount = available <= MAX_GENERATED_ITEMS
348
- ? available
349
- : Math.min(count, MAX_GENERATED_ITEMS);
350
- const values = [];
351
- for (let index = 0; index < tickCount; index += 1) {
352
- const exponent = available <= MAX_GENERATED_ITEMS
353
- ? start + index
354
- : Math.round(start + (end - start) * index / Math.max(1, tickCount - 1));
355
- const magnitude = radixPower(radix, exponent, logarithm);
356
- const value = (negative ? -1 : 1) * magnitude;
357
- if (
358
- Number.isFinite(value) &&
359
- magnitude >= minimumMagnitude &&
360
- magnitude <= maximumMagnitude &&
361
- (values.length === 0 || value !== values.at(-1))
362
- ) {
363
- values.push(value);
364
- }
365
- }
366
- if (negative) values.reverse();
367
- if (reversed) values.reverse();
368
- return cloneAndFreeze(values.length === 0 ? [...validated] : values);
369
- }
370
-
371
- export function formatTransformedTick(type, value) {
372
- if (type !== "log") return String(value);
373
- const magnitude = Math.abs(value);
374
- const units = [
375
- [1_000_000_000, "B"],
376
- [1_000_000, "M"],
377
- [1_000, "K"]
378
- ];
379
- const unit = units.find(([threshold]) => magnitude >= threshold);
380
- if (unit === undefined) return String(value);
381
- const scaled = value / unit[0];
382
- return `${Number(scaled.toPrecision(3))}${unit[1]}`;
383
- }
1
+ import{cloneAndFreeze as h,isPlainObject as I}from"../../core/immutable.js";import{MAX_GENERATED_ITEMS as R}from"../../core/validation.js";import{interpolateNumber as D,inverseLerp as b,numericExtent as q}from"../numeric.js";import{niceTicks as $}from"../ticks.js";import{niceLinearDomain as j}from"./continuous.js";import{SCALE_ROLES as L,validateScaleTypeForRole as A}from"./types.js";import{validatePair as _}from"./validation.js";const z=Object.freeze(["log","pow","sqrt","symlog"]);function v(t,e){if(!Number.isFinite(t)||t<=0)throw new RangeError(`${e} must be a positive finite number.`);return t}function p(t,e={}){if(!z.includes(t)&&t!=="linear")throw new Error(`Scale type "${t}" is not a quantitative transform.`);if(!I(e))throw new TypeError("Scale transform options must be a plain object.");const o=t==="log"?["base"]:t==="pow"?["exponent"]:t==="symlog"?["constant"]:[];for(const n of Object.keys(e))if(!o.includes(n))throw new Error(`Scale type "${t}" does not support ${n}.`);if(t==="log"){const n=v(e.base??10,"Log scale base");if(n===1)throw new RangeError("Log scale base must not equal 1.");return h({base:n})}return t==="pow"?h({exponent:v(e.exponent??1,"Power scale exponent")}):t==="sqrt"?h({exponent:.5}):t==="symlog"?h({constant:v(e.constant??1,"Symlog scale constant")}):h({})}function E(t,e,o,n){const r=[e,o,t].map(i=>Math.sign(i)*n(Math.abs(i)));return r[0]!==r[1]&&r.every(Number.isFinite)?b(r[2],r[0],r[1]):void 0}function V(t,e,o){const[n,r]=e,i=E(t,n,r,c=>c**o);if(i!==void 0)return i;const s=Math.max(Math.abs(n),Math.abs(r)),a=E(t,n,r,c=>c===0?0:Math.exp(o*(Math.log(c)-Math.log(s))));if(a!==void 0)return a;if(n!==0&&r!==0&&t!==0&&Math.sign(n)===Math.sign(r)){const c=Math.log(Math.abs(r))-Math.log(Math.abs(n)),f=Math.log(Math.abs(t))-Math.log(Math.abs(n));if(Math.abs(o*c)<1e-8)return f/c;const u=Math.expm1(o*f)/Math.expm1(o*c);if(Number.isFinite(u))return u}return b(t,n,r)}function N(t,e){if(t===0)return 0;const o=Math.abs(t)/e;if(Number.isFinite(o))return Math.sign(t)*Math.log1p(o);const n=Math.log(Math.abs(t))-Math.log(e);return Math.sign(t)*(Math.max(0,n)+Math.log1p(Math.exp(-Math.abs(n))))}function C(t,e,o,n){const[r,i]=e;if(o==="linear")return b(t,r,i);if(o==="log"){const f=Math.log10(n.base);return E(t,r,i,d=>Math.log10(d)/f)??E(t,r,i,Math.log)??b(t,r,i)}if(o==="pow"||o==="sqrt")return V(t,e,n.exponent);const s=N(r,n.constant),a=N(i,n.constant);if(s===a)return b(t,r,i);const c=b(N(t,n.constant),s,a);return Number.isFinite(c)?c:b(t,r,i)}function x(t,e,o={}){A(t,L.quantitativePosition);const n=_(e,"Transformed scale domain");if(n[0]===n[1])throw new RangeError("Transformed scale domain values must be distinct.");if(t==="log"&&(n.includes(0)||Math.sign(n[0])!==Math.sign(n[1])))throw new RangeError("Log scale domain must be strictly positive or strictly negative.");return p(t,o),n}function P(t,e,o){const n=Number.isFinite(t)?t**e:Number.NaN;if(Number.isFinite(n)&&n>0)return n;const r=e*o;return r>Math.log(Number.MAX_VALUE)?Number.POSITIVE_INFINITY:r<Math.log(Number.MIN_VALUE)?0:Math.exp(r)}function O(t,e){const o=Math.min(...t),n=Math.max(...t),r=n<0;return[e<1?-Math.log(e):Math.log(e),e<1?1/e:e,t[0]>t[1],r,r?Math.abs(n):o,r?Math.abs(o):n]}function U(t,e){const[o,n,r,i,s,a]=O(t,e);let c=P(n,Math.floor(Math.log(s)/o),o),f=P(n,Math.ceil(Math.log(a)/o),o);(!Number.isFinite(c)||c<=0||c>s)&&(c=s),(!Number.isFinite(f)||f<a)&&(f=a);const u=i?[-f,-c]:[c,f];return h(r?u.reverse():u)}function X(t,e,o){const n=p(t,o);if(e===0){if(t==="log")throw new RangeError("Log scale domain must be strictly positive or strictly negative.");return[-1,1]}const r=Math.abs(e);let i=r/2,s=r*2;if(t==="pow"||t==="sqrt")s=r*(2-2**-n.exponent)**(1/n.exponent);else if(t==="symlog"){const a=Math.abs(N(e,n.constant));if(a===0)return[-1,1];s=n.constant*Math.expm1(2*a-N(i,n.constant))}return i<r&&s>r&&Number.isFinite(s)||(i=r/2,s=Number.isFinite(r*2)?r*2:r),t==="log"&&i===0&&(i=r),e>0?[i,s]:[-s,i===0?0:-i]}function W({type:t,domain:e="auto",values:o,nice:n=!1,zero:r=!1,...i}){if(A(t,L.quantitativePosition),!Array.isArray(o)||!o.every(Number.isFinite))throw new TypeError("Transformed scale values must be finite numbers.");if(typeof n!="boolean"||typeof r!="boolean")throw new TypeError("Scale nice and zero must be booleans.");if(t==="log"&&r)throw new Error('Scale type "log" does not support zero.');if(e!=="auto")return x(t,e,i);if(o.length===0)throw new Error("Cannot infer an automatic scale domain from no values.");let a=q(o);return r&&(a=[Math.min(0,a[0]),Math.max(0,a[1])]),a[0]===a[1]&&(a=X(t,a[0],i)),x(t,a,i),n&&(a=t==="log"?U(a,p(t,i).base):j(a)),x(t,a,i)}function Y(t,{reverse:e=!1}={}){const o=_(t,"Scale range");if(typeof e!="boolean")throw new TypeError("Scale reverse must be a boolean.");return e?h([...o].reverse()):o}function Z(t,e,o,{type:n="linear",clamp:r=!1,reverse:i=!1,...s}={}){if(!Array.isArray(t))throw new TypeError("Scale values must be an array.");if(typeof r!="boolean")throw new TypeError("Scale clamp must be a boolean.");const a=Object.hasOwn(s,"unknown"),{unknown:c,...f}=s,u=p(n,f),d=x(n,e,f),l=Y(o,{reverse:i}),w=l[1]-l[0];return h(t.map(g=>{if(!(Number.isFinite(g)&&!(n==="log"&&(g===0||Math.sign(g)!==Math.sign(d[0]))))){if(a)return c;throw new TypeError(`Scale type "${n}" received an invalid value.`)}let m=C(g,d,n,u);r&&(m=Math.max(0,Math.min(1,m)));const M=l[0]+m*w;return Number.isFinite(w)&&Number.isFinite(M)&&(m!==1||Math.abs(M-l[1])<=Number.EPSILON*Math.abs(l[1]))?M:D(l[0],l[1],m)}))}function tt(t,e,o,n={}){const r=x(t,e,n);if(!Number.isInteger(o)||o<=0)throw new RangeError("Tick count must be a positive integer.");if(t!=="log")return $(r,o);const{base:i}=p(t,n),[s,a,c,f,u,d]=O(r,i),l=Math.ceil(Math.log(u)/s-1e-12),w=Math.floor(Math.log(d)/s+1e-12),g=w-l+1,T=g<=R?g:Math.min(o,R),m=[];for(let M=0;M<T;M+=1){const k=g<=R?l+M:Math.round(l+(w-l)*M/Math.max(1,T-1)),S=P(a,k,s),F=(f?-1:1)*S;Number.isFinite(F)&&S>=u&&S<=d&&(m.length===0||F!==m.at(-1))&&m.push(F)}return f&&m.reverse(),c&&m.reverse(),h(m.length===0?[...r]:m)}function nt(t,e){if(t!=="log")return String(e);const o=Math.abs(e),r=[[1e9,"B"],[1e6,"M"],[1e3,"K"]].find(([s])=>o>=s);if(r===void 0)return String(e);const i=e/r[0];return`${Number(i.toPrecision(3))}${r[1]}`}export{nt as formatTransformedTick,Z as mapTransformedValues,p as normalizeTransformParameters,Y as resolveMappingRange,W as resolveTransformedDomain,tt as transformedTicks,x as validateTransformedDomain};
@@ -16,7 +16,7 @@ export const SCALE_TYPES_BY_ROLE = Object.freeze({
16
16
  [SCALE_ROLES.bandPosition]: Object.freeze(["band"]),
17
17
  [SCALE_ROLES.pointPosition]: Object.freeze(["point"]),
18
18
  [SCALE_ROLES.discreteAppearance]: Object.freeze(["ordinal"]),
19
- [SCALE_ROLES.continuousColor]: Object.freeze(["sequential"]),
19
+ [SCALE_ROLES.continuousColor]: Object.freeze(["sequential", "log", "sqrt", "symlog"]),
20
20
  [SCALE_ROLES.discretizedColor]: Object.freeze([
21
21
  "quantize", "quantile", "threshold"
22
22
  ])
@@ -1,66 +1 @@
1
- import { cloneAndFreeze } from "../../core/immutable.js";
2
- import { POSITION_CHANNELS } from "../../core/vocabulary.js";
3
- import { validateCompleteScaleType } from "./types.js";
4
-
5
- export function validatePair(value, label) {
6
- if (
7
- !Array.isArray(value) ||
8
- value.length !== 2 ||
9
- !value.every(Number.isFinite)
10
- ) {
11
- throw new TypeError(`${label} must be "auto" or two finite numbers.`);
12
- }
13
- return cloneAndFreeze(value);
14
- }
15
-
16
- export function validatePositionChannel(channel) {
17
- if (!POSITION_CHANNELS.includes(channel)) {
18
- throw new Error(`Unknown position channel "${channel}".`);
19
- }
20
- return channel;
21
- }
22
-
23
- export function validateScaleType(type) {
24
- return validateCompleteScaleType(type);
25
- }
26
-
27
- export function validateSemanticScaleType(type) {
28
- return validateCompleteScaleType(type);
29
- }
30
-
31
- export function validateLinearScaleType(type) {
32
- if (type !== "linear") {
33
- throw new Error(`Unsupported position scale type "${type}".`);
34
- }
35
- return type;
36
- }
37
-
38
- export function validateTimeScaleType(type) {
39
- if (type !== "time") {
40
- throw new Error(`Unsupported temporal scale type "${type}".`);
41
- }
42
- return type;
43
- }
44
-
45
- export function validateOrdinalScaleType(type) {
46
- if (type !== "ordinal") {
47
- throw new Error(`Unsupported color scale type "${type}".`);
48
- }
49
- return type;
50
- }
51
-
52
- export function validateScaleDomain(domain) {
53
- return domain === "auto" ? domain : validatePair(domain, "Scale domain");
54
- }
55
-
56
- export function validateScaleRange(range) {
57
- return range === "auto" ? range : validatePair(range, "Scale range");
58
- }
59
-
60
- export function validateSemanticScaleDomain(domain) {
61
- if (domain === "auto") return domain;
62
- if (!Array.isArray(domain) || domain.length === 0) {
63
- throw new TypeError("Scale domain must be \"auto\" or a non-empty array.");
64
- }
65
- return cloneAndFreeze(domain);
66
- }
1
+ import{cloneAndFreeze as o}from"../../core/immutable.js";import{POSITION_CHANNELS as c}from"../../core/vocabulary.js";import{validateCompleteScaleType as i}from"./types.js";function u(r,e,t=1){if(!Array.isArray(r)||r.length<t||!r.every(Number.isFinite))throw new TypeError(`${e} must contain finite numbers.`);return r}function s(r,e){if(u(r,e),r.some((t,n)=>n>0&&t<=r[n-1]))throw new RangeError(`${e} must be strictly increasing.`);return r}function a(r,e){if(!Array.isArray(r)||r.length!==2||!r.every(Number.isFinite))throw new TypeError(`${e} must be "auto" or two finite numbers.`);return o(r)}function m(r){if(!c.includes(r))throw new Error(`Unknown position channel "${r}".`);return r}function y(r){return i(r)}function w(r){return i(r)}function S(r){if(r!=="linear")throw new Error(`Unsupported position scale type "${r}".`);return r}function d(r){if(r!=="time")throw new Error(`Unsupported temporal scale type "${r}".`);return r}function h(r){if(r!=="ordinal")throw new Error(`Unsupported color scale type "${r}".`);return r}function x(r){return r==="auto"?r:a(r,"Scale domain")}function T(r){return r==="auto"?r:a(r,"Scale range")}function A(r){if(r==="auto")return r;if(!Array.isArray(r)||r.length===0)throw new TypeError('Scale domain must be "auto" or a non-empty array.');return o(r)}export{u as validateFiniteScaleArray,s as validateIncreasingScaleArray,S as validateLinearScaleType,h as validateOrdinalScaleType,a as validatePair,m as validatePositionChannel,x as validateScaleDomain,T as validateScaleRange,y as validateScaleType,A as validateSemanticScaleDomain,w as validateSemanticScaleType,d as validateTimeScaleType};
@@ -1,3 +1,5 @@
1
+ import { validateGeneratedItemLimit } from "../../core/validation.js";
2
+ import { validateFontStyle } from "../../core/font.js";
1
3
  import { isPlainObject } from "../../core/immutable.js";
2
4
  import { validatePathCommands } from "../pathCommands.js";
3
5
  import { validateFillPaint } from "../paint.js";
@@ -25,6 +27,14 @@ const TEXT_BASELINES = new Set([
25
27
  ]);
26
28
 
27
29
  export function validateConcreteGraphicValue(type, property, value) {
30
+ if (property === "lines") {
31
+ if (!Array.isArray(value) || value.length === 0 || value.some(line =>
32
+ !isPlainObject(line) || Object.keys(line).some(key => !["text", "x", "y"].includes(key)) ||
33
+ typeof line.text !== "string" || !Number.isFinite(line.x) || !Number.isFinite(line.y))) {
34
+ throw new TypeError("Text lines require non-empty resolved text/x/y records.");
35
+ }
36
+ validateGeneratedItemLimit(value.length, "Text line count");
37
+ }
28
38
  if (property === "fill") {
29
39
  if (typeof value === "string") {
30
40
  validateFillPaint(value, `${type}.fill`);
@@ -60,6 +70,7 @@ export function validateConcreteGraphicValue(type, property, value) {
60
70
  )) {
61
71
  throw new TypeError("text.fontWeight must be a non-empty string or finite number.");
62
72
  }
73
+ if (property === "fontStyle") validateFontStyle(value);
63
74
  if (property === "textAlign" && !TEXT_ALIGNS.has(value)) {
64
75
  throw new Error(`Unsupported text.textAlign "${value}".`);
65
76
  }
@@ -33,6 +33,7 @@ const GRAPHIC_PROPERTIES = Object.freeze({
33
33
  "fontSize",
34
34
  "fontFamily",
35
35
  "fontWeight",
36
+ "fontStyle",
36
37
  "text",
37
38
  "textAlign",
38
39
  "textBaseline",
@@ -85,10 +86,12 @@ const GRAPHIC_PROPERTIES = Object.freeze({
85
86
  "x",
86
87
  "y",
87
88
  "text",
89
+ "lines",
88
90
  "fill",
89
91
  "fontSize",
90
92
  "fontFamily",
91
93
  "fontWeight",
94
+ "fontStyle",
92
95
  "textAlign",
93
96
  "textBaseline",
94
97
  "rotation",
@@ -23,7 +23,7 @@ const ENCODING_PATHS = Object.freeze([
23
23
  "encoding.color.aggregate",
24
24
  "encoding.group.fields",
25
25
  "encoding.group.inferredFrom",
26
- ...["x", "y", "x2", "y2", "theta", "color"].map(channel =>
26
+ ...["x", "y", "x2", "y2", "theta", "color", "stroke"].map(channel =>
27
27
  `encoding.${channel}.temporalUnit`
28
28
  ),
29
29
  "encoding.pathOrder.field",
@@ -49,7 +49,7 @@ const ENCODING_PATHS = Object.freeze([
49
49
  const ENTITY_PATHS = Object.freeze({
50
50
  dataset: {
51
51
  collection: "datasets",
52
- properties: new Set(["source", "transform", "values"])
52
+ properties: new Set(["source", "transform", "schema", "values"])
53
53
  },
54
54
  layer: {
55
55
  collection: "layers",
@@ -64,6 +64,8 @@ const ENTITY_PATHS = Object.freeze({
64
64
  "transform",
65
65
  "mark.type",
66
66
  "mark.missing",
67
+ "mark.orientation",
68
+ "derivedBindings.regression",
67
69
  "layout.mode",
68
70
  ...ENCODING_PATHS
69
71
  ])
@@ -71,7 +73,7 @@ const ENTITY_PATHS = Object.freeze({
71
73
  scale: {
72
74
  collection: "scales",
73
75
  properties: new Set([
74
- "type", "domain", "range", "nice", "zero", "clamp", "reverse",
76
+ "type", "domain", "range", "emptyDomain", "nice", "zero", "clamp", "reverse",
75
77
  "unknown", "base", "exponent", "constant", "interpolate", "radialMapping", "midpoint",
76
78
  "paddingInner", "paddingOuter", "padding", "align"
77
79
  ])