ggaction 0.0.17 → 0.0.19

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 (315) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +6 -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 +20 -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 +8 -7
  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/derived.js +11 -1
  35. package/src/actions/data/edit.js +3 -1
  36. package/src/actions/data/index.js +4 -0
  37. package/src/actions/data/regression.js +25 -16
  38. package/src/actions/data/regressionStatistics.js +73 -0
  39. package/src/actions/data/shared.js +8 -4
  40. package/src/actions/data/stack.js +3 -1
  41. package/src/actions/distributions/revision.js +24 -18
  42. package/src/actions/encodings/appearance.js +37 -29
  43. package/src/actions/encodings/barWidth.js +36 -11
  44. package/src/actions/encodings/channels.js +0 -1
  45. package/src/actions/encodings/color/continuous.js +1 -1
  46. package/src/actions/encodings/color/index.js +1 -1
  47. package/src/actions/encodings/color/policy.js +10 -3
  48. package/src/actions/encodings/density/resolve.js +1 -6
  49. package/src/actions/encodings/position/policies/arc.js +12 -10
  50. package/src/actions/encodings/position/policies/area.js +3 -3
  51. package/src/actions/encodings/position/policies/bar.js +15 -6
  52. package/src/actions/encodings/position/policies/line.js +17 -6
  53. package/src/actions/encodings/position/resolve.js +3 -2
  54. package/src/actions/encodings/ranged.js +17 -0
  55. package/src/actions/encodings/remove.js +14 -8
  56. package/src/actions/encodings/seriesLayout.js +1 -0
  57. package/src/actions/encodings/shared.js +26 -24
  58. package/src/actions/encodings/text.js +6 -3
  59. package/src/actions/errorBands/create.js +2 -2
  60. package/src/actions/errorBands/edit.js +2 -1
  61. package/src/actions/facets/actions.js +176 -247
  62. package/src/actions/facets/guides.js +2 -1
  63. package/src/actions/gradientPlots/resolve.js +2 -17
  64. package/src/actions/guides/applicability.js +1 -1
  65. package/src/actions/guides/axes/axes.js +30 -11
  66. package/src/actions/guides/axes/axis.js +72 -10
  67. package/src/actions/guides/axes/components.js +13 -13
  68. package/src/actions/guides/axes/edit.js +36 -24
  69. package/src/actions/guides/axes/labels.js +155 -71
  70. package/src/actions/guides/axes/lines.js +27 -24
  71. package/src/actions/guides/axes/parallel/lifecycle.js +1 -1
  72. package/src/actions/guides/axes/parallel/policy.js +8 -7
  73. package/src/actions/guides/axes/remove.js +13 -8
  74. package/src/actions/guides/axes/tickGroups.js +11 -7
  75. package/src/actions/guides/axes/ticks.js +36 -24
  76. package/src/actions/guides/axes/titles.js +107 -69
  77. package/src/actions/guides/basic.js +2 -0
  78. package/src/actions/guides/facadeAxes.js +33 -24
  79. package/src/actions/guides/facadeLegend.js +12 -4
  80. package/src/actions/guides/grids/grid.js +8 -2
  81. package/src/actions/guides/grids/resolve.js +102 -58
  82. package/src/actions/guides/legends/blocks.js +46 -20
  83. package/src/actions/guides/legends/categorical/actions.js +95 -19
  84. package/src/actions/guides/legends/categorical/components.js +15 -18
  85. package/src/actions/guides/legends/categorical/index.js +8 -0
  86. package/src/actions/guides/legends/categorical/layout.js +45 -20
  87. package/src/actions/guides/legends/categorical/options.js +39 -22
  88. package/src/actions/guides/legends/categorical/recipes.js +7 -3
  89. package/src/actions/guides/legends/categorical/resolve.js +11 -4
  90. package/src/actions/guides/legends/categorical/symbols.js +38 -18
  91. package/src/actions/guides/legends/categorical/validation.js +1 -6
  92. package/src/actions/guides/legends/colorSize.js +110 -0
  93. package/src/actions/guides/legends/combine.js +37 -0
  94. package/src/actions/guides/legends/continuous/colorSymbols.js +1 -0
  95. package/src/actions/guides/legends/continuous/common.js +1 -447
  96. package/src/actions/guides/legends/continuous/gradient.js +73 -46
  97. package/src/actions/guides/legends/continuous/interval.js +4 -4
  98. package/src/actions/guides/legends/continuous/opacity.js +33 -21
  99. package/src/actions/guides/legends/creation.js +2 -0
  100. package/src/actions/guides/legends/edit.js +1 -604
  101. package/src/actions/guides/legends/focused.js +8 -7
  102. package/src/actions/guides/legends/group.js +1 -0
  103. package/src/actions/guides/legends/lane.js +1 -516
  104. package/src/actions/guides/legends/lifecycle.js +2 -1
  105. package/src/actions/guides/legends/remove.js +9 -3
  106. package/src/actions/guides/legends/sampling.js +1 -1
  107. package/src/actions/guides/legends/size.js +65 -47
  108. package/src/actions/guides/legends/strokeWidth.js +13 -29
  109. package/src/actions/guides/legends/target.js +2 -79
  110. package/src/actions/guides/legends/transition.js +44 -11
  111. package/src/actions/guides/polar/axes/labels.js +4 -2
  112. package/src/actions/guides/polar/axes/titles.js +7 -5
  113. package/src/actions/guides/polar/resolve.js +6 -14
  114. package/src/actions/guides/tickValues.js +8 -5
  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 +10 -4
  120. package/src/actions/marks/bar/edit.js +29 -14
  121. package/src/actions/marks/bar/materialize.js +15 -7
  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/edit.js +1 -1
  125. package/src/actions/marks/point/materialize.js +5 -1
  126. package/src/actions/marks/rect/actions.js +3 -3
  127. package/src/actions/marks/remove.js +12 -10
  128. package/src/actions/marks/rule/actions.js +7 -4
  129. package/src/actions/marks/text/actions.js +17 -5
  130. package/src/actions/marks/text/layout.js +3 -0
  131. package/src/actions/primitives/createGraphics.js +3 -2
  132. package/src/actions/primitives/editGraphics.js +15 -11
  133. package/src/actions/primitives/graphicProperties.js +1 -6
  134. package/src/actions/primitives/semanticAction.js +27 -1
  135. package/src/actions/primitives/semanticValidation/guide.js +11 -1
  136. package/src/actions/primitives/semanticValidation/layer.js +8 -0
  137. package/src/actions/regression/create.js +1 -1
  138. package/src/actions/regression/edit.js +18 -6
  139. package/src/actions/regression/resolve.js +2 -6
  140. package/src/actions/scales/channels.js +9 -4
  141. package/src/actions/scales/consumers/families.js +16 -5
  142. package/src/actions/scales/create.js +10 -5
  143. package/src/actions/scales/definitions.js +1 -425
  144. package/src/actions/scales/edit.js +3 -3
  145. package/src/actions/scales/editPolicy.js +12 -3
  146. package/src/actions/scales/materialize.js +1 -1
  147. package/src/actions/scales/patch.js +1 -1
  148. package/src/actions/scales/preview.js +21 -1
  149. package/src/actions/scales/quantitativeColor.js +8 -3
  150. package/src/actions/selection/actions.js +1 -13
  151. package/src/actions/theme/reconcile.js +126 -45
  152. package/src/actions/theme/state.js +12 -0
  153. package/src/actions/titles/actions.js +5 -18
  154. package/src/actions/titles/resolve.js +41 -19
  155. package/src/actions/violinPlots/edit.js +3 -20
  156. package/src/core/compositionState.js +6 -3
  157. package/src/core/font.js +9 -2
  158. package/src/core/graphicProperties.js +1 -0
  159. package/src/core/optionRequirements.js +1 -0
  160. package/src/core/programState.js +4 -0
  161. package/src/core/resourceReferences.js +5 -2
  162. package/src/core/textMetricProfile.js +3 -2
  163. package/src/core/textMetrics.js +41 -4
  164. package/src/core/validation.js +35 -0
  165. package/src/core/vocabulary.js +7 -0
  166. package/src/grammar/aggregate.js +4 -18
  167. package/src/grammar/arcs.js +1 -386
  168. package/src/grammar/areaPaint.js +10 -0
  169. package/src/grammar/areaSeries.js +18 -10
  170. package/src/grammar/axisTextPlacement.js +1 -0
  171. package/src/grammar/bars/geometry.js +30 -3
  172. package/src/grammar/bars/policy.js +12 -1
  173. package/src/grammar/bin.js +2 -12
  174. package/src/grammar/bin2d.js +1 -6
  175. package/src/grammar/boxPlot.js +56 -9
  176. package/src/grammar/categoryOrder.js +1 -6
  177. package/src/grammar/colorSizeLegend.js +39 -0
  178. package/src/grammar/complete.js +15 -19
  179. package/src/grammar/computed.js +115 -26
  180. package/src/grammar/curveCommands.js +29 -16
  181. package/src/grammar/datasetSchema.js +13 -9
  182. package/src/grammar/displayLabels.js +36 -0
  183. package/src/grammar/ecdf.js +2 -9
  184. package/src/grammar/facets/dependencies.js +6 -2
  185. package/src/grammar/facets/guides.js +58 -30
  186. package/src/grammar/facets/index.js +2 -5
  187. package/src/grammar/facets/legendIntent.js +65 -0
  188. package/src/grammar/fold.js +2 -12
  189. package/src/grammar/gradientProfile.js +1 -6
  190. package/src/grammar/guideDash.js +1 -0
  191. package/src/grammar/impute.js +21 -48
  192. package/src/grammar/interval.js +2 -9
  193. package/src/grammar/legendBlocks.js +1 -0
  194. package/src/grammar/legendBorder.js +9 -0
  195. package/src/grammar/legendClip.js +19 -0
  196. package/src/grammar/legendCombinations.js +45 -0
  197. package/src/grammar/legendGradient.js +27 -0
  198. package/src/grammar/legendGroups.js +1 -0
  199. package/src/grammar/legendOpacity.js +6 -0
  200. package/src/grammar/legendTitleStyle.js +22 -0
  201. package/src/grammar/lineSeries.js +32 -17
  202. package/src/grammar/markLabels.js +5 -2
  203. package/src/grammar/normalize.js +4 -54
  204. package/src/grammar/numeric.js +1 -290
  205. package/src/grammar/parallelCoordinates.js +1 -6
  206. package/src/grammar/parseTime.js +41 -0
  207. package/src/grammar/pathCommands.js +11 -4
  208. package/src/grammar/pointShapes.js +16 -2
  209. package/src/grammar/polar.js +1 -184
  210. package/src/grammar/positionCompatibility.js +3 -3
  211. package/src/grammar/regression/derive.js +16 -2
  212. package/src/grammar/regression/models.js +142 -54
  213. package/src/grammar/regression/parameters.js +23 -4
  214. package/src/grammar/regression/statistics.js +64 -0
  215. package/src/grammar/roundedRect.js +42 -54
  216. package/src/grammar/scales/appearance.js +1 -206
  217. package/src/grammar/scales/color.js +1 -382
  218. package/src/grammar/scales/colorConsumers.js +5 -2
  219. package/src/grammar/scales/definition.js +1 -197
  220. package/src/grammar/scales/discretized.js +1 -195
  221. package/src/grammar/scales/size.js +1 -456
  222. package/src/grammar/scales/temporal.js +2 -13
  223. package/src/grammar/scales/transformed.js +1 -383
  224. package/src/grammar/scales/types.js +1 -1
  225. package/src/grammar/scales/validation.js +1 -66
  226. package/src/grammar/schemas/concreteGraphic.js +14 -0
  227. package/src/grammar/schemas/graphic.js +6 -0
  228. package/src/grammar/schemas/graphicBounds.js +31 -7
  229. package/src/grammar/schemas/semanticPath.js +19 -3
  230. package/src/grammar/stack.js +2 -10
  231. package/src/grammar/statisticalReference.js +1 -6
  232. package/src/grammar/summary.js +4 -27
  233. package/src/grammar/text.js +19 -0
  234. package/src/grammar/timeUnit.js +22 -11
  235. package/src/grammar/transformKeys.js +47 -0
  236. package/src/grammar/transformTopology.js +1 -0
  237. package/src/grammar/transforms.js +15 -2
  238. package/src/grammar/valueFormat.js +17 -5
  239. package/src/grammar/weightedStatistics.js +1 -6
  240. package/src/grammar/window.js +4 -58
  241. package/src/inspectionDescriptors.js +1 -1
  242. package/src/layout/canvas.js +1 -110
  243. package/src/layout/facets.js +1 -244
  244. package/src/layout/guideCollisions.js +9 -1
  245. package/src/layout/labels.js +1 -0
  246. package/src/layout/legend.js +13 -0
  247. package/src/layout/legendGroup.js +1 -0
  248. package/src/layout/legendItems.js +59 -15
  249. package/src/layout/legendLane.js +1 -441
  250. package/src/layout/legendText.js +1 -0
  251. package/src/layout/title.js +35 -13
  252. package/src/materialization/bars/aggregate.js +11 -5
  253. package/src/materialization/bars/histogram.js +4 -1
  254. package/src/materialization/bars/ranged.js +19 -6
  255. package/src/materialization/bars/resolve.js +22 -1
  256. package/src/materialization/compositionSnapshot.js +2 -7
  257. package/src/materialization/coordinateBounds.js +21 -0
  258. package/src/materialization/dependencies.js +4 -44
  259. package/src/materialization/facetGuides/edit.js +30 -0
  260. package/src/materialization/facetGuides/layout.js +1 -0
  261. package/src/materialization/facetGuides/owner.js +125 -0
  262. package/src/materialization/facetGuides/ownerLayout.js +71 -0
  263. package/src/materialization/facetGuides/placement.js +71 -14
  264. package/src/materialization/facetGuides/preparation.js +22 -7
  265. package/src/materialization/facetHeaders.js +1 -347
  266. package/src/materialization/facets.js +1 -185
  267. package/src/materialization/graphicHierarchy.js +5 -1
  268. package/src/materialization/graphicTranslation.js +73 -0
  269. package/src/materialization/guides/layout.js +1 -57
  270. package/src/materialization/guides/resources.js +1 -123
  271. package/src/materialization/layout.js +53 -0
  272. package/src/materialization/legendBorder.js +28 -0
  273. package/src/materialization/legendClip.js +76 -0
  274. package/src/materialization/legendGroups.js +1 -0
  275. package/src/materialization/legendText.js +1 -0
  276. package/src/materialization/legends.js +2 -2
  277. package/src/materialization/marks/capabilities.js +5 -6
  278. package/src/materialization/rect.js +1 -1
  279. package/src/materialization/scaleGuideDependencies.js +14 -15
  280. package/src/materialization/scales/map.js +6 -0
  281. package/src/materialization/scales/policies/arc.js +8 -7
  282. package/src/materialization/scales/policies/bar.js +19 -7
  283. package/src/materialization/scales/policies/series.js +2 -1
  284. package/src/materialization/scales/resolve.js +7 -3
  285. package/src/materialization/selection/items/arc.js +1 -1
  286. package/src/materialization/selection/items/bar.js +7 -6
  287. package/src/materialization/selection/items/path.js +7 -6
  288. package/src/materialization/selection/items/rect.js +2 -2
  289. package/src/materialization/text.js +13 -4
  290. package/src/materialization/textLines.js +15 -0
  291. package/src/materialization/typography.js +10 -2
  292. package/src/persistence/codec.js +37 -6
  293. package/src/persistence/validation.js +220 -0
  294. package/src/persistence.js +19 -6
  295. package/src/renderers/canvas/circle.js +6 -3
  296. package/src/renderers/canvas/fill.js +7 -1
  297. package/src/renderers/canvas/index.js +22 -11
  298. package/src/renderers/canvas/isolated.js +166 -0
  299. package/src/renderers/canvas/limits.js +3 -0
  300. package/src/renderers/canvas/line.js +1 -0
  301. package/src/renderers/canvas/path.js +1 -1
  302. package/src/renderers/canvas/rect.js +11 -8
  303. package/src/renderers/canvas/stroke.js +2 -1
  304. package/src/renderers/canvas/text.js +5 -3
  305. package/src/renderers/pdf.js +2 -2
  306. package/src/renderers/png.js +2 -2
  307. package/src/renderers/svg.js +14 -4
  308. package/src/selectors/axes.js +1 -0
  309. package/src/selectors/graphicCapacity.js +38 -0
  310. package/src/selectors/layers.js +19 -0
  311. package/src/theme/defaults.js +8 -2
  312. package/src/version.js +1 -1
  313. package/types/basic.d.ts +3 -0
  314. package/types/index.d.ts +12 -0
  315. package/types/program.d.ts +536 -129
@@ -1,447 +1 @@
1
- import { isPlainObject } from "../../../../core/immutable.js";
2
- import { resolveTextBounds } from "../../../../core/textMetrics.js";
3
- import {
4
- validateKeys,
5
- validateOptionObject,
6
- validateNonEmptyString,
7
- validateNonNegativeFinite,
8
- validatePositiveFinite,
9
- validateGeneratedItemLimit
10
- } from "../../../../core/validation.js";
11
- import { formatTimeTick } from "../../../../grammar/ticks.js";
12
- import {
13
- formatValue,
14
- validateValueFormat
15
- } from "../../../../grammar/valueFormat.js";
16
- import {
17
- formatDistinctNumericSamples,
18
- sampleNumericRange
19
- } from "../../../../grammar/numeric.js";
20
- import { resolveGraphicBounds } from "../../../../layout/canvas.js";
21
- import { isHorizontalEdgeLegend } from "../../../../layout/legendLane.js";
22
- import { DEFAULT_COLORS, DEFAULT_FONT_FAMILY } from
23
- "../../../../theme/defaults.js";
24
- import { findLayer } from "../../../../selectors/layers.js";
25
- import { isOpacityLegendLayer } from "../../../../materialization/legends.js";
26
- import { findCanvasGraphic } from
27
- "../../../../materialization/graphicHierarchy.js";
28
- import { normalizeLegendSampling } from "../sampling.js";
29
- import { editGraphicProperties } from "../../../primitives/graphicProperties.js";
30
-
31
- const OPTIONS = [
32
- "target", "channels", "position", "align", "offset", "title", "count",
33
- "values",
34
- "gradient", "symbol", "labels", "titleStyle", "itemGap", "border",
35
- "direction", "columns", "titlePosition"
36
- ];
37
- const TEXT_OPTIONS = [
38
- "offset", "color", "fontSize", "fontFamily", "fontWeight", "format"
39
- ];
40
- const BORDER_OPTIONS = [
41
- "color", "lineWidth", "padding", "background"
42
- ];
43
- const POSITIONS = ["right", "left", "top", "bottom"];
44
- const DEFAULT_LABELS = {
45
- offset: 12,
46
- color: DEFAULT_COLORS.text,
47
- fontSize: 12,
48
- fontFamily: DEFAULT_FONT_FAMILY,
49
- fontWeight: "normal"
50
- };
51
- const DEFAULT_TITLE = {
52
- color: DEFAULT_COLORS.text,
53
- fontSize: 13,
54
- fontFamily: DEFAULT_FONT_FAMILY,
55
- fontWeight: 600
56
- };
57
- const DEFAULT_BORDER = {
58
- color: DEFAULT_COLORS.border,
59
- lineWidth: 1,
60
- padding: 12,
61
- background: "transparent"
62
- };
63
-
64
- export const validatePositive = validatePositiveFinite;
65
- export const validateNonNegative = validateNonNegativeFinite;
66
-
67
- export { editGraphicProperties };
68
-
69
- export function normalizeLegendTextOptions(value, label, defaults) {
70
- if (value === undefined) return { ...defaults };
71
- if (!isPlainObject(value)) {
72
- throw new TypeError(`${label} must be a plain object.`);
73
- }
74
- validateKeys(value, TEXT_OPTIONS, label);
75
- const result = { ...defaults, ...value };
76
- if (Object.hasOwn(result, "offset")) {
77
- validateNonNegative(result.offset, `${label} offset`);
78
- }
79
- validatePositive(result.fontSize, `${label} fontSize`);
80
- for (const key of ["color", "fontFamily"]) {
81
- validateNonEmptyString(result[key], `${label} ${key}`);
82
- }
83
- if (
84
- typeof result.fontWeight !== "string" &&
85
- !Number.isFinite(result.fontWeight)
86
- ) {
87
- throw new TypeError(`${label} fontWeight must be a string or finite number.`);
88
- }
89
- if (Object.hasOwn(result, "format")) {
90
- result.format = validateValueFormat(result.format, `${label} format`);
91
- }
92
- return result;
93
- }
94
-
95
- export function normalizeLegendTitleOptions(value, label, defaults) {
96
- if (value !== undefined) {
97
- validateOptionObject(value, ["color", "fontSize", "fontFamily", "fontWeight"], label);
98
- }
99
- return normalizeLegendTextOptions(value, label, defaults);
100
- }
101
-
102
- export function normalizeLegendBorder(value) {
103
- if (value === undefined || value === false) return false;
104
- if (value !== true && !isPlainObject(value)) {
105
- throw new TypeError("createLegend.border must be a boolean or plain object.");
106
- }
107
- if (value !== true) validateKeys(value, BORDER_OPTIONS, "createLegend.border");
108
- const border = { ...DEFAULT_BORDER, ...(value === true ? {} : value) };
109
- for (const key of ["color", "background"]) {
110
- validateNonEmptyString(border[key], `Legend border ${key}`);
111
- }
112
- validateNonNegative(border.lineWidth, "Legend border lineWidth");
113
- validateNonNegative(border.padding, "Legend border padding");
114
- return border;
115
- }
116
-
117
- export function normalizeContinuousLegend(args, kind) {
118
- validateOptionObject(args, OPTIONS, "createLegend");
119
- const position = args.position ?? "right";
120
- if (!POSITIONS.includes(position)) {
121
- throw new Error(`Unsupported legend position "${position}".`);
122
- }
123
- const align = args.align ?? "center";
124
- if (!["left", "center", "right"].includes(align)) {
125
- throw new Error(`Unsupported legend alignment "${align}".`);
126
- }
127
- if (["left", "right"].includes(position) && align !== "center") {
128
- throw new Error("Side continuous legends require center alignment.");
129
- }
130
- if (kind === "gradient" && Object.hasOwn(args, "values")) {
131
- throw new Error("Gradient legends do not support exact sample values.");
132
- }
133
- const sampling = kind === "opacity"
134
- ? normalizeLegendSampling(args, {
135
- operation: "create",
136
- label: "Opacity legend"
137
- })
138
- : undefined;
139
- const count = kind === "gradient" ? args.count ?? 5 : undefined;
140
- if (kind === "gradient") {
141
- if (!Number.isInteger(count) || count < 2) {
142
- throw new RangeError(
143
- "Continuous legend count must be an integer of at least 2."
144
- );
145
- }
146
- validateGeneratedItemLimit(count, "Continuous legend count");
147
- }
148
- const offset = args.offset ?? 30;
149
- validateNonNegative(offset, "Legend offset");
150
- const titlePosition = args.titlePosition ?? "top";
151
- if (!["top", "left"].includes(titlePosition)) {
152
- throw new Error(`Unsupported legend titlePosition "${titlePosition}".`);
153
- }
154
- if (
155
- titlePosition === "left" &&
156
- (!["top", "bottom"].includes(position) || kind !== "opacity")
157
- ) {
158
- throw new Error(
159
- "Only horizontal opacity legends support left titlePosition."
160
- );
161
- }
162
- const itemGap = args.itemGap ?? (titlePosition === "left" ? 20 : 28);
163
- validatePositive(itemGap, "Legend itemGap");
164
- if (args.title !== undefined) validateNonEmptyString(args.title, "Legend title");
165
- if (kind === "gradient") {
166
- for (const key of ["symbol", "columns", "direction", "itemGap"]) {
167
- if (Object.hasOwn(args, key)) {
168
- throw new Error(`Gradient legend does not accept ${key}.`);
169
- }
170
- }
171
- } else {
172
- for (const key of ["columns", "direction", "gradient"]) {
173
- if (Object.hasOwn(args, key)) {
174
- throw new Error(`Opacity legend does not accept ${key}.`);
175
- }
176
- }
177
- }
178
- return {
179
- target: args.target,
180
- position,
181
- align,
182
- offset,
183
- ...(kind === "gradient" ? { count } : { sampling }),
184
- title: args.title,
185
- inferredTitle: args.title === undefined,
186
- labels: normalizeLegendTextOptions(
187
- args.labels,
188
- "createLegend.labels",
189
- titlePosition === "left"
190
- ? { ...DEFAULT_LABELS, offset: 8 }
191
- : DEFAULT_LABELS
192
- ),
193
- titleStyle: normalizeLegendTitleOptions(
194
- args.titleStyle,
195
- "createLegend.titleStyle",
196
- DEFAULT_TITLE
197
- ),
198
- itemGap,
199
- titlePosition,
200
- border: normalizeLegendBorder(args.border)
201
- };
202
- }
203
-
204
- export function selectLegendLayer(program, requested, predicate) {
205
- const candidates = program.semanticSpec.layers.filter(predicate);
206
- if (requested === undefined) return candidates.length === 1
207
- ? candidates[0]
208
- : undefined;
209
- const candidate = findLayer(program, requested);
210
- return candidates.includes(candidate) ? candidate : undefined;
211
- }
212
-
213
- export function resolveContinuousLegendLayer(program, requested, channel) {
214
- const layer = selectLegendLayer(
215
- program,
216
- requested,
217
- channel === "opacity" ? isOpacityLegendLayer : candidate =>
218
- candidate.mark?.type === "point" && candidate.encoding?.[channel]?.scale !== undefined
219
- );
220
- if (layer === undefined) {
221
- throw new Error(
222
- requested === undefined
223
- ? `${channel} legend requires one eligible ${channel === "opacity" ? "point or line" : "point"} mark.`
224
- : `Unknown ${channel} legend target "${requested}".`
225
- );
226
- }
227
- return layer;
228
- }
229
-
230
- export function resolveContinuousColorLayer(program, requested, channel = "color") {
231
- const layer = selectLegendLayer(
232
- program,
233
- requested,
234
- candidate => (channel === "stroke"
235
- ? ["point", "line", "area", "bar", "rect", "arc", "rule", "tick"]
236
- : ["point", "bar", "rect"]
237
- ).includes(candidate.mark?.type) &&
238
- candidate.encoding?.[channel]?.scale !== undefined
239
- );
240
- if (layer === undefined) {
241
- throw new Error(
242
- requested === undefined
243
- ? channel === "color"
244
- ? "color legend requires one eligible point, bar, or rect mark."
245
- : "stroke legend requires one eligible mark."
246
- : `Unknown ${channel} legend target "${requested}".`
247
- );
248
- }
249
- return layer;
250
- }
251
-
252
- export function requireResolvedLegendScale(program, id, type) {
253
- const scale = program.resolvedScales[id];
254
- if (scale?.type !== type) {
255
- throw new Error(`Legend requires resolved ${type} scale "${id}".`);
256
- }
257
- return scale;
258
- }
259
-
260
- export function resolveContinuousBounds(
261
- program,
262
- message = "Continuous legend layout requires Canvas bounds."
263
- ) {
264
- const plot = resolveGraphicBounds(program);
265
- const canvas = findCanvasGraphic(program);
266
- if (
267
- plot === undefined ||
268
- ![plot.x, plot.y, plot.width, plot.height].every(Number.isFinite) ||
269
- canvas?.type !== "canvas" ||
270
- !Number.isFinite(canvas.properties.width) ||
271
- !Number.isFinite(canvas.properties.height)
272
- ) {
273
- throw new Error(message);
274
- }
275
- return { plot, canvas: canvas.properties };
276
- }
277
-
278
- export function sampleContinuousValues(domain, count) {
279
- return sampleNumericRange(
280
- domain[0],
281
- domain[1],
282
- count,
283
- "Continuous legend domain"
284
- );
285
- }
286
-
287
- export function formatContinuousValues(values, domain, fieldType, format = "auto") {
288
- const resolved = validateValueFormat(format, "Legend label format");
289
- if (resolved === "auto") {
290
- return fieldType === "temporal"
291
- ? values.map(value => formatTimeTick(value, domain))
292
- : formatDistinctNumericSamples(values);
293
- }
294
- return values.map(value => formatValue(value, {
295
- format: resolved,
296
- valueType: fieldType === "temporal" ? "temporal" : "quantitative",
297
- label: "Legend label format"
298
- }));
299
- }
300
-
301
- export function styleContinuousText(
302
- program,
303
- id,
304
- style,
305
- { align = "left" } = {}
306
- ) {
307
- return editGraphicProperties(program, id, {
308
- fill: style.color,
309
- fontSize: style.fontSize,
310
- fontFamily: style.fontFamily,
311
- fontWeight: style.fontWeight,
312
- textAlign: align,
313
- textBaseline: "middle"
314
- });
315
- }
316
-
317
- export function resolveLegendTextBounds(position, text, style, profile) {
318
- return resolveTextBounds({
319
- x: position.x,
320
- y: position.y,
321
- text: String(text),
322
- ...style,
323
- textAlign: position.align ?? "left",
324
- textBaseline: "middle"
325
- }, profile);
326
- }
327
-
328
- export function assertLegendBoundsInsideCanvas(bounds, canvas, label, config) {
329
- // Horizontal content is intrinsic until the shared lane places its actual
330
- // concrete bounds. Only that final placement can determine Canvas fit.
331
- if (isHorizontalEdgeLegend(config)) return;
332
- if (bounds.some(item =>
333
- item.left < 0 || item.right > canvas.width ||
334
- item.top < 0 || item.bottom > canvas.height
335
- )) {
336
- throw new Error(`${label} requires more Canvas margin space.`);
337
- }
338
- }
339
-
340
- export function resolveLegendBackgroundFromBounds(
341
- bounds,
342
- border,
343
- canvas,
344
- label,
345
- config
346
- ) {
347
- if (border === false) return undefined;
348
- const strokeExtent = border.lineWidth / 2;
349
- let x = Infinity;
350
- let y = Infinity;
351
- let right = -Infinity;
352
- let bottom = -Infinity;
353
- for (const item of bounds) {
354
- x = Math.min(x, item.left);
355
- y = Math.min(y, item.top);
356
- right = Math.max(right, item.right);
357
- bottom = Math.max(bottom, item.bottom);
358
- }
359
- x -= border.padding;
360
- y -= border.padding;
361
- right += border.padding;
362
- bottom += border.padding;
363
- if (!isHorizontalEdgeLegend(config) && (
364
- x - strokeExtent < 0 || y - strokeExtent < 0 ||
365
- right + strokeExtent > canvas.width ||
366
- bottom + strokeExtent > canvas.height
367
- )) {
368
- throw new Error(`${label} background requires more Canvas margin space.`);
369
- }
370
- return { x, y, width: right - x, height: bottom - y };
371
- }
372
-
373
- export function editLegendBackground(program, id, bounds, border) {
374
- if (bounds === undefined) return program;
375
- return editGraphicProperties(program, id, {
376
- ...bounds,
377
- fill: border.background,
378
- stroke: border.color,
379
- strokeWidth: border.lineWidth
380
- });
381
- }
382
-
383
- export function normalizeItemLegendLayout(args) {
384
- const position = args.position ?? "right";
385
- if (!["right", "left", "top", "bottom"].includes(position)) throw new Error(`Unsupported legend position "${position}".`);
386
- const side = ["left", "right"].includes(position);
387
- const layout = args.layout === undefined ? "edge" : args.layout;
388
- if (layout !== "edge") throw new Error('This legend requires layout "edge".');
389
- const align = args.align ?? "center";
390
- if (!["left", "center", "right"].includes(align)) throw new Error(`Unsupported legend alignment "${align}".`);
391
- const direction = args.direction ?? (side ? "vertical" : "horizontal");
392
- if (!["horizontal", "vertical"].includes(direction)) throw new Error(`Unsupported legend direction "${direction}".`);
393
- const columns = args.columns;
394
- if (columns !== undefined && (!Number.isInteger(columns) || columns < 1)) throw new RangeError("Legend columns must be a positive integer.");
395
- const titlePosition = args.titlePosition ?? "top";
396
- if (!["top", "left"].includes(titlePosition)) throw new Error(`Unsupported legend titlePosition "${titlePosition}".`);
397
- if (side && (direction !== "vertical" || align !== "center" || (columns !== undefined && columns !== 1) || titlePosition !== "top")) {
398
- throw new Error("Side item legends require vertical direction, center alignment, top title and one column.");
399
- }
400
- const offset = args.offset ?? 30;
401
- const itemGap = args.itemGap ?? 28;
402
- validateNonNegative(offset, "Legend offset");
403
- validatePositive(itemGap, "Legend itemGap");
404
- return { position, layout, align, direction, columns, titlePosition, offset, itemGap };
405
- }
406
-
407
- export function materializeItemLegend(program, kind, config, layout, {
408
- symbols, optional = {}, text = layout.labels,
409
- labels = layout.config.labels, titleStyle = layout.config.titleStyle
410
- }) {
411
- const prefix = `${kind}Legend`;
412
- let next = program
413
- .editSemantic({ property: `guide.legend.${kind}.scale`, value: config.scale })
414
- .editSemantic({ property: `guide.legend.${kind}.title`, value: config.title })
415
- ._withLegendConfig(kind, config);
416
- next = editGraphicProperties(next, `${prefix}Symbols`, {
417
- length: text.length,
418
- ...symbols
419
- });
420
- next = editGraphicProperties(next, `${prefix}Labels`, {
421
- length: text.length, x: layout.labelX, y: layout.itemY, text
422
- });
423
- next = editLegendBackground(next, `${prefix}Background`, layout.background, config.border);
424
- next = editGraphicProperties(next, `${prefix}Symbols`, Object.fromEntries(
425
- Object.entries(optional).filter(([, value]) => value !== undefined)
426
- ));
427
- next = styleContinuousText(next, `${prefix}Labels`, labels);
428
- const effective = layout.config;
429
- if (effective.titleVisible === false) return next;
430
- next = editGraphicProperties(next, `${prefix}Title`, {
431
- x: layout.title.x, y: layout.title.y, text: effective.title
432
- });
433
- return styleContinuousText(next, `${prefix}Title`, titleStyle, { align: layout.title.align });
434
- }
435
-
436
- export function createItemLegendGraphics(program, kind, config, count, type, placement) {
437
- const prefix = `${kind}Legend`;
438
- let next = program
439
- .editSemantic({ property: `guide.legend.${kind}.scale`, value: config.scale })
440
- .editSemantic({ property: `guide.legend.${kind}.title`, value: config.title })
441
- ._withLegendConfig(kind, config);
442
- if (config.border !== false) next = next.createGraphics({ id: `${prefix}Background`, type: "rect", ...placement });
443
- next = next.createGraphics({ id: `${prefix}Symbols`, type, length: count, ...placement })
444
- .createGraphics({ id: `${prefix}Labels`, type: "text", length: count, ...placement });
445
- if (config.titleVisible !== false) next = next.createGraphics({ id: `${prefix}Title`, type: "text", ...placement });
446
- return next;
447
- }
1
+ import{normalizeLegendClipHeight as O}from"../../../../grammar/legendClip.js";import{applyLegendClipGraphics as $}from"../../../../materialization/legendClip.js";import{normalizeGradientDirection as H}from"../../../../grammar/legendGradient.js";import{normalizeLegendBorderDash as R}from"../../../../grammar/legendBorder.js";import{materializeLegendBorder as M}from"../../../../materialization/legendBorder.js";import{LEGEND_TITLE_STYLE_KEYS as q,mergeLegendTitleStyle as V,validateLegendTitleAlignment as v}from"../../../../grammar/legendTitleStyle.js";import{PLOT_LEGEND_POSITIONS as Y,isPlotLegend as m}from"../../../../layout/legend.js";import{isContinuousColorScaleType as K}from"../../../../grammar/scales/types.js";import{isPlainObject as I}from"../../../../core/immutable.js";import{resolveTextBounds as X}from"../../../../core/textMetrics.js";import{resolveLegendTitleText as J}from"../../../../layout/legendText.js";import{materializeLegendTitleLines as Q}from"../../../../materialization/legendText.js";import{validateKeys as G,validateFontWeight as Z,validateOptionObject as z,validateNonEmptyString as y,validateNonNegativeFinite as ee,validatePositiveFinite as te,validateGeneratedItemLimit as ne}from"../../../../core/validation.js";import{formatTimeTick as ie}from"../../../../grammar/ticks.js";import{formatValue as oe,validateValueFormat as C}from"../../../../grammar/valueFormat.js";import{formatDistinctNumericSamples as re,sampleNumericRange as le}from"../../../../grammar/numeric.js";import{resolveGraphicBounds as de,canvasOverflowError as _}from"../../../../layout/canvas.js";import{isHorizontalEdgeLegend as N}from"../../../../layout/legendLane.js";import{DEFAULT_COLORS as h,DEFAULT_FONT_FAMILY as F,DEFAULT_VERTICAL_LEGEND_ITEM_GAP as w}from"../../../../theme/defaults.js";import{findLayer as se}from"../../../../selectors/layers.js";import{isOpacityLegendLayer as ae}from"../../../../materialization/legends.js";import{findCanvasGraphic as ce}from"../../../../materialization/graphicHierarchy.js";import{normalizeLegendSampling as fe}from"../sampling.js";import{editGraphicProperties as p}from"../../../primitives/graphicProperties.js";const pe=["clipHeight","target","channels","position","align","offset","title","count","values","gradient","symbol","labels","titleStyle","itemGap","border","direction","columns","titlePosition"],Re=Object.freeze(["clipHeight","target","count","values","position","layout","align","direction","columns","titlePosition","offset","itemGap","title","labels","titleStyle","border","overflow"]),ue=["offset","color","fontSize","fontFamily","fontWeight","format"],me=["color","lineWidth","padding","background","strokeDash"],P=["right","left","top","bottom",...Y],E={offset:12,color:h.text,fontSize:12,fontFamily:F,fontWeight:"normal"},D={color:h.text,fontSize:13,fontFamily:F,fontWeight:600},ge=Object.freeze(E),he=Object.freeze({...D,color:h.strongText});function B(e,t){return e!==void 0&&e!==!1&&y(e,"Legend title"),{title:e===!1||e===void 0?t:e,inferredTitle:e===!1||e===void 0,titleVisible:e!==!1}}function Me(e,t,n){const i=["right","left"].includes(e.position??"right")||m(e),r=(e.direction??(i?"vertical":"horizontal"))==="vertical"?w:n;return{...e.clipHeight===void 0?{}:{clipHeight:O(e.clipHeight)},...ye({...e,itemGap:e.itemGap??r}),...B(e.title,t.field),labels:T(e.labels,"createLegend.labels",ge),titleStyle:A(e.titleStyle,"createLegend.titleStyle",he),border:j(e.border)}}const Le={color:h.border,lineWidth:1,padding:12,background:"transparent"},x=te,g=ee;function T(e,t,n){if(e===void 0)return{...n};if(!I(e))throw new TypeError(`${t} must be a plain object.`);G(e,ue,t);const i={...n,...e};Object.hasOwn(i,"offset")&&g(i.offset,`${t} offset`),x(i.fontSize,`${t} fontSize`);for(const o of["color","fontFamily"])y(i[o],`${t} ${o}`);return Z(i.fontWeight,`${t} fontWeight`,"finite number"),Object.hasOwn(i,"format")&&(i.format=C(i.format,`${t} format`)),i}function A(e,t,n,i=!1){e!==void 0&&z(e,q,t);const o=V(n,e,{edit:i}),{align:r,...d}=o;return{...T(d,t,{}),...r===void 0?{}:{align:r}}}function j(e){if(e===void 0||e===!1)return!1;if(e!==!0&&!I(e))throw new TypeError("createLegend.border must be a boolean or plain object.");e!==!0&&G(e,me,"createLegend.border");const t={...Le,...e===!0?{}:e};for(const i of["color","background"])y(t[i],`Legend border ${i}`);g(t.lineWidth,"Legend border lineWidth"),g(t.padding,"Legend border padding");const n=R(t.strokeDash);return{...t,...n===void 0?{}:{strokeDash:n}}}function qe(e,t){z(e,pe,"createLegend");const n=e.position??"right";if(!P.includes(n))throw new Error(`Unsupported legend position "${n}".`);const i=e.align??"center";if(!["left","center","right"].includes(i))throw new Error(`Unsupported legend alignment "${i}".`);if(["left","right"].includes(n)&&i!=="center")throw new Error("Side continuous legends require center alignment.");if(t==="gradient"&&Object.hasOwn(e,"values"))throw new Error("Gradient legends do not support exact sample values.");const o=t==="opacity"?fe(e,{operation:"create",label:"Opacity legend"}):void 0,r=t==="gradient"?e.count??5:void 0;if(t==="gradient"){if(!Number.isInteger(r)||r<2)throw new RangeError("Continuous legend count must be an integer of at least 2.");ne(r,"Continuous legend count")}const d=e.offset??(m({position:n})?8:30);g(d,"Legend offset");const l=e.titlePosition??"top";if(v(e.titleStyle,l),!(t==="gradient"?["top","left","bottom"]:["top","left"]).includes(l))throw new Error(`Unsupported legend titlePosition "${l}".`);if(l==="left"&&(!["top","bottom"].includes(n)||t!=="opacity"))throw new Error("Only horizontal opacity legends support left titlePosition.");const a=e.itemGap??(t==="opacity"&&(["right","left"].includes(n)||m({position:n}))?w:l==="left"?20:28);if(x(a,"Legend itemGap"),t==="gradient"){for(const s of["symbol","columns","itemGap"])if(Object.hasOwn(e,s))throw new Error(`Gradient legend does not accept ${s}.`)}else for(const s of["columns","direction","gradient"])if(Object.hasOwn(e,s))throw new Error(`Opacity legend does not accept ${s}.`);return{...e.clipHeight===void 0?{}:{clipHeight:O(e.clipHeight)},target:e.target,position:n,align:i,offset:d,...t==="gradient"?{count:r,...e.direction===void 0?{}:{gradientDirection:H(e.direction)}}:{sampling:o},...B(e.title),labels:T(e.labels,"createLegend.labels",l==="left"?{...E,offset:8}:E),titleStyle:A(e.titleStyle,"createLegend.titleStyle",D),itemGap:a,titlePosition:l,border:j(e.border)}}function U(e,t,n){const i=e.semanticSpec.layers.filter(n);if(t===void 0)return i.length===1?i[0]:void 0;const o=se(e,t);return i.includes(o)?o:void 0}function Ve(e,t,n){const i=U(e,t,n==="opacity"?ae:o=>o.mark?.type==="point"&&o.encoding?.[n]?.scale!==void 0);if(i===void 0)throw new Error(t===void 0?`${n} legend requires one eligible ${n==="opacity"?"point, line or bar":"point"} mark.`:`Unknown ${n} legend target "${t}".`);return i}function Ye(e,t,n="color"){const i=U(e,t,o=>(n==="stroke"?["point","line","area","bar","rect","arc","rule","tick"]:["point","bar","rect","text"]).includes(o.mark?.type)&&o.encoding?.[n]?.scale!==void 0);if(i===void 0)throw new Error(t===void 0?n==="color"?"color legend requires one eligible point, bar, or rect mark.":"stroke legend requires one eligible mark.":`Unknown ${n} legend target "${t}".`);return i}function Ke(e,t,n){const i=e.resolvedScales[t];if(n==="sequential"?!K(i?.type):i?.type!==n)throw new Error(`Legend requires resolved ${n} scale "${t}".`);return i}function Xe(e,t="Continuous legend layout requires Canvas bounds."){const n=de(e),i=ce(e);if(n===void 0||![n.x,n.y,n.width,n.height].every(Number.isFinite)||i?.type!=="canvas"||!Number.isFinite(i.properties.width)||!Number.isFinite(i.properties.height))throw new Error(t);return{plot:n,canvas:i.properties}}function Je(e,t){return le(e[0],e[1],t,"Continuous legend domain")}function Qe(e,t,n,i="auto"){const o=C(i,"Legend label format");return o==="auto"?n==="temporal"?e.map(r=>ie(r,t)):re(e):e.map(r=>oe(r,{format:o,valueType:n==="temporal"?"temporal":"quantitative",label:"Legend label format"}))}function k(e,t,n,{align:i="left"}={}){const o=p(e,t,{fill:n.color,fontSize:n.fontSize,fontFamily:n.fontFamily,fontWeight:n.fontWeight,textAlign:i,textBaseline:"middle"});return t.endsWith("Title")?Q(o,t,o.graphicSpec.objects[t].properties.text,n):o}function Ze(e,t,n,i,{hardBreaks:o=!1}={}){return X({x:e.x,y:e.y,text:String(t),...n,...o?{lines:J(t,n,i).lines}:{},textAlign:e.align??"left",textBaseline:"middle"},i)}function et(e,t,n,i){if(!(i?.groupIntrinsic||N(i)||["left","right"].includes(i?.position))&&e.some(o=>o.left<0||o.right>t.width||o.top<0||o.bottom>t.height))throw _(`${n} requires more Canvas margin space.`,e,t)}function tt(e,t,n,i,o){if(t===!1)return;const r=t.lineWidth/2;let d=1/0,l=1/0,a=-1/0,s=-1/0;for(const f of e)d=Math.min(d,f.left),l=Math.min(l,f.top),a=Math.max(a,f.right),s=Math.max(s,f.bottom);if(d-=t.padding,l-=t.padding,a+=t.padding,s+=t.padding,!o?.groupIntrinsic&&!N(o)&&!["left","right"].includes(o?.position)&&(d-r<0||l-r<0||a+r>n.width||s+r>n.height))throw _(`${i} background requires more Canvas margin space.`,[{left:d-r,right:a+r,top:l-r,bottom:s+r}],n);return{x:d,y:l,width:a-d,height:s-l}}function be(e,t,n,i){return n===void 0?e:M(e,t,n,i)}function ye(e,{multiColumnSide:t=!1}={}){const n=e.position??"right";if(!P.includes(n))throw new Error(`Unsupported legend position "${n}".`);const i=["left","right"].includes(n)||m({position:n}),o=e.layout===void 0?"edge":e.layout;if(o!=="edge")throw new Error('This legend requires layout "edge".');const r=e.align??"center";if(!["left","center","right"].includes(r))throw new Error(`Unsupported legend alignment "${r}".`);const d=e.direction??(i?"vertical":"horizontal");if(!["horizontal","vertical"].includes(d))throw new Error(`Unsupported legend direction "${d}".`);const l=e.columns;if(l!==void 0&&(!Number.isInteger(l)||l<1))throw new RangeError("Legend columns must be a positive integer.");const a=e.titlePosition??"top";if(v(e.titleStyle,a),!["top","left"].includes(a))throw new Error(`Unsupported legend titlePosition "${a}".`);if(i&&(r!=="center"||a!=="top"||!t&&(d!=="vertical"||l!==void 0&&l!==1)))throw new Error("Side item legends require vertical direction, center alignment, top title and one column.");const s=e.offset??(m({position:n})?8:30),f=e.itemGap??(d==="vertical"?w:28);return g(s,"Legend offset"),x(f,"Legend itemGap"),{position:n,layout:o,align:r,direction:d,columns:l,titlePosition:a,offset:s,itemGap:f}}function nt(e,t,n,i,{symbols:o,optional:r={},text:d=i.labels,labels:l=i.config.labels,titleStyle:a=i.config.titleStyle}){const s=`${t}Legend`,f=Object.values(o).find(Array.isArray)?.length??d.length;let c=e.editSemantic({property:`guide.legend.${t}.scale`,value:n.scale}).editSemantic({property:`guide.legend.${t}.title`,value:n.title})._withLegendConfig(t,n);c=p(c,`${s}Symbols`,{length:f,...o});const L=i.labelItemIndices,b=u=>L===void 0||!Array.isArray(u)?u:L.map(W=>u[W]);c=p(c,`${s}Labels`,{length:L?.length??d.length,x:b(i.labelX),y:b(i.itemY),text:b(d)}),c=be(c,`${s}Background`,i.background,n.border),c=p(c,`${s}Symbols`,Object.fromEntries(Object.entries(r).filter(([,u])=>u!==void 0))),c=k(c,`${s}Labels`,l);const S=i.config;return S.titleVisible===!1?$(c):(c=p(c,`${s}Title`,{x:i.title.x,y:i.title.y,text:S.title}),$(k(c,`${s}Title`,a,{align:i.title.align})))}function it(e,t,n,i,o,r){const d=`${t}Legend`;let l=e.editSemantic({property:`guide.legend.${t}.scale`,value:n.scale}).editSemantic({property:`guide.legend.${t}.title`,value:n.title})._withLegendConfig(t,n);return n.border!==!1&&(l=l.createGraphics({id:`${d}Background`,type:"rect",...r})),l=l.createGraphics({id:`${d}Symbols`,type:o,length:i,...r}).createGraphics({id:`${d}Labels`,type:"text",length:i,...r}),n.titleVisible!==!1&&(l=l.createGraphics({id:`${d}Title`,type:"text",...r})),l}export{ge as ITEM_LEGEND_LABELS,Re as ITEM_LEGEND_OPTIONS,he as ITEM_LEGEND_TITLE_STYLE,et as assertLegendBoundsInsideCanvas,it as createItemLegendGraphics,p as editGraphicProperties,be as editLegendBackground,Qe as formatContinuousValues,nt as materializeItemLegend,qe as normalizeContinuousLegend,B as normalizeInitialLegendTitle,Me as normalizeItemLegendConfig,ye as normalizeItemLegendLayout,j as normalizeLegendBorder,T as normalizeLegendTextOptions,A as normalizeLegendTitleOptions,Ke as requireResolvedLegendScale,Xe as resolveContinuousBounds,Ye as resolveContinuousColorLayer,Ve as resolveContinuousLegendLayer,tt as resolveLegendBackgroundFromBounds,Ze as resolveLegendTextBounds,Je as sampleContinuousValues,U as selectLegendLayer,k as styleContinuousText,g as validateNonNegative,x as validatePositive};