ggaction 0.0.17 → 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 (263) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +2 -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 +3 -3
  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/box.js +2 -1
  34. package/src/actions/data/edit.js +1 -1
  35. package/src/actions/data/regression.js +2 -1
  36. package/src/actions/data/shared.js +3 -2
  37. package/src/actions/data/stack.js +3 -1
  38. package/src/actions/encodings/appearance.js +37 -29
  39. package/src/actions/encodings/barWidth.js +36 -11
  40. package/src/actions/encodings/channels.js +0 -1
  41. package/src/actions/encodings/color/continuous.js +1 -1
  42. package/src/actions/encodings/color/index.js +1 -1
  43. package/src/actions/encodings/color/policy.js +10 -3
  44. package/src/actions/encodings/density/resolve.js +1 -6
  45. package/src/actions/encodings/position/policies/arc.js +12 -10
  46. package/src/actions/encodings/position/policies/area.js +3 -3
  47. package/src/actions/encodings/position/policies/bar.js +15 -6
  48. package/src/actions/encodings/position/policies/line.js +17 -6
  49. package/src/actions/encodings/position/resolve.js +3 -2
  50. package/src/actions/encodings/ranged.js +17 -0
  51. package/src/actions/encodings/remove.js +3 -2
  52. package/src/actions/encodings/seriesLayout.js +1 -0
  53. package/src/actions/encodings/text.js +6 -3
  54. package/src/actions/errorBands/create.js +2 -2
  55. package/src/actions/errorBands/edit.js +2 -1
  56. package/src/actions/facets/actions.js +158 -240
  57. package/src/actions/gradientPlots/resolve.js +2 -17
  58. package/src/actions/guides/applicability.js +1 -1
  59. package/src/actions/guides/axes/axis.js +16 -4
  60. package/src/actions/guides/axes/edit.js +4 -4
  61. package/src/actions/guides/axes/labels.js +120 -46
  62. package/src/actions/guides/axes/lines.js +6 -5
  63. package/src/actions/guides/axes/parallel/lifecycle.js +1 -1
  64. package/src/actions/guides/axes/parallel/policy.js +8 -7
  65. package/src/actions/guides/axes/remove.js +3 -2
  66. package/src/actions/guides/axes/tickGroups.js +5 -2
  67. package/src/actions/guides/axes/ticks.js +6 -3
  68. package/src/actions/guides/axes/titles.js +71 -26
  69. package/src/actions/guides/basic.js +2 -0
  70. package/src/actions/guides/facadeLegend.js +10 -2
  71. package/src/actions/guides/grids/grid.js +7 -1
  72. package/src/actions/guides/grids/resolve.js +59 -12
  73. package/src/actions/guides/legends/blocks.js +3 -1
  74. package/src/actions/guides/legends/categorical/actions.js +70 -17
  75. package/src/actions/guides/legends/categorical/components.js +12 -8
  76. package/src/actions/guides/legends/categorical/index.js +6 -0
  77. package/src/actions/guides/legends/categorical/layout.js +37 -8
  78. package/src/actions/guides/legends/categorical/options.js +31 -20
  79. package/src/actions/guides/legends/categorical/resolve.js +2 -2
  80. package/src/actions/guides/legends/categorical/symbols.js +18 -12
  81. package/src/actions/guides/legends/categorical/validation.js +1 -6
  82. package/src/actions/guides/legends/continuous/colorSymbols.js +1 -0
  83. package/src/actions/guides/legends/continuous/common.js +1 -447
  84. package/src/actions/guides/legends/continuous/gradient.js +47 -34
  85. package/src/actions/guides/legends/continuous/interval.js +4 -4
  86. package/src/actions/guides/legends/continuous/opacity.js +19 -12
  87. package/src/actions/guides/legends/edit.js +1 -604
  88. package/src/actions/guides/legends/focused.js +2 -2
  89. package/src/actions/guides/legends/group.js +1 -0
  90. package/src/actions/guides/legends/lane.js +1 -516
  91. package/src/actions/guides/legends/remove.js +3 -0
  92. package/src/actions/guides/legends/sampling.js +1 -1
  93. package/src/actions/guides/legends/size.js +51 -43
  94. package/src/actions/guides/legends/strokeWidth.js +11 -29
  95. package/src/actions/guides/legends/target.js +3 -3
  96. package/src/actions/guides/legends/transition.js +21 -7
  97. package/src/actions/guides/polar/axes/labels.js +4 -2
  98. package/src/actions/guides/polar/axes/titles.js +7 -5
  99. package/src/actions/guides/polar/resolve.js +6 -14
  100. package/src/actions/index.js +16 -16
  101. package/src/actions/marks/arc/actions.js +3 -10
  102. package/src/actions/marks/area/actions.js +8 -5
  103. package/src/actions/marks/area/materialize.js +8 -9
  104. package/src/actions/marks/bar/create.js +10 -4
  105. package/src/actions/marks/bar/edit.js +8 -4
  106. package/src/actions/marks/bar/materialize.js +9 -6
  107. package/src/actions/marks/line/actions.js +11 -4
  108. package/src/actions/marks/line/materialize.js +9 -5
  109. package/src/actions/marks/point/edit.js +1 -1
  110. package/src/actions/marks/rect/actions.js +3 -3
  111. package/src/actions/marks/remove.js +2 -0
  112. package/src/actions/marks/rule/actions.js +7 -4
  113. package/src/actions/marks/text/actions.js +17 -5
  114. package/src/actions/marks/text/layout.js +3 -0
  115. package/src/actions/primitives/semanticValidation/layer.js +4 -0
  116. package/src/actions/regression/create.js +1 -1
  117. package/src/actions/regression/edit.js +3 -3
  118. package/src/actions/regression/resolve.js +2 -6
  119. package/src/actions/scales/channels.js +9 -4
  120. package/src/actions/scales/consumers/families.js +16 -5
  121. package/src/actions/scales/create.js +8 -3
  122. package/src/actions/scales/definitions.js +1 -425
  123. package/src/actions/scales/edit.js +2 -2
  124. package/src/actions/scales/editPolicy.js +9 -3
  125. package/src/actions/scales/materialize.js +1 -1
  126. package/src/actions/scales/preview.js +21 -1
  127. package/src/actions/scales/quantitativeColor.js +3 -3
  128. package/src/actions/selection/actions.js +1 -13
  129. package/src/actions/theme/reconcile.js +81 -27
  130. package/src/actions/theme/state.js +12 -0
  131. package/src/actions/titles/actions.js +5 -18
  132. package/src/actions/titles/resolve.js +41 -19
  133. package/src/actions/violinPlots/edit.js +3 -20
  134. package/src/core/compositionState.js +4 -1
  135. package/src/core/font.js +9 -2
  136. package/src/core/optionRequirements.js +1 -0
  137. package/src/core/resourceReferences.js +3 -0
  138. package/src/core/textMetricProfile.js +3 -2
  139. package/src/core/textMetrics.js +41 -4
  140. package/src/core/validation.js +26 -0
  141. package/src/core/vocabulary.js +4 -0
  142. package/src/grammar/aggregate.js +4 -18
  143. package/src/grammar/arcs.js +1 -386
  144. package/src/grammar/areaPaint.js +10 -0
  145. package/src/grammar/areaSeries.js +18 -10
  146. package/src/grammar/bars/geometry.js +30 -3
  147. package/src/grammar/bars/policy.js +12 -1
  148. package/src/grammar/bin.js +2 -12
  149. package/src/grammar/bin2d.js +1 -6
  150. package/src/grammar/boxPlot.js +56 -9
  151. package/src/grammar/categoryOrder.js +1 -6
  152. package/src/grammar/complete.js +15 -19
  153. package/src/grammar/computed.js +92 -25
  154. package/src/grammar/curveCommands.js +29 -16
  155. package/src/grammar/datasetSchema.js +3 -7
  156. package/src/grammar/displayLabels.js +28 -0
  157. package/src/grammar/ecdf.js +2 -9
  158. package/src/grammar/facets/dependencies.js +6 -2
  159. package/src/grammar/facets/guides.js +5 -0
  160. package/src/grammar/facets/index.js +2 -5
  161. package/src/grammar/fold.js +2 -12
  162. package/src/grammar/gradientProfile.js +1 -6
  163. package/src/grammar/impute.js +21 -48
  164. package/src/grammar/interval.js +2 -9
  165. package/src/grammar/legendGradient.js +18 -0
  166. package/src/grammar/legendGroups.js +1 -0
  167. package/src/grammar/legendTitleStyle.js +22 -0
  168. package/src/grammar/lineSeries.js +32 -17
  169. package/src/grammar/markLabels.js +5 -2
  170. package/src/grammar/normalize.js +4 -54
  171. package/src/grammar/numeric.js +1 -290
  172. package/src/grammar/parallelCoordinates.js +1 -6
  173. package/src/grammar/parseTime.js +41 -0
  174. package/src/grammar/pathCommands.js +11 -4
  175. package/src/grammar/polar.js +1 -184
  176. package/src/grammar/positionCompatibility.js +3 -3
  177. package/src/grammar/regression/derive.js +7 -1
  178. package/src/grammar/regression/models.js +83 -55
  179. package/src/grammar/regression/parameters.js +14 -3
  180. package/src/grammar/roundedRect.js +42 -54
  181. package/src/grammar/scales/appearance.js +1 -206
  182. package/src/grammar/scales/color.js +1 -382
  183. package/src/grammar/scales/colorConsumers.js +5 -2
  184. package/src/grammar/scales/definition.js +1 -197
  185. package/src/grammar/scales/discretized.js +1 -195
  186. package/src/grammar/scales/size.js +1 -456
  187. package/src/grammar/scales/temporal.js +2 -13
  188. package/src/grammar/scales/transformed.js +1 -383
  189. package/src/grammar/scales/types.js +1 -1
  190. package/src/grammar/scales/validation.js +1 -66
  191. package/src/grammar/schemas/concreteGraphic.js +11 -0
  192. package/src/grammar/schemas/graphic.js +3 -0
  193. package/src/grammar/schemas/semanticPath.js +2 -1
  194. package/src/grammar/stack.js +2 -10
  195. package/src/grammar/statisticalReference.js +1 -6
  196. package/src/grammar/summary.js +3 -26
  197. package/src/grammar/text.js +19 -0
  198. package/src/grammar/timeUnit.js +16 -7
  199. package/src/grammar/transformKeys.js +47 -0
  200. package/src/grammar/transforms.js +4 -1
  201. package/src/grammar/valueFormat.js +17 -5
  202. package/src/grammar/weightedStatistics.js +1 -6
  203. package/src/grammar/window.js +4 -58
  204. package/src/inspectionDescriptors.js +1 -1
  205. package/src/layout/canvas.js +1 -110
  206. package/src/layout/facets.js +1 -244
  207. package/src/layout/labels.js +1 -0
  208. package/src/layout/legend.js +13 -0
  209. package/src/layout/legendGroup.js +1 -0
  210. package/src/layout/legendItems.js +59 -15
  211. package/src/layout/legendLane.js +1 -441
  212. package/src/layout/legendText.js +1 -0
  213. package/src/layout/title.js +35 -13
  214. package/src/materialization/bars/aggregate.js +11 -5
  215. package/src/materialization/bars/histogram.js +4 -1
  216. package/src/materialization/bars/ranged.js +19 -6
  217. package/src/materialization/bars/resolve.js +22 -1
  218. package/src/materialization/coordinateBounds.js +8 -0
  219. package/src/materialization/dependencies.js +4 -44
  220. package/src/materialization/facetGuides/layout.js +1 -0
  221. package/src/materialization/facetGuides/placement.js +19 -8
  222. package/src/materialization/facetGuides/preparation.js +15 -7
  223. package/src/materialization/facetHeaders.js +1 -347
  224. package/src/materialization/facets.js +1 -185
  225. package/src/materialization/graphicHierarchy.js +1 -1
  226. package/src/materialization/guides/layout.js +1 -57
  227. package/src/materialization/guides/resources.js +1 -123
  228. package/src/materialization/layout.js +53 -0
  229. package/src/materialization/legendGroups.js +1 -0
  230. package/src/materialization/legendText.js +1 -0
  231. package/src/materialization/legends.js +1 -1
  232. package/src/materialization/marks/capabilities.js +5 -6
  233. package/src/materialization/rect.js +1 -1
  234. package/src/materialization/scaleGuideDependencies.js +1 -1
  235. package/src/materialization/scales/map.js +3 -0
  236. package/src/materialization/scales/policies/arc.js +8 -7
  237. package/src/materialization/scales/policies/bar.js +19 -7
  238. package/src/materialization/scales/policies/series.js +2 -1
  239. package/src/materialization/scales/resolve.js +5 -3
  240. package/src/materialization/selection/items/arc.js +1 -1
  241. package/src/materialization/selection/items/bar.js +7 -6
  242. package/src/materialization/selection/items/path.js +7 -6
  243. package/src/materialization/selection/items/rect.js +2 -2
  244. package/src/materialization/text.js +13 -4
  245. package/src/materialization/textLines.js +15 -0
  246. package/src/persistence/codec.js +37 -6
  247. package/src/persistence/validation.js +47 -0
  248. package/src/persistence.js +18 -5
  249. package/src/renderers/canvas/circle.js +6 -3
  250. package/src/renderers/canvas/fill.js +7 -1
  251. package/src/renderers/canvas/index.js +2 -1
  252. package/src/renderers/canvas/line.js +1 -0
  253. package/src/renderers/canvas/path.js +1 -1
  254. package/src/renderers/canvas/rect.js +11 -8
  255. package/src/renderers/canvas/stroke.js +2 -1
  256. package/src/renderers/canvas/text.js +5 -3
  257. package/src/renderers/svg.js +4 -1
  258. package/src/selectors/layers.js +19 -0
  259. package/src/theme/defaults.js +8 -2
  260. package/src/version.js +1 -1
  261. package/types/basic.d.ts +1 -0
  262. package/types/index.d.ts +7 -0
  263. package/types/program.d.ts +347 -85
@@ -1,197 +1 @@
1
- import { validateMeasuredRadiusScale } from "./radial.js";
2
- import { validateContinuousColorInterpolation, validateSequentialMidpoint } from "./color.js";
3
- import { isTransformedScaleType } from "./mapping.js";
4
- import {
5
- normalizeTransformParameters,
6
- validateTransformedDomain
7
- } from "./transformed.js";
8
- import {
9
- isContinuousColorScaleType,
10
- validateScalePropertyForType
11
- } from "./types.js";
12
-
13
- const BOOLEAN_PROPERTIES = Object.freeze([
14
- "nice", "zero", "clamp", "reverse"
15
- ]);
16
- const TYPE_PARAMETERS = Object.freeze(["base", "exponent", "constant"]);
17
-
18
- function retainedValue(previous, patch, defaults, property, typeChanged) {
19
- if (Object.hasOwn(patch, property)) return patch[property];
20
- if (!typeChanged && Object.hasOwn(previous, property)) {
21
- return previous[property];
22
- }
23
- return defaults[property];
24
- }
25
-
26
- function validateBandParameters(definition, previous, patch, defaults, typeChanged) {
27
- const hasPadding = Object.hasOwn(patch, "padding");
28
- if (
29
- hasPadding &&
30
- (Object.hasOwn(patch, "paddingInner") || Object.hasOwn(patch, "paddingOuter"))
31
- ) {
32
- throw new Error(
33
- "Scale padding cannot be combined with paddingInner or paddingOuter."
34
- );
35
- }
36
- if (
37
- hasPadding &&
38
- (!Number.isFinite(patch.padding) || patch.padding < 0 || patch.padding >= 1)
39
- ) {
40
- throw new RangeError(
41
- "Scale padding must be from 0 (inclusive) to 1 (exclusive)."
42
- );
43
- }
44
- const normalizedPatch = hasPadding
45
- ? { ...patch, paddingInner: patch.padding, paddingOuter: patch.padding }
46
- : patch;
47
- const paddingInner = retainedValue(
48
- previous, normalizedPatch, defaults, "paddingInner", typeChanged
49
- ) ?? 0;
50
- const paddingOuter = retainedValue(
51
- previous, normalizedPatch, defaults, "paddingOuter", typeChanged
52
- ) ?? 0;
53
- const align = retainedValue(
54
- previous, normalizedPatch, defaults, "align", typeChanged
55
- ) ?? 0.5;
56
- if (!Number.isFinite(paddingInner) || paddingInner < 0 || paddingInner >= 1) {
57
- throw new RangeError(
58
- "Scale paddingInner must be from 0 (inclusive) to 1 (exclusive)."
59
- );
60
- }
61
- if (!Number.isFinite(paddingOuter) || paddingOuter < 0) {
62
- throw new RangeError("Scale paddingOuter must be non-negative and finite.");
63
- }
64
- if (!Number.isFinite(align) || align < 0 || align > 1) {
65
- throw new RangeError("Scale align must be between 0 and 1.");
66
- }
67
- Object.assign(definition, { paddingInner, paddingOuter, align });
68
- }
69
-
70
- function validatePointParameters(definition, previous, patch, defaults, typeChanged) {
71
- const padding = retainedValue(
72
- previous, patch, defaults, "padding", typeChanged
73
- ) ?? 0.5;
74
- const align = retainedValue(previous, patch, defaults, "align", typeChanged) ?? 0.5;
75
- if (!Number.isFinite(padding) || padding < 0) {
76
- throw new RangeError("Scale padding must be non-negative and finite.");
77
- }
78
- if (!Number.isFinite(align) || align < 0 || align > 1) {
79
- throw new RangeError("Scale align must be between 0 and 1.");
80
- }
81
- Object.assign(definition, { padding, align });
82
- }
83
-
84
- export function normalizeScaleDefinition({
85
- type,
86
- previous = {},
87
- patch = {},
88
- defaults = {},
89
- retainCoreOnTypeChange = false,
90
- retainCompatibleOnTypeChange = false,
91
- allowOrdinalBandParameters = false,
92
- validateDomain,
93
- validateRange
94
- }) {
95
- const typeChanged = previous.type !== undefined && previous.type !== type;
96
- const rawDomain = Object.hasOwn(patch, "domain")
97
- ? patch.domain
98
- : (!typeChanged || retainCoreOnTypeChange) && Object.hasOwn(previous, "domain")
99
- ? previous.domain
100
- : defaults.domain ?? "auto";
101
- const rawRange = Object.hasOwn(patch, "range")
102
- ? patch.range
103
- : (!typeChanged || retainCoreOnTypeChange) && Object.hasOwn(previous, "range")
104
- ? previous.range
105
- : defaults.range ?? "auto";
106
- const definition = {
107
- type,
108
- domain: validateDomain(type, rawDomain),
109
- range: validateRange(type, rawRange)
110
- };
111
- const emptyDomain = retainedValue(previous, patch, defaults, "emptyDomain", typeChanged);
112
- if (emptyDomain !== undefined) {
113
- if (!["preserve", "require-explicit"].includes(emptyDomain)) {
114
- throw new Error('Scale emptyDomain must be "preserve" or "require-explicit".');
115
- }
116
- definition.emptyDomain = emptyDomain;
117
- }
118
-
119
- for (const property of BOOLEAN_PROPERTIES) {
120
- let value = retainedValue(previous, patch, defaults, property, typeChanged);
121
- if (
122
- value === undefined &&
123
- typeChanged &&
124
- retainCompatibleOnTypeChange &&
125
- Object.hasOwn(previous, property)
126
- ) {
127
- try {
128
- validateScalePropertyForType(type, property);
129
- value = previous[property];
130
- } catch {
131
- value = undefined;
132
- }
133
- }
134
- if (value === undefined) continue;
135
- if (typeof value !== "boolean") {
136
- throw new TypeError(`Scale ${property} must be a boolean.`);
137
- }
138
- validateScalePropertyForType(type, property);
139
- definition[property] = value;
140
- }
141
-
142
- const requested = Object.fromEntries(TYPE_PARAMETERS.flatMap(property => {
143
- const value = retainedValue(previous, patch, defaults, property, typeChanged);
144
- return value === undefined ? [] : [[property, value]];
145
- }));
146
- if (isTransformedScaleType(type)) {
147
- const parameters = normalizeTransformParameters(type, requested);
148
- if (type === "log") definition.base = parameters.base;
149
- if (type === "pow") definition.exponent = parameters.exponent;
150
- if (type === "symlog") definition.constant = parameters.constant;
151
- if (definition.domain !== "auto") {
152
- validateTransformedDomain(type, definition.domain, requested);
153
- }
154
- } else {
155
- for (const property of Object.keys(requested)) {
156
- validateScalePropertyForType(type, property);
157
- }
158
- }
159
-
160
- const interpolate = retainedValue(
161
- previous, patch, defaults, "interpolate", typeChanged
162
- );
163
- if (isContinuousColorScaleType(type)) {
164
- definition.interpolate = validateContinuousColorInterpolation(
165
- interpolate ?? "rgb"
166
- );
167
- } else if (interpolate !== undefined) {
168
- throw new Error(`Scale type "${type}" does not support interpolate.`);
169
- }
170
-
171
- const midpoint = validateSequentialMidpoint(
172
- retainedValue(previous, patch, defaults, "midpoint", typeChanged), type, definition.domain
173
- );
174
- if (midpoint !== undefined) definition.midpoint = midpoint;
175
-
176
- if (type === "band" || (type === "ordinal" && allowOrdinalBandParameters)) {
177
- validateBandParameters(definition, previous, patch, defaults, typeChanged);
178
- } else if (type === "point") {
179
- validatePointParameters(definition, previous, patch, defaults, typeChanged);
180
- } else {
181
- for (const property of ["paddingInner", "paddingOuter", "padding", "align"]) {
182
- if (Object.hasOwn(patch, property)) {
183
- validateScalePropertyForType(type, property);
184
- }
185
- }
186
- }
187
-
188
- const radialMapping = Object.hasOwn(patch, "radialMapping") ? patch.radialMapping : previous.radialMapping;
189
- if (radialMapping !== undefined) {
190
- definition.radialMapping = radialMapping;
191
- validateMeasuredRadiusScale({ ...definition,
192
- ...(Object.hasOwn(patch, "unknown") || Object.hasOwn(previous, "unknown")
193
- ? { unknown: patch.unknown ?? previous.unknown } : {})
194
- }, { allowAuto: true });
195
- }
196
- return definition;
197
- }
1
+ import{validateMeasuredRadiusScale as I}from"./radial.js";import{validateContinuousColorInterpolation as M,validateSequentialMidpoint as x}from"./color.js";import{isTransformedScaleType as T}from"./mapping.js";import{normalizeTransformParameters as D,validateTransformedDomain as F}from"./transformed.js";import{isContinuousColorScaleType as N,validateScalePropertyForType as O}from"./types.js";const k=Object.freeze(["nice","zero","clamp","reverse"]),z=Object.freeze(["base","exponent","constant"]);function m(i,t,n,e,o){return Object.hasOwn(t,e)?t[e]:!o&&Object.hasOwn(i,e)?i[e]:n[e]}function q(i,t,n,e,o){const l=Object.hasOwn(n,"padding");if(l&&(Object.hasOwn(n,"paddingInner")||Object.hasOwn(n,"paddingOuter")))throw new Error("Scale padding cannot be combined with paddingInner or paddingOuter.");if(l&&(!Number.isFinite(n.padding)||n.padding<0||n.padding>=1))throw new RangeError("Scale padding must be from 0 (inclusive) to 1 (exclusive).");const d=l?{...n,paddingInner:n.padding,paddingOuter:n.padding}:n,g=m(t,d,e,"paddingInner",o)??0,w=m(t,d,e,"paddingOuter",o)??0,c=m(t,d,e,"align",o)??.5;if(!Number.isFinite(g)||g<0||g>=1)throw new RangeError("Scale paddingInner must be from 0 (inclusive) to 1 (exclusive).");if(!Number.isFinite(w)||w<0)throw new RangeError("Scale paddingOuter must be non-negative and finite.");if(!Number.isFinite(c)||c<0||c>1)throw new RangeError("Scale align must be between 0 and 1.");Object.assign(i,{paddingInner:g,paddingOuter:w,align:c})}function A(i,t,n,e,o){const l=m(t,n,e,"padding",o)??.5,d=m(t,n,e,"align",o)??.5;if(!Number.isFinite(l)||l<0)throw new RangeError("Scale padding must be non-negative and finite.");if(!Number.isFinite(d)||d<0||d>1)throw new RangeError("Scale align must be between 0 and 1.");Object.assign(i,{padding:l,align:d})}function V({type:i,color:t=!1,previous:n={},patch:e={},defaults:o={},retainCoreOnTypeChange:l=!1,retainCompatibleOnTypeChange:d=!1,allowOrdinalBandParameters:g=!1,validateDomain:w,validateRange:c}){const s=n.type!==void 0&&n.type!==i,R=Object.hasOwn(e,"domain")?e.domain:(!s||l)&&Object.hasOwn(n,"domain")?n.domain:o.domain??"auto",P=Object.hasOwn(e,"range")?e.range:(!s||l)&&Object.hasOwn(n,"range")?n.range:o.range??"auto",r={type:i,domain:w(i,R),range:c(i,P)},u=m(n,e,o,"emptyDomain",s);if(u!==void 0){if(!["preserve","require-explicit"].includes(u))throw new Error('Scale emptyDomain must be "preserve" or "require-explicit".');r.emptyDomain=u}for(const a of k){let f=m(n,e,o,a,s);if(f===void 0&&s&&d&&Object.hasOwn(n,a))try{O(i,a),f=n[a]}catch{f=void 0}if(f!==void 0){if(typeof f!="boolean")throw new TypeError(`Scale ${a} must be a boolean.`);O(i,a),r[a]=f}}const j=Object.fromEntries(z.flatMap(a=>{const f=m(n,e,o,a,s);return f===void 0?[]:[[a,f]]}));if(T(i)){const a=D(i,j);i==="log"&&(r.base=a.base),i==="pow"&&(r.exponent=a.exponent),i==="symlog"&&(r.constant=a.constant),r.domain!=="auto"&&F(i,r.domain,j)}else for(const a of Object.keys(j))O(i,a);let b=m(n,e,o,"interpolate",s);if(i==="sequential"||t&&N(i))b===void 0&&d&&(b=n.interpolate),r.interpolate=M(b??"rgb");else if(b!==void 0)throw new Error(`Scale type "${i}" does not support interpolate.`);const S=x(m(n,e,o,"midpoint",s),i,r.domain);if(S!==void 0&&(r.midpoint=S),i==="band"||i==="ordinal"&&g)q(r,n,e,o,s);else if(i==="point")A(r,n,e,o,s);else for(const a of["paddingInner","paddingOuter","padding","align"])Object.hasOwn(e,a)&&O(i,a);const E=Object.hasOwn(e,"radialMapping")?e.radialMapping:n.radialMapping;return E!==void 0&&(r.radialMapping=E,I({...r,...Object.hasOwn(e,"unknown")||Object.hasOwn(n,"unknown")?{unknown:e.unknown??n.unknown}:{}},{allowAuto:!0})),r}export{V as normalizeScaleDefinition};
@@ -1,195 +1 @@
1
- import { cloneAndFreeze } from "../../core/immutable.js";
2
- import { validateGeneratedItemLimit } from "../../core/validation.js";
3
- import { interpolateNumber } from "../numeric.js";
4
- import { formatValue, validateValueFormat } from "../valueFormat.js";
5
- import { resolveColorRange, validateColorRange } from "./appearance.js";
6
- import { SCALE_ROLES, validateScaleTypeForRole } from "./types.js";
7
-
8
- export const DISCRETIZED_COLOR_SCALE_TYPES = cloneAndFreeze([
9
- "quantize",
10
- "quantile",
11
- "threshold"
12
- ]);
13
-
14
- function finiteValues(values, label) {
15
- if (!Array.isArray(values) || values.length === 0 || !values.every(Number.isFinite)) {
16
- throw new TypeError(`${label} must contain finite numbers.`);
17
- }
18
- return values;
19
- }
20
-
21
- function ascending(values, label) {
22
- finiteValues(values, label);
23
- if (values.some((value, index) => index > 0 && value <= values[index - 1])) {
24
- throw new RangeError(`${label} must be strictly increasing.`);
25
- }
26
- return values;
27
- }
28
-
29
- function nondecreasing(values, label) {
30
- finiteValues(values, label);
31
- if (values.some((value, index) => index > 0 && value < values[index - 1])) {
32
- throw new RangeError(`${label} must be nondecreasing.`);
33
- }
34
- return values;
35
- }
36
-
37
- function quantile(sorted, probability) {
38
- const position = (sorted.length - 1) * probability;
39
- const lower = Math.floor(position);
40
- const upper = Math.ceil(position);
41
- return interpolateNumber(sorted[lower], sorted[upper], position - lower);
42
- }
43
-
44
- export function validateDiscretizedColorDomain(type, domain) {
45
- validateScaleTypeForRole(type, SCALE_ROLES.discretizedColor);
46
- if (domain === "auto") {
47
- if (type === "threshold") {
48
- throw new Error("Threshold color scale requires an explicit domain.");
49
- }
50
- return domain;
51
- }
52
- if (!Array.isArray(domain)) {
53
- throw new TypeError("Discretized color domain must be an array or auto.");
54
- }
55
- if (type === "quantize") {
56
- if (domain.length !== 2 || !domain.every(Number.isFinite) || domain[0] >= domain[1]) {
57
- throw new RangeError("Quantize color domain must be an increasing pair.");
58
- }
59
- } else if (type === "quantile") {
60
- finiteValues(domain, "Quantile color domain");
61
- } else {
62
- ascending(domain, "Threshold color domain");
63
- }
64
- return cloneAndFreeze(domain);
65
- }
66
-
67
- export function validateDiscretizedColorRange(range) {
68
- if (Array.isArray(range)) {
69
- validateGeneratedItemLimit(
70
- range.length,
71
- "Discretized color range length"
72
- );
73
- }
74
- const validated = validateColorRange(range);
75
- if (validated === "auto") return validated;
76
- if (Array.isArray(validated) && validated.length < 2) {
77
- throw new RangeError("Discretized color range requires at least two colors.");
78
- }
79
- return validated;
80
- }
81
-
82
- export function resolveDiscretizedColorScale({ type, domain, range, values }) {
83
- validateScaleTypeForRole(type, SCALE_ROLES.discretizedColor);
84
- finiteValues(values, "Discretized color values");
85
- const requestedDomain = validateDiscretizedColorDomain(type, domain);
86
- const sample = [...values].sort((left, right) => left - right);
87
- const validatedRange = validateDiscretizedColorRange(range);
88
- const colorCount = type === "threshold"
89
- ? requestedDomain.length + 1
90
- : Array.isArray(range) ? range.length : 5;
91
- const colors = resolveColorRange(
92
- validatedRange === "auto" ? { palette: "viridis" } : validatedRange,
93
- colorCount
94
- );
95
- if (colors.length < 2) {
96
- throw new RangeError("Discretized color range requires at least two colors.");
97
- }
98
- let resolvedDomain;
99
- let thresholds;
100
- if (type === "quantize") {
101
- resolvedDomain = requestedDomain === "auto"
102
- ? [sample[0], sample.at(-1)]
103
- : [...requestedDomain];
104
- if (resolvedDomain[0] === resolvedDomain[1]) {
105
- throw new RangeError("Quantize color scale requires a non-zero domain span.");
106
- }
107
- thresholds = Array.from(
108
- { length: colors.length - 1 },
109
- (_, index) => interpolateNumber(
110
- resolvedDomain[0],
111
- resolvedDomain[1],
112
- (index + 1) / colors.length
113
- )
114
- );
115
- if (thresholds.some((value, index) =>
116
- value <= (index === 0 ? resolvedDomain[0] : thresholds[index - 1]) ||
117
- value >= resolvedDomain[1]
118
- )) {
119
- throw new RangeError(
120
- "Quantize color range requests more classes than its numeric domain can represent."
121
- );
122
- }
123
- } else if (type === "quantile") {
124
- const source = requestedDomain === "auto"
125
- ? sample
126
- : [...requestedDomain].sort((left, right) => left - right);
127
- resolvedDomain = source;
128
- thresholds = Array.from(
129
- { length: colors.length - 1 },
130
- (_, index) => quantile(source, (index + 1) / colors.length)
131
- );
132
- } else {
133
- resolvedDomain = [...requestedDomain];
134
- thresholds = [...requestedDomain];
135
- if (colors.length !== thresholds.length + 1) {
136
- throw new RangeError(
137
- "Threshold color range must contain exactly one more color than its domain."
138
- );
139
- }
140
- }
141
- return cloneAndFreeze({
142
- type,
143
- domain: resolvedDomain,
144
- thresholds,
145
- range: colors
146
- });
147
- }
148
-
149
- export function discretizedColorIndex(value, thresholds) {
150
- if (!Number.isFinite(value)) {
151
- throw new TypeError("Discretized color values must be finite numbers.");
152
- }
153
- let index = 0;
154
- while (index < thresholds.length && value >= thresholds[index]) index += 1;
155
- return index;
156
- }
157
-
158
- export function mapDiscretizedColors(values, scale) {
159
- const hasUnknown = Object.hasOwn(scale, "unknown");
160
- return cloneAndFreeze(values.map(value => {
161
- if (!Number.isFinite(value) && hasUnknown) return scale.unknown;
162
- return scale.range[discretizedColorIndex(value, scale.thresholds)];
163
- }));
164
- }
165
-
166
- export function formatDiscretizedIntervals(thresholds, format = "auto") {
167
- nondecreasing(thresholds, "Discretized color thresholds");
168
- const resolvedFormat = validateValueFormat(format, "Legend label format");
169
- let resolution = Infinity;
170
- for (let index = 1; index < thresholds.length; index += 1) {
171
- const gap = thresholds[index] - thresholds[index - 1];
172
- if (Number.isFinite(gap) && gap > 0) resolution = Math.min(resolution, gap);
173
- }
174
- let labels = resolvedFormat === "auto"
175
- ? thresholds.map(value => Number.isInteger(value) ? String(value) : value.toFixed(1))
176
- : thresholds.map(value => formatValue(value, {
177
- format: resolvedFormat,
178
- valueType: "quantitative",
179
- label: "Legend label format"
180
- }));
181
- if (resolvedFormat === "auto" && thresholds.some((value, index) => {
182
- const rounded = Number(labels[index]);
183
- return value !== 0 && rounded === 0 ||
184
- index > 0 && value !== thresholds[index - 1] &&
185
- labels[index] === labels[index - 1] ||
186
- !Number.isInteger(value) && Math.abs(rounded - value) > resolution / 4;
187
- })) labels = thresholds.map(String);
188
- return cloneAndFreeze([
189
- `< ${labels[0]}`,
190
- ...labels.slice(0, -1).map((value, index) =>
191
- `${value}–${labels[index + 1]}`
192
- ),
193
- `≥ ${labels.at(-1)}`
194
- ]);
195
- }
1
+ import{cloneAndFreeze as c}from"../../core/immutable.js";import{validateGeneratedItemLimit as g}from"../../core/validation.js";import{interpolateNumber as h,quantileSorted as z}from"../numeric.js";import{formatValue as w,validateValueFormat as p}from"../valueFormat.js";import{resolveColorRange as D,validateColorRange as b}from"./appearance.js";import{SCALE_ROLES as m,validateScaleTypeForRole as f}from"./types.js";import{validatePair as E,validateFiniteScaleArray as u,validateIncreasingScaleArray as q}from"./validation.js";const Q=c(["quantize","quantile","threshold"]);function C(r,e){if(u(r,e),r.some((i,o)=>o>0&&i<r[o-1]))throw new RangeError(`${e} must be nondecreasing.`);return r}function R(r,e){return f(r,m.discretizedColor),v(r,e,"color")}function v(r,e,i){if(e==="auto"){if(r==="threshold")throw new Error(`Threshold ${i} scale requires an explicit domain.`);return e}if(i==="color"&&!Array.isArray(e))throw new TypeError("Discretized color domain must be an array or auto.");if(r==="quantize"){if(i==="size"){if(E(e,"Quantize size domain"),e[0]>=e[1])throw new RangeError("Quantize size domain must be a strictly increasing pair.")}else if(e.length!==2||!e.every(Number.isFinite)||e[0]>=e[1])throw new RangeError("Quantize color domain must be an increasing pair.")}else r==="quantile"?u(e,`Quantile ${i} domain`):q(e,`Threshold ${i} domain`);return c(e)}function A(r){Array.isArray(r)&&g(r.length,"Discretized color range length");const e=b(r);if(e==="auto")return e;if(Array.isArray(e)&&e.length<2)throw new RangeError("Discretized color range requires at least two colors.");return e}function y({type:r,domain:e,values:i,count:o,kind:a}){let t=e==="auto"?[...i].sort((s,l)=>s-l):[...e],n;if(r==="quantize"){if(e==="auto"){if(t.length===0)throw new Error("Cannot infer an automatic quantize size domain from no values.");t=[t[0],t.at(-1)]}if(t[0]===t[1])throw new RangeError(`Quantize ${a} scale requires a non-zero domain span.`);if(n=Array.from({length:o-1},(s,l)=>h(t[0],t[1],(l+1)/o)),n.some((s,l)=>s<=(l===0?t[0]:n[l-1])||s>=t[1]))throw new RangeError(a==="size"?"Quantize size range requests more buckets than its domain can represent.":"Quantize color range requests more classes than its numeric domain can represent.")}else if(r==="quantile"){if(t.length===0)throw new Error("Cannot infer automatic quantile size thresholds from no values.");t.sort((s,l)=>s-l),n=Array.from({length:o-1},(s,l)=>z(t,(l+1)/o))}else if(n=[...t],o!==n.length+1)throw new RangeError(`Threshold ${a} range must contain exactly one more ${a==="size"?"area":"color"} than its domain.`);return{domain:t,thresholds:n}}function _({type:r,domain:e,range:i,values:o}){f(r,m.discretizedColor),u(o,"Discretized color values");const a=R(r,e),t=A(i),n=r==="threshold"?a.length+1:Array.isArray(i)?i.length:5,s=D(t==="auto"?{palette:"viridis"}:t,n);if(s.length<2)throw new RangeError("Discretized color range requires at least two colors.");const{domain:l,thresholds:d}=y({type:r,domain:a,values:o,count:s.length,kind:"color"});return c({type:r,domain:l,thresholds:d,range:s})}function S(r,e,i="Discretized color"){if(!Number.isFinite(r))throw new TypeError(`${i} values must be finite numbers.`);let o=0;for(;o<e.length&&r>=e[o];)o+=1;return o}function O(r,e){const i=Object.hasOwn(e,"unknown");return c(r.map(o=>!Number.isFinite(o)&&i?e.unknown:e.range[S(o,e.thresholds)]))}function V(r,e="auto"){C(r,"Discretized color thresholds");const i=p(e,"Legend label format");let o=1/0;for(let t=1;t<r.length;t+=1){const n=r[t]-r[t-1];Number.isFinite(n)&&n>0&&(o=Math.min(o,n))}let a=i==="auto"?r.map(t=>Number.isInteger(t)?String(t):t.toFixed(1)):r.map(t=>w(t,{format:i,valueType:"quantitative",label:"Legend label format"}));return i==="auto"&&r.some((t,n)=>{const s=Number(a[n]);return t!==0&&s===0||n>0&&t!==r[n-1]&&a[n]===a[n-1]||!Number.isInteger(t)&&Math.abs(s-t)>o/4})&&(a=r.map(String)),c([`< ${a[0]}`,...a.slice(0,-1).map((t,n)=>`${t}\u2013${a[n+1]}`),`\u2265 ${a.at(-1)}`])}export{Q as DISCRETIZED_COLOR_SCALE_TYPES,S as discretizedColorIndex,V as formatDiscretizedIntervals,O as mapDiscretizedColors,_ as resolveDiscretizedColorScale,y as resolveDiscretizedDomain,R as validateDiscretizedColorDomain,A as validateDiscretizedColorRange,v as validateDiscretizedDomain};