ggaction 0.0.1

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 (222) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/LICENSE +21 -0
  3. package/README.md +108 -0
  4. package/package.json +84 -0
  5. package/src/AGENTS.md +119 -0
  6. package/src/ChartProgram.js +10 -0
  7. package/src/actions/boxPlots/components.js +88 -0
  8. package/src/actions/boxPlots/create.js +84 -0
  9. package/src/actions/boxPlots/index.js +10 -0
  10. package/src/actions/boxPlots/materialize.js +183 -0
  11. package/src/actions/boxPlots/options.js +147 -0
  12. package/src/actions/boxPlots/resolve.js +38 -0
  13. package/src/actions/canvas/actions.js +144 -0
  14. package/src/actions/canvas/index.js +6 -0
  15. package/src/actions/coordinates/actions.js +85 -0
  16. package/src/actions/coordinates/index.js +5 -0
  17. package/src/actions/data/box.js +40 -0
  18. package/src/actions/data/create.js +33 -0
  19. package/src/actions/data/density.js +62 -0
  20. package/src/actions/data/derived.js +52 -0
  21. package/src/actions/data/filter.js +168 -0
  22. package/src/actions/data/index.js +32 -0
  23. package/src/actions/data/interval.js +69 -0
  24. package/src/actions/data/regression.js +71 -0
  25. package/src/actions/data/shared.js +25 -0
  26. package/src/actions/encodings/appearance.js +243 -0
  27. package/src/actions/encodings/barWidth.js +60 -0
  28. package/src/actions/encodings/color.js +329 -0
  29. package/src/actions/encodings/density.js +225 -0
  30. package/src/actions/encodings/histogram.js +50 -0
  31. package/src/actions/encodings/index.js +23 -0
  32. package/src/actions/encodings/offset.js +104 -0
  33. package/src/actions/encodings/position/policies/area.js +24 -0
  34. package/src/actions/encodings/position/policies/bar.js +110 -0
  35. package/src/actions/encodings/position/policies/common.js +17 -0
  36. package/src/actions/encodings/position/policies/index.js +25 -0
  37. package/src/actions/encodings/position/policies/line.js +74 -0
  38. package/src/actions/encodings/position/policies/point.js +19 -0
  39. package/src/actions/encodings/position/policies/rule.js +17 -0
  40. package/src/actions/encodings/position/resolve.js +170 -0
  41. package/src/actions/encodings/position.js +176 -0
  42. package/src/actions/encodings/ranged.js +276 -0
  43. package/src/actions/encodings/ruleAppearance.js +47 -0
  44. package/src/actions/encodings/shared.js +164 -0
  45. package/src/actions/encodings/strokeDash.js +145 -0
  46. package/src/actions/errorBands/components.js +81 -0
  47. package/src/actions/errorBands/create.js +159 -0
  48. package/src/actions/errorBands/index.js +7 -0
  49. package/src/actions/errorBands/resolve.js +28 -0
  50. package/src/actions/errorBars/components.js +62 -0
  51. package/src/actions/errorBars/create.js +144 -0
  52. package/src/actions/errorBars/index.js +7 -0
  53. package/src/actions/errorBars/resolve.js +25 -0
  54. package/src/actions/guides/axes/axes.js +204 -0
  55. package/src/actions/guides/axes/axis.js +131 -0
  56. package/src/actions/guides/axes/index.js +17 -0
  57. package/src/actions/guides/axes/labels.js +231 -0
  58. package/src/actions/guides/axes/lines.js +149 -0
  59. package/src/actions/guides/axes/policy.js +155 -0
  60. package/src/actions/guides/axes/tickGroups.js +149 -0
  61. package/src/actions/guides/axes/ticks.js +179 -0
  62. package/src/actions/guides/axes/titles.js +264 -0
  63. package/src/actions/guides/grids/grid.js +228 -0
  64. package/src/actions/guides/grids/index.js +1 -0
  65. package/src/actions/guides/grids/resolve.js +258 -0
  66. package/src/actions/guides/guides.js +138 -0
  67. package/src/actions/guides/index.js +11 -0
  68. package/src/actions/guides/legends/categorical/actions.js +329 -0
  69. package/src/actions/guides/legends/categorical/components.js +140 -0
  70. package/src/actions/guides/legends/categorical/index.js +49 -0
  71. package/src/actions/guides/legends/categorical/layout.js +445 -0
  72. package/src/actions/guides/legends/categorical/options.js +188 -0
  73. package/src/actions/guides/legends/categorical/recipes.js +120 -0
  74. package/src/actions/guides/legends/categorical/resolve.js +131 -0
  75. package/src/actions/guides/legends/categorical/symbols.js +280 -0
  76. package/src/actions/guides/legends/categorical/validation.js +27 -0
  77. package/src/actions/guides/legends/continuous/common.js +273 -0
  78. package/src/actions/guides/legends/continuous/gradient.js +363 -0
  79. package/src/actions/guides/legends/continuous/index.js +25 -0
  80. package/src/actions/guides/legends/continuous/interval.js +234 -0
  81. package/src/actions/guides/legends/continuous/opacity.js +382 -0
  82. package/src/actions/guides/legends/edit.js +314 -0
  83. package/src/actions/guides/legends/index.js +9 -0
  84. package/src/actions/guides/legends/size.js +220 -0
  85. package/src/actions/guides/tickValues.js +84 -0
  86. package/src/actions/index.js +31 -0
  87. package/src/actions/intervals/resolve.js +352 -0
  88. package/src/actions/marks/area.js +369 -0
  89. package/src/actions/marks/bar/create.js +46 -0
  90. package/src/actions/marks/bar/edit.js +90 -0
  91. package/src/actions/marks/bar/index.js +9 -0
  92. package/src/actions/marks/bar/materialize.js +130 -0
  93. package/src/actions/marks/index.js +13 -0
  94. package/src/actions/marks/line.js +248 -0
  95. package/src/actions/marks/point.js +420 -0
  96. package/src/actions/marks/rule.js +287 -0
  97. package/src/actions/marks/shared.js +112 -0
  98. package/src/actions/primitives/createGraphics.js +163 -0
  99. package/src/actions/primitives/editGraphics.js +208 -0
  100. package/src/actions/primitives/index.js +9 -0
  101. package/src/actions/primitives/semantic.js +168 -0
  102. package/src/actions/primitives/semanticValue.js +182 -0
  103. package/src/actions/regression/components.js +216 -0
  104. package/src/actions/regression/create.js +102 -0
  105. package/src/actions/regression/index.js +23 -0
  106. package/src/actions/regression/resolve.js +57 -0
  107. package/src/actions/scales/consumers.js +283 -0
  108. package/src/actions/scales/create.js +148 -0
  109. package/src/actions/scales/definitions.js +319 -0
  110. package/src/actions/scales/edit.js +290 -0
  111. package/src/actions/scales/index.js +11 -0
  112. package/src/actions/scales/materialize.js +466 -0
  113. package/src/actions/scales/quantitativeColor.js +45 -0
  114. package/src/actions/selection/actions.js +384 -0
  115. package/src/actions/selection/index.js +23 -0
  116. package/src/actions/titles/actions.js +235 -0
  117. package/src/actions/titles/index.js +19 -0
  118. package/src/actions/titles/resolve.js +321 -0
  119. package/src/core/ChartProgram.js +316 -0
  120. package/src/core/action.js +120 -0
  121. package/src/core/identifiers.js +28 -0
  122. package/src/core/immutable.js +94 -0
  123. package/src/core/specs.js +29 -0
  124. package/src/core/textMetrics.js +23 -0
  125. package/src/core/validation.js +54 -0
  126. package/src/core/vocabulary.js +58 -0
  127. package/src/extension.js +2 -0
  128. package/src/grammar/aggregate.js +259 -0
  129. package/src/grammar/areaSeries.js +217 -0
  130. package/src/grammar/bars/aggregate.js +98 -0
  131. package/src/grammar/bars/geometry.js +54 -0
  132. package/src/grammar/bars/policy.js +90 -0
  133. package/src/grammar/boxPlot.js +140 -0
  134. package/src/grammar/coordinates.js +25 -0
  135. package/src/grammar/curveCommands.js +356 -0
  136. package/src/grammar/density.js +296 -0
  137. package/src/grammar/filter.js +169 -0
  138. package/src/grammar/histogram.js +332 -0
  139. package/src/grammar/interval.js +343 -0
  140. package/src/grammar/lineSeries.js +197 -0
  141. package/src/grammar/markFilter.js +46 -0
  142. package/src/grammar/markSelection.js +326 -0
  143. package/src/grammar/palettes.js +208 -0
  144. package/src/grammar/pathCommands.js +86 -0
  145. package/src/grammar/pointShapes.js +164 -0
  146. package/src/grammar/positionCompatibility.js +32 -0
  147. package/src/grammar/regression.js +578 -0
  148. package/src/grammar/ruleAppearance.js +12 -0
  149. package/src/grammar/rules.js +90 -0
  150. package/src/grammar/scales/appearance.js +185 -0
  151. package/src/grammar/scales/color.js +349 -0
  152. package/src/grammar/scales/continuous.js +111 -0
  153. package/src/grammar/scales/definition.js +153 -0
  154. package/src/grammar/scales/discretized.js +159 -0
  155. package/src/grammar/scales/fields.js +133 -0
  156. package/src/grammar/scales/index.js +12 -0
  157. package/src/grammar/scales/mapping.js +27 -0
  158. package/src/grammar/scales/ordinal.js +221 -0
  159. package/src/grammar/scales/policies.js +50 -0
  160. package/src/grammar/scales/temporal.js +53 -0
  161. package/src/grammar/scales/transformed.js +236 -0
  162. package/src/grammar/scales/types.js +102 -0
  163. package/src/grammar/scales/validation.js +66 -0
  164. package/src/grammar/scales.js +1 -0
  165. package/src/grammar/schemas/concreteGraphic.js +73 -0
  166. package/src/grammar/schemas/graphic.js +131 -0
  167. package/src/grammar/schemas/graphicBounds.js +146 -0
  168. package/src/grammar/schemas/graphicTree.js +133 -0
  169. package/src/grammar/schemas/mark.js +4 -0
  170. package/src/grammar/schemas/semanticPath.js +174 -0
  171. package/src/grammar/seriesLayout.js +76 -0
  172. package/src/grammar/ticks.js +149 -0
  173. package/src/grammar/transforms.js +37 -0
  174. package/src/index.js +2 -0
  175. package/src/layout/canvas.js +114 -0
  176. package/src/layout/grid.js +17 -0
  177. package/src/layout/legend.js +51 -0
  178. package/src/layout/text.js +62 -0
  179. package/src/layout/title.js +107 -0
  180. package/src/materialization/bars/aggregate.js +184 -0
  181. package/src/materialization/bars/grouped.js +117 -0
  182. package/src/materialization/bars/histogram.js +163 -0
  183. package/src/materialization/bars/ranged.js +53 -0
  184. package/src/materialization/bars/resolve.js +56 -0
  185. package/src/materialization/dataProvenance.js +15 -0
  186. package/src/materialization/density.js +29 -0
  187. package/src/materialization/dependencies.js +134 -0
  188. package/src/materialization/encodings.js +50 -0
  189. package/src/materialization/graphicHierarchy.js +104 -0
  190. package/src/materialization/legends.js +20 -0
  191. package/src/materialization/marks.js +110 -0
  192. package/src/materialization/planner.js +38 -0
  193. package/src/materialization/selection/filter.js +40 -0
  194. package/src/materialization/selection/items.js +503 -0
  195. package/src/materialization/selection/path.js +37 -0
  196. package/src/materialization/selection/point.js +118 -0
  197. package/src/materialization/selection/policies/area.js +10 -0
  198. package/src/materialization/selection/policies/bar.js +10 -0
  199. package/src/materialization/selection/policies/index.js +40 -0
  200. package/src/materialization/selection/policies/line.js +10 -0
  201. package/src/materialization/selection/policies/point.js +10 -0
  202. package/src/materialization/selection/policies/rule.js +10 -0
  203. package/src/materialization/selection/state.js +67 -0
  204. package/src/materialization/selection/styles.js +183 -0
  205. package/src/renderers/canvas/circle.js +62 -0
  206. package/src/renderers/canvas/index.js +132 -0
  207. package/src/renderers/canvas/line.js +54 -0
  208. package/src/renderers/canvas/path.js +98 -0
  209. package/src/renderers/canvas/rect.js +57 -0
  210. package/src/renderers/canvas/text.js +96 -0
  211. package/src/renderers/canvas/validation.js +23 -0
  212. package/src/renderers/png.js +29 -0
  213. package/src/selectors/coordinates.js +13 -0
  214. package/src/selectors/datasets.js +13 -0
  215. package/src/selectors/index.js +4 -0
  216. package/src/selectors/layers.js +32 -0
  217. package/src/selectors/scales.js +21 -0
  218. package/src/theme/defaults.js +13 -0
  219. package/types/extension.d.ts +14 -0
  220. package/types/index.d.ts +81 -0
  221. package/types/png.d.ts +14 -0
  222. package/types/program.d.ts +1096 -0
@@ -0,0 +1,148 @@
1
+ import { action } from "../../core/action.js";
2
+ import { validateUserId } from "../../core/identifiers.js";
3
+ import { validateKeys } from "../../core/validation.js";
4
+ import {
5
+ validateOrdinalDomain,
6
+ validateOrdinalRange,
7
+ validateScaleDomain,
8
+ validateScaleRange,
9
+ validateScaleType,
10
+ validateDiscretizedColorDomain,
11
+ validateDiscretizedColorRange,
12
+ validateSequentialColorRange,
13
+ hasOrdinalDomain,
14
+ isColorScaleType,
15
+ isContinuousColorScaleType,
16
+ isDiscretizedColorScaleType,
17
+ normalizeScaleDefinition
18
+ } from "../../grammar/scales.js";
19
+ import { findSemanticScale } from "../../selectors/scales.js";
20
+
21
+ const CREATE_SCALE_OPTIONS = Object.freeze([
22
+ "id",
23
+ "type",
24
+ "domain",
25
+ "range",
26
+ "nice",
27
+ "zero",
28
+ "clamp",
29
+ "reverse",
30
+ "base",
31
+ "exponent",
32
+ "constant",
33
+ "paddingInner",
34
+ "paddingOuter",
35
+ "padding",
36
+ "align",
37
+ "palette",
38
+ "interpolate",
39
+ "unknown"
40
+ ]);
41
+
42
+ function validateOptions(args) {
43
+ validateKeys(args, CREATE_SCALE_OPTIONS, "createScale");
44
+ }
45
+
46
+ function sameScaleSetting(left, right) {
47
+ if (left === right) return true;
48
+ if (Array.isArray(left) && Array.isArray(right)) {
49
+ return left.length === right.length && left.every(
50
+ (value, index) => sameScaleSetting(value, right[index])
51
+ );
52
+ }
53
+ if (
54
+ left !== null &&
55
+ right !== null &&
56
+ typeof left === "object" &&
57
+ typeof right === "object" &&
58
+ !Array.isArray(left) &&
59
+ !Array.isArray(right)
60
+ ) {
61
+ const leftKeys = Object.keys(left);
62
+ const rightKeys = Object.keys(right);
63
+ return leftKeys.length === rightKeys.length && leftKeys.every(
64
+ key => Object.hasOwn(right, key) && sameScaleSetting(left[key], right[key])
65
+ );
66
+ }
67
+ return false;
68
+ }
69
+
70
+ function assertEquivalentScale(existing, expected) {
71
+ const keys = new Set([...Object.keys(existing), ...Object.keys(expected)]);
72
+ keys.delete("id");
73
+ if ([...keys].some(key => !sameScaleSetting(existing[key], expected[key]))) {
74
+ throw new Error(`Scale "${existing.id}" already exists with a different definition.`);
75
+ }
76
+ }
77
+
78
+ export const createScale = action(
79
+ {
80
+ op: "createScale",
81
+ description: "Create a named semantic scale."
82
+ },
83
+ function (args = {}) {
84
+ validateOptions(args);
85
+ const id = validateUserId(args.id, "Scale id");
86
+ const type = validateScaleType(args.type ?? "linear");
87
+ const colorType = isColorScaleType(type);
88
+ if (args.palette !== undefined && args.range !== undefined) {
89
+ throw new Error("Color scale cannot specify both palette and range.");
90
+ }
91
+ if (args.palette !== undefined && !colorType) {
92
+ throw new Error(`Scale type "${type}" does not support palette.`);
93
+ }
94
+ if (args.interpolate !== undefined && type !== "sequential") {
95
+ throw new Error(`Scale type "${type}" does not support interpolate.`);
96
+ }
97
+ const requestedRange = args.palette === undefined
98
+ ? args.range
99
+ : { palette: args.palette };
100
+ const definition = normalizeScaleDefinition({
101
+ type,
102
+ patch: {
103
+ ...args,
104
+ ...(requestedRange === undefined ? {} : { range: requestedRange })
105
+ },
106
+ validateDomain: (scaleType, value) =>
107
+ isDiscretizedColorScaleType(scaleType)
108
+ ? validateDiscretizedColorDomain(scaleType, value)
109
+ : hasOrdinalDomain(scaleType)
110
+ ? validateOrdinalDomain(value)
111
+ : validateScaleDomain(value),
112
+ validateRange: (scaleType, value) =>
113
+ isDiscretizedColorScaleType(scaleType)
114
+ ? validateDiscretizedColorRange(value)
115
+ : isContinuousColorScaleType(scaleType)
116
+ ? validateSequentialColorRange(value)
117
+ : scaleType === "ordinal"
118
+ ? validateOrdinalRange(value)
119
+ : validateScaleRange(value)
120
+ });
121
+ if (Object.hasOwn(args, "unknown")) definition.unknown = args.unknown;
122
+ const existing = findSemanticScale(this, id);
123
+
124
+ if (existing !== undefined) {
125
+ assertEquivalentScale(existing, definition);
126
+ return this;
127
+ }
128
+
129
+ let next = this
130
+ .editSemantic({ property: `scale[${id}].type`, value: definition.type })
131
+ .editSemantic({ property: `scale[${id}].domain`, value: definition.domain })
132
+ .editSemantic({ property: `scale[${id}].range`, value: definition.range });
133
+
134
+ for (const property of [
135
+ "nice", "zero", "clamp", "reverse", "base", "exponent", "constant",
136
+ "paddingInner", "paddingOuter", "padding", "align", "interpolate",
137
+ "unknown"
138
+ ]) {
139
+ if (definition[property] === undefined) continue;
140
+ next = next.editSemantic({
141
+ property: `scale[${id}].${property}`,
142
+ value: definition[property]
143
+ });
144
+ }
145
+
146
+ return next;
147
+ }
148
+ );
@@ -0,0 +1,319 @@
1
+ import { validateUserId } from "../../core/identifiers.js";
2
+ import { isPlainObject } from "../../core/immutable.js";
3
+ import { validateKeys } from "../../core/validation.js";
4
+ import {
5
+ validateColorRange,
6
+ validateContinuousColorInterpolation,
7
+ validateLinearScaleType,
8
+ validateOpacityRange,
9
+ validateOrdinalDomain,
10
+ validateOrdinalScaleType,
11
+ validateScaleDomain,
12
+ validateScaleRange,
13
+ validateShapeRange,
14
+ validateSizeRange,
15
+ validateStrokeDashRange,
16
+ validateSequentialColorRange,
17
+ validateDiscretizedColorDomain,
18
+ validateDiscretizedColorRange,
19
+ validateTimeScaleType,
20
+ normalizeScaleDefinition,
21
+ SCALE_ROLES,
22
+ validateScalePropertyForType,
23
+ validateScaleTypeForRole,
24
+ isDiscretePositionScaleType
25
+ } from "../../grammar/scales.js";
26
+ import { withScaleUnknown } from "../../grammar/scales.js";
27
+ import { findSemanticScale } from "../../selectors/scales.js";
28
+
29
+ const BASE_OPTIONS = Object.freeze(["id", "type", "domain", "range"]);
30
+ const POSITION_OPTIONS = Object.freeze([
31
+ ...BASE_OPTIONS,
32
+ "nice",
33
+ "zero",
34
+ "clamp",
35
+ "reverse",
36
+ "base",
37
+ "exponent",
38
+ "constant",
39
+ "paddingInner",
40
+ "paddingOuter",
41
+ "padding",
42
+ "align",
43
+ "unknown"
44
+ ]);
45
+ const COLOR_OPTIONS = Object.freeze([...BASE_OPTIONS, "palette", "unknown"]);
46
+ const SEQUENTIAL_COLOR_OPTIONS = Object.freeze([
47
+ ...COLOR_OPTIONS,
48
+ "interpolate",
49
+ "clamp",
50
+ "reverse"
51
+ ]);
52
+ const OPACITY_OPTIONS = Object.freeze([
53
+ ...POSITION_OPTIONS,
54
+ "clamp",
55
+ "reverse"
56
+ ]);
57
+
58
+ function optionsObject(options) {
59
+ if (!isPlainObject(options)) {
60
+ throw new TypeError("Encoding scale must be a plain object.");
61
+ }
62
+ return options;
63
+ }
64
+
65
+ export function resolvePositionScaleDefinition(
66
+ program,
67
+ channel,
68
+ fieldType,
69
+ options,
70
+ defaults = {}
71
+ ) {
72
+ optionsObject(options);
73
+ validateKeys(options, POSITION_OPTIONS, "scale");
74
+ const id = validateUserId(options.id ?? channel, "Scale id");
75
+ const existing = findSemanticScale(program, id);
76
+ const expectedType = fieldType === "temporal"
77
+ ? "time"
78
+ : ["ordinal", "nominal"].includes(fieldType)
79
+ ? defaults.discreteType ?? "point"
80
+ : "linear";
81
+ const type = options.type ?? existing?.type ?? expectedType;
82
+ if (fieldType === "temporal") validateTimeScaleType(type);
83
+ else if (["ordinal", "nominal"].includes(fieldType)) {
84
+ if (!isDiscretePositionScaleType(type)) {
85
+ throw new Error(
86
+ `Scale type "${type}" is not valid for discrete position.`
87
+ );
88
+ }
89
+ }
90
+ else validateScaleTypeForRole(type, SCALE_ROLES.quantitativePosition);
91
+ const scale = {
92
+ id,
93
+ ...normalizeScaleDefinition({
94
+ type,
95
+ previous: existing,
96
+ patch: options,
97
+ defaults: {
98
+ ...(existing === undefined && defaults.nice !== undefined
99
+ ? { nice: defaults.nice }
100
+ : {}),
101
+ ...(existing === undefined && defaults.zero !== undefined
102
+ ? { zero: defaults.zero }
103
+ : {})
104
+ },
105
+ retainCoreOnTypeChange: true,
106
+ retainCompatibleOnTypeChange: true,
107
+ validateDomain: (_scaleType, value) =>
108
+ ["ordinal", "nominal"].includes(fieldType)
109
+ ? validateOrdinalDomain(value)
110
+ : validateScaleDomain(value),
111
+ validateRange: (_scaleType, value) => validateScaleRange(value)
112
+ })
113
+ };
114
+ return withScaleUnknown(scale, { ...existing, ...options }, channel);
115
+ }
116
+
117
+ export function resolveColorScaleDefinition(program, options) {
118
+ optionsObject(options);
119
+ validateKeys(options, COLOR_OPTIONS, "scale");
120
+ if (options.palette !== undefined && options.range !== undefined) {
121
+ throw new Error("Color scale cannot specify both palette and range.");
122
+ }
123
+ const id = validateUserId(options.id ?? "color", "Scale id");
124
+ const existing = findSemanticScale(program, id);
125
+ const range = options.palette === undefined
126
+ ? options.range
127
+ : { palette: options.palette };
128
+ return withScaleUnknown({
129
+ id,
130
+ type: validateOrdinalScaleType(options.type ?? existing?.type ?? "ordinal"),
131
+ domain: validateOrdinalDomain(options.domain ?? existing?.domain ?? "auto"),
132
+ range: validateColorRange(range ?? existing?.range ?? "auto")
133
+ }, { ...existing, ...options }, "color");
134
+ }
135
+
136
+ function continuousDomain(value, fieldType) {
137
+ if (value === "auto") return value;
138
+ if (!Array.isArray(value) || value.length !== 2) {
139
+ throw new TypeError("Continuous color domain must contain two values or auto.");
140
+ }
141
+ const normalized = value.map(item => {
142
+ if (fieldType === "quantitative") return item;
143
+ return typeof item === "string" ? Date.parse(item) : item;
144
+ });
145
+ if (!normalized.every(Number.isFinite) || normalized[0] === normalized[1]) {
146
+ throw new TypeError("Continuous color domain requires two distinct valid values.");
147
+ }
148
+ return normalized;
149
+ }
150
+
151
+ export function resolveSequentialColorScaleDefinition(
152
+ program,
153
+ fieldType,
154
+ options
155
+ ) {
156
+ optionsObject(options);
157
+ validateKeys(options, SEQUENTIAL_COLOR_OPTIONS, "scale");
158
+ if (options.palette !== undefined && options.range !== undefined) {
159
+ throw new Error("Color scale cannot specify both palette and range.");
160
+ }
161
+ const id = validateUserId(options.id ?? "color", "Scale id");
162
+ const existing = findSemanticScale(program, id);
163
+ const type = options.type ?? existing?.type ?? "sequential";
164
+ if (type !== "sequential") {
165
+ throw new Error(`Unsupported continuous color scale type "${type}".`);
166
+ }
167
+ for (const property of ["clamp", "reverse"]) {
168
+ if (options[property] !== undefined && typeof options[property] !== "boolean") {
169
+ throw new TypeError(`Scale ${property} must be a boolean.`);
170
+ }
171
+ }
172
+ const requestedRange = options.palette === undefined
173
+ ? options.range
174
+ : { palette: options.palette };
175
+ const scale = {
176
+ id,
177
+ type,
178
+ domain: continuousDomain(options.domain ?? existing?.domain ?? "auto", fieldType),
179
+ range: validateSequentialColorRange(
180
+ requestedRange ?? existing?.range ?? { palette: "viridis" }
181
+ ),
182
+ interpolate: validateContinuousColorInterpolation(
183
+ options.interpolate ?? existing?.interpolate ?? "rgb"
184
+ )
185
+ };
186
+ const clamp = options.clamp ?? existing?.clamp;
187
+ const reverse = options.reverse ?? existing?.reverse;
188
+ if (clamp !== undefined) scale.clamp = clamp;
189
+ if (reverse !== undefined) scale.reverse = reverse;
190
+ return withScaleUnknown(scale, { ...existing, ...options }, "color");
191
+ }
192
+
193
+ export function resolveQuantitativeColorScaleDefinition(
194
+ program,
195
+ fieldType,
196
+ options
197
+ ) {
198
+ optionsObject(options);
199
+ const type = options.type ?? findSemanticScale(
200
+ program,
201
+ options.id ?? "color"
202
+ )?.type ?? "sequential";
203
+ if (type === "sequential") {
204
+ return resolveSequentialColorScaleDefinition(program, fieldType, options);
205
+ }
206
+ validateKeys(options, [
207
+ ...COLOR_OPTIONS,
208
+ "clamp",
209
+ "reverse"
210
+ ], "scale");
211
+ if (fieldType !== "quantitative") {
212
+ throw new Error(`Scale type "${type}" requires quantitative color.`);
213
+ }
214
+ validateScaleTypeForRole(type, SCALE_ROLES.discretizedColor);
215
+ if (options.palette !== undefined && options.range !== undefined) {
216
+ throw new Error("Color scale cannot specify both palette and range.");
217
+ }
218
+ for (const property of ["clamp", "reverse"]) {
219
+ if (options[property] !== undefined && typeof options[property] !== "boolean") {
220
+ throw new TypeError(`Scale ${property} must be a boolean.`);
221
+ }
222
+ if (options[property] !== undefined) validateScalePropertyForType(type, property);
223
+ }
224
+ const id = validateUserId(options.id ?? "color", "Scale id");
225
+ const existing = findSemanticScale(program, id);
226
+ const requestedRange = options.palette === undefined
227
+ ? options.range
228
+ : { palette: options.palette };
229
+ const scale = {
230
+ id,
231
+ type,
232
+ domain: validateDiscretizedColorDomain(
233
+ type,
234
+ options.domain ?? (existing?.type === type ? existing.domain : "auto")
235
+ ),
236
+ range: validateDiscretizedColorRange(
237
+ requestedRange ?? (existing?.type === type ? existing.range : { palette: "viridis" })
238
+ )
239
+ };
240
+ const clamp = options.clamp ?? (existing?.type === type ? existing.clamp : undefined);
241
+ const reverse = options.reverse ?? (existing?.type === type ? existing.reverse : undefined);
242
+ if (clamp !== undefined) scale.clamp = clamp;
243
+ if (reverse !== undefined) scale.reverse = reverse;
244
+ return withScaleUnknown(scale, { ...existing, ...options }, "color");
245
+ }
246
+
247
+ export function resolveStrokeDashScaleDefinition(program, options) {
248
+ optionsObject(options);
249
+ validateKeys(options, [...BASE_OPTIONS, "unknown"], "scale");
250
+ const id = validateUserId(options.id ?? "strokeDash", "Scale id");
251
+ const existing = findSemanticScale(program, id);
252
+ return withScaleUnknown({
253
+ id,
254
+ type: validateOrdinalScaleType(options.type ?? existing?.type ?? "ordinal"),
255
+ domain: validateOrdinalDomain(options.domain ?? existing?.domain ?? "auto"),
256
+ range: validateStrokeDashRange(options.range ?? existing?.range ?? "auto")
257
+ }, { ...existing, ...options }, "strokeDash");
258
+ }
259
+
260
+ export function resolveAppearanceScaleDefinition(program, channel, options) {
261
+ optionsObject(options);
262
+ validateKeys(options, [...BASE_OPTIONS, "unknown"], "scale");
263
+ const id = validateUserId(options.id ?? channel, "Scale id");
264
+ const existing = findSemanticScale(program, id);
265
+ const shape = channel === "shape";
266
+ return withScaleUnknown({
267
+ id,
268
+ type: shape
269
+ ? validateOrdinalScaleType(options.type ?? existing?.type ?? "ordinal")
270
+ : validateLinearScaleType(options.type ?? existing?.type ?? "linear"),
271
+ domain: shape
272
+ ? validateOrdinalDomain(options.domain ?? existing?.domain ?? "auto")
273
+ : validateScaleDomain(options.domain ?? existing?.domain ?? "auto"),
274
+ range: shape
275
+ ? validateShapeRange(options.range ?? existing?.range ?? "auto")
276
+ : validateSizeRange(options.range ?? existing?.range ?? "auto")
277
+ }, { ...existing, ...options }, channel);
278
+ }
279
+
280
+ export function resolveOpacityScaleDefinition(program, options) {
281
+ optionsObject(options);
282
+ validateKeys(options, OPACITY_OPTIONS, "scale");
283
+ const id = validateUserId(options.id ?? "opacity", "Scale id");
284
+ const existing = findSemanticScale(program, id);
285
+ const type = validateLinearScaleType(options.type ?? existing?.type ?? "linear");
286
+ for (const property of ["nice", "zero", "clamp", "reverse"]) {
287
+ if (options[property] !== undefined && typeof options[property] !== "boolean") {
288
+ throw new TypeError(`Scale ${property} must be a boolean.`);
289
+ }
290
+ }
291
+ const scale = {
292
+ id,
293
+ type,
294
+ domain: validateScaleDomain(options.domain ?? existing?.domain ?? "auto"),
295
+ range: validateOpacityRange(options.range ?? existing?.range ?? "auto")
296
+ };
297
+ const nice = options.nice ?? existing?.nice;
298
+ const zero = options.zero ?? existing?.zero;
299
+ const clamp = options.clamp ?? existing?.clamp;
300
+ const reverse = options.reverse ?? existing?.reverse;
301
+ if (nice !== undefined) scale.nice = nice;
302
+ if (zero !== undefined) scale.zero = zero;
303
+ if (clamp !== undefined) scale.clamp = clamp;
304
+ if (reverse !== undefined) scale.reverse = reverse;
305
+ return withScaleUnknown(scale, { ...existing, ...options }, "opacity");
306
+ }
307
+
308
+ export function resolveOffsetScaleDefinition(program, options) {
309
+ optionsObject(options);
310
+ validateKeys(options, [...BASE_OPTIONS, "unknown"], "scale");
311
+ const id = validateUserId(options.id ?? "xOffset", "Scale id");
312
+ const existing = findSemanticScale(program, id);
313
+ return withScaleUnknown({
314
+ id,
315
+ type: validateOrdinalScaleType(options.type ?? existing?.type ?? "ordinal"),
316
+ domain: validateOrdinalDomain(options.domain ?? existing?.domain ?? "auto"),
317
+ range: validateScaleRange(options.range ?? existing?.range ?? "auto")
318
+ }, { ...existing, ...options }, "xOffset");
319
+ }