ggaction 0.0.13 → 0.0.16

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 (359) hide show
  1. package/CHANGELOG.md +146 -0
  2. package/README.md +41 -12
  3. package/knowledge/action-cards.json +1 -1
  4. package/knowledge/intent-taxonomy.json +1 -1
  5. package/knowledge/mcp-resources.json +1 -1
  6. package/knowledge/task-packet.schema.json +1 -1
  7. package/knowledge/task-resolver.js +1 -1
  8. package/package.json +39 -7
  9. package/src/BasicChartProgram.js +3 -1
  10. package/src/ChartProgram.js +2 -0
  11. package/src/accessibility.js +157 -0
  12. package/src/actions/basic.js +2 -2
  13. package/src/actions/boxPlots/components.js +20 -12
  14. package/src/actions/boxPlots/create.js +4 -5
  15. package/src/actions/boxPlots/edit.js +34 -9
  16. package/src/actions/boxPlots/materialize.js +12 -4
  17. package/src/actions/boxPlots/options.js +48 -16
  18. package/src/actions/canvas/actions.js +3 -3
  19. package/src/actions/canvas/fitting.js +1 -1
  20. package/src/actions/categoryOrder/index.js +21 -12
  21. package/src/actions/charts/area.js +6 -2
  22. package/src/actions/charts/bar.js +3 -2
  23. package/src/actions/charts/beeswarm.js +1 -1
  24. package/src/actions/charts/density.js +7 -3
  25. package/src/actions/charts/ecdf.js +6 -3
  26. package/src/actions/charts/endpoints.js +29 -7
  27. package/src/actions/charts/heatmap.js +3 -2
  28. package/src/actions/charts/histogram.js +5 -4
  29. package/src/actions/charts/horizon.js +4 -2
  30. package/src/actions/charts/interval-regression.js +7 -4
  31. package/src/actions/charts/line.js +4 -2
  32. package/src/actions/charts/parallel.js +9 -3
  33. package/src/actions/charts/pie.js +11 -3
  34. package/src/actions/charts/polar.js +17 -6
  35. package/src/actions/charts/radar.js +9 -3
  36. package/src/actions/charts/radial.js +12 -4
  37. package/src/actions/charts/raincloud.js +2 -2
  38. package/src/actions/charts/rug-strip.js +8 -4
  39. package/src/actions/charts/scatter.js +4 -2
  40. package/src/actions/composition/actions.js +29 -24
  41. package/src/actions/coordinates/actions.js +2 -2
  42. package/src/actions/coordinates/edit.js +76 -0
  43. package/src/actions/data/basicBin2d.js +4 -5
  44. package/src/actions/data/bin.js +3 -3
  45. package/src/actions/data/bin2d.js +63 -193
  46. package/src/actions/data/bin2dMaterialize.js +4 -5
  47. package/src/actions/data/box.js +4 -4
  48. package/src/actions/data/complete.js +26 -0
  49. package/src/actions/data/computed.js +4 -4
  50. package/src/actions/data/create.js +7 -14
  51. package/src/actions/data/density.js +15 -23
  52. package/src/actions/data/derived.js +57 -31
  53. package/src/actions/data/ecdf.js +2 -2
  54. package/src/actions/data/edit.js +551 -0
  55. package/src/actions/data/filter.js +28 -22
  56. package/src/actions/data/fold.js +2 -2
  57. package/src/actions/data/gradientProfile.js +2 -2
  58. package/src/actions/data/horizon.js +2 -2
  59. package/src/actions/data/impute.js +26 -0
  60. package/src/actions/data/index.js +62 -47
  61. package/src/actions/data/interval.js +2 -2
  62. package/src/actions/data/intervalEdit.js +26 -18
  63. package/src/actions/data/normalize.js +27 -0
  64. package/src/actions/data/regression.js +21 -27
  65. package/src/actions/data/revise.js +48 -0
  66. package/src/actions/data/revisionBindings.js +125 -0
  67. package/src/actions/data/shared.js +20 -4
  68. package/src/actions/data/stack.js +2 -2
  69. package/src/actions/data/summary.js +3 -3
  70. package/src/actions/data/timeUnit.js +6 -3
  71. package/src/actions/data/window.js +3 -3
  72. package/src/actions/encodings/angle.js +1 -1
  73. package/src/actions/encodings/appearance.js +4 -4
  74. package/src/actions/encodings/barWidth.js +1 -1
  75. package/src/actions/encodings/channels.js +415 -0
  76. package/src/actions/encodings/color/index.js +3 -2
  77. package/src/actions/encodings/density.js +7 -4
  78. package/src/actions/encodings/histogram.js +4 -3
  79. package/src/actions/encodings/horizon.js +6 -8
  80. package/src/actions/encodings/index.js +4 -0
  81. package/src/actions/encodings/offset.js +22 -15
  82. package/src/actions/encodings/parallel.js +4 -5
  83. package/src/actions/encodings/pathOrder.js +6 -11
  84. package/src/actions/encodings/position/apply.js +53 -7
  85. package/src/actions/encodings/position/index.js +14 -6
  86. package/src/actions/encodings/position/policies/bar.js +9 -0
  87. package/src/actions/encodings/position/resolve.js +24 -7
  88. package/src/actions/encodings/ranged.js +5 -5
  89. package/src/actions/encodings/remove.js +13 -9
  90. package/src/actions/encodings/ruleAppearance.js +2 -24
  91. package/src/actions/encodings/seriesLayout.js +2 -1
  92. package/src/actions/encodings/shared.js +82 -14
  93. package/src/actions/encodings/stroke.js +226 -0
  94. package/src/actions/encodings/strokeDash.js +2 -2
  95. package/src/actions/encodings/text.js +1 -1
  96. package/src/actions/errorBands/create.js +34 -20
  97. package/src/actions/errorBands/edit.js +34 -137
  98. package/src/actions/errorBars/create.js +43 -20
  99. package/src/actions/errorBars/edit.js +29 -125
  100. package/src/actions/facets/actions.js +267 -73
  101. package/src/actions/facets/derive.js +99 -28
  102. package/src/actions/facets/guides.js +4 -5
  103. package/src/actions/facets/replay.js +4 -5
  104. package/src/actions/gradientPlots/components.js +15 -8
  105. package/src/actions/gradientPlots/create.js +4 -5
  106. package/src/actions/gradientPlots/edit.js +18 -9
  107. package/src/actions/gradientPlots/materialize.js +5 -3
  108. package/src/actions/gradientPlots/options.js +10 -2
  109. package/src/actions/gradientPlots/rebind.js +4 -5
  110. package/src/actions/guides/applicability.js +8 -2
  111. package/src/actions/guides/axes/axes.js +23 -4
  112. package/src/actions/guides/axes/axis.js +2 -2
  113. package/src/actions/guides/axes/edit.js +2 -2
  114. package/src/actions/guides/axes/labels.js +67 -21
  115. package/src/actions/guides/axes/lines.js +15 -9
  116. package/src/actions/guides/axes/parallel/lifecycle.js +13 -13
  117. package/src/actions/guides/axes/parallel/resolve.js +6 -2
  118. package/src/actions/guides/axes/parallel.js +5 -5
  119. package/src/actions/guides/axes/remove.js +4 -4
  120. package/src/actions/guides/axes/tickGroups.js +6 -5
  121. package/src/actions/guides/axes/ticks.js +19 -6
  122. package/src/actions/guides/axes/titles.js +21 -13
  123. package/src/actions/guides/grids/grid.js +18 -29
  124. package/src/actions/guides/grids/resolve.js +7 -2
  125. package/src/actions/guides/guides.js +20 -4
  126. package/src/actions/guides/legends/blocks.js +426 -0
  127. package/src/actions/guides/legends/categorical/actions.js +138 -52
  128. package/src/actions/guides/legends/categorical/components.js +55 -45
  129. package/src/actions/guides/legends/categorical/index.js +37 -33
  130. package/src/actions/guides/legends/categorical/layout.js +98 -16
  131. package/src/actions/guides/legends/categorical/options.js +2 -2
  132. package/src/actions/guides/legends/categorical/recipes.js +111 -20
  133. package/src/actions/guides/legends/categorical/resolve.js +12 -8
  134. package/src/actions/guides/legends/categorical/symbols.js +104 -52
  135. package/src/actions/guides/legends/continuous/common.js +76 -20
  136. package/src/actions/guides/legends/continuous/gradient.js +95 -158
  137. package/src/actions/guides/legends/continuous/index.js +2 -0
  138. package/src/actions/guides/legends/continuous/interval.js +21 -16
  139. package/src/actions/guides/legends/continuous/opacity.js +37 -83
  140. package/src/actions/guides/legends/continuous/stroke.js +225 -0
  141. package/src/actions/guides/legends/creation.js +14 -1
  142. package/src/actions/guides/legends/edit.js +159 -60
  143. package/src/actions/guides/legends/focused.js +5 -5
  144. package/src/actions/guides/legends/index.js +2 -0
  145. package/src/actions/guides/legends/lane.js +59 -24
  146. package/src/actions/guides/legends/lifecycle.js +19 -8
  147. package/src/actions/guides/legends/remove.js +17 -8
  148. package/src/actions/guides/legends/sampling.js +140 -0
  149. package/src/actions/guides/legends/size.js +100 -108
  150. package/src/actions/guides/legends/strokeWidth.js +38 -92
  151. package/src/actions/guides/legends/target.js +112 -1
  152. package/src/actions/guides/legends/transition.js +165 -16
  153. package/src/actions/guides/polar/axes/facade.js +8 -8
  154. package/src/actions/guides/polar/axes/labels.js +29 -10
  155. package/src/actions/guides/polar/axes/lines.js +19 -19
  156. package/src/actions/guides/polar/axes/shared.js +16 -0
  157. package/src/actions/guides/polar/axes/ticks.js +10 -16
  158. package/src/actions/guides/polar/axes/titles.js +6 -6
  159. package/src/actions/guides/polar/grids.js +12 -14
  160. package/src/actions/guides/polar/resolve.js +12 -8
  161. package/src/actions/index.js +16 -3
  162. package/src/actions/intervals/revision.js +126 -0
  163. package/src/actions/marks/arc/actions.js +80 -22
  164. package/src/actions/marks/area/actions.js +38 -13
  165. package/src/actions/marks/area/materialize.js +17 -1
  166. package/src/actions/marks/bar/create.js +12 -3
  167. package/src/actions/marks/bar/edit.js +21 -4
  168. package/src/actions/marks/bar/materialize.js +34 -40
  169. package/src/actions/marks/index.js +10 -1
  170. package/src/actions/marks/line/actions.js +40 -17
  171. package/src/actions/marks/line/materialize.js +29 -10
  172. package/src/actions/marks/point/create.js +9 -3
  173. package/src/actions/marks/point/edit.js +19 -7
  174. package/src/actions/marks/point/jitter.js +6 -8
  175. package/src/actions/marks/point/materialize.js +48 -22
  176. package/src/actions/marks/point/packing.js +6 -8
  177. package/src/actions/marks/rect/actions.js +36 -15
  178. package/src/actions/marks/references.js +602 -33
  179. package/src/actions/marks/remove.js +67 -41
  180. package/src/actions/marks/rule/actions.js +51 -14
  181. package/src/actions/marks/shared.js +1 -1
  182. package/src/actions/marks/text/actions.js +79 -14
  183. package/src/actions/marks/text/index.js +4 -0
  184. package/src/actions/marks/text/layout.js +157 -17
  185. package/src/actions/marks/text/removal.js +173 -0
  186. package/src/actions/marks/text/selection.js +72 -0
  187. package/src/actions/marks/tick/actions.js +25 -8
  188. package/src/actions/primitives/createGraphics.js +5 -4
  189. package/src/actions/primitives/editGraphics.js +31 -4
  190. package/src/actions/primitives/graphicProperties.js +6 -0
  191. package/src/actions/primitives/semantic.js +11 -3
  192. package/src/actions/primitives/semanticAction.js +90 -14
  193. package/src/actions/primitives/semanticValidation/dataset.js +2 -4
  194. package/src/actions/primitives/semanticValidation/guide.js +1 -1
  195. package/src/actions/primitives/semanticValidation/index.js +9 -1
  196. package/src/actions/primitives/semanticValidation/layer.js +5 -0
  197. package/src/actions/primitives/semanticValidation/scale.js +28 -7
  198. package/src/actions/regression/components.js +36 -25
  199. package/src/actions/regression/create.js +13 -6
  200. package/src/actions/regression/edit.js +24 -11
  201. package/src/actions/resources/index.js +7 -0
  202. package/src/actions/resources/remove.js +173 -0
  203. package/src/actions/scales/channels.js +212 -0
  204. package/src/actions/scales/consumers/common.js +1 -1
  205. package/src/actions/scales/consumers/families.js +3 -2
  206. package/src/actions/scales/consumers/index.js +19 -2
  207. package/src/actions/scales/consumers/seriesLayout.js +28 -14
  208. package/src/actions/scales/create.js +55 -23
  209. package/src/actions/scales/definitions.js +94 -40
  210. package/src/actions/scales/edit.js +67 -12
  211. package/src/actions/scales/editPolicy.js +81 -11
  212. package/src/actions/scales/index.js +6 -0
  213. package/src/actions/scales/materialize.js +5 -4
  214. package/src/actions/scales/offset.js +48 -0
  215. package/src/actions/scales/parallel.js +71 -0
  216. package/src/actions/scales/preview.js +111 -10
  217. package/src/actions/scales/quantitativeColor.js +4 -5
  218. package/src/actions/selection/actions.js +179 -66
  219. package/src/actions/selection/index.js +3 -0
  220. package/src/actions/textMetrics/index.js +56 -0
  221. package/src/actions/theme/actions.js +70 -15
  222. package/src/actions/theme/composition.js +238 -0
  223. package/src/actions/theme/index.js +9 -2
  224. package/src/actions/theme/reconcile.js +313 -121
  225. package/src/actions/theme/state.js +160 -0
  226. package/src/actions/titles/actions.js +8 -8
  227. package/src/actions/titles/resolve.js +3 -3
  228. package/src/actions/violinPlots/create.js +6 -3
  229. package/src/actions/violinPlots/edit.js +2 -1
  230. package/src/core/action.js +117 -35
  231. package/src/core/compositionState.js +6 -1
  232. package/src/core/diagnostics.js +16 -0
  233. package/src/core/extensionRegistry.js +27 -0
  234. package/src/core/font.js +18 -0
  235. package/src/core/identifiers.js +5 -4
  236. package/src/core/immutable.js +10 -3
  237. package/src/core/optionRequirements.js +8 -0
  238. package/src/core/programState.js +21 -1
  239. package/src/core/resourceReferences.js +341 -0
  240. package/src/core/textMetricProfile.js +28 -0
  241. package/src/core/textMetrics.js +24 -3
  242. package/src/core/validation.js +22 -10
  243. package/src/core/vocabulary.js +16 -3
  244. package/src/diagnostics.js +1 -0
  245. package/src/grammar/bars/aggregate.js +4 -1
  246. package/src/grammar/bars/geometry.js +44 -0
  247. package/src/grammar/bin.js +54 -9
  248. package/src/grammar/complete.js +279 -0
  249. package/src/grammar/computed.js +265 -64
  250. package/src/grammar/coordinates.js +37 -0
  251. package/src/grammar/density.js +239 -37
  252. package/src/grammar/displayLabels.js +57 -0
  253. package/src/grammar/facets/dependencies.js +133 -1
  254. package/src/grammar/facets/guides.js +3 -0
  255. package/src/grammar/facets/index.js +7 -47
  256. package/src/grammar/facets/scales.js +15 -18
  257. package/src/grammar/impute.js +341 -0
  258. package/src/grammar/markLabelSelection.js +57 -0
  259. package/src/grammar/markLabels.js +18 -1
  260. package/src/grammar/normalize.js +366 -0
  261. package/src/grammar/pathSeries.js +26 -8
  262. package/src/grammar/pointShapes.js +13 -4
  263. package/src/grammar/polar.js +90 -8
  264. package/src/grammar/regression/index.js +1 -0
  265. package/src/grammar/regression/parameters.js +23 -0
  266. package/src/grammar/roundedRect.js +180 -0
  267. package/src/grammar/rules.js +1 -0
  268. package/src/grammar/scales/colorConsumers.js +20 -1
  269. package/src/grammar/scales/continuous.js +2 -2
  270. package/src/grammar/scales/definition.js +27 -4
  271. package/src/grammar/scales/index.js +1 -0
  272. package/src/grammar/scales/ordinal.js +12 -3
  273. package/src/grammar/scales/policies.js +2 -2
  274. package/src/grammar/scales/size.js +456 -0
  275. package/src/grammar/scales/types.js +8 -0
  276. package/src/grammar/schemas/concreteGraphic.js +14 -2
  277. package/src/grammar/schemas/graphic.js +15 -0
  278. package/src/grammar/schemas/graphicBounds.js +190 -41
  279. package/src/grammar/schemas/semanticPath.js +7 -2
  280. package/src/grammar/statisticalReference.js +103 -0
  281. package/src/grammar/strokeStyle.js +77 -0
  282. package/src/grammar/summary.js +78 -16
  283. package/src/grammar/timeUnit.js +216 -5
  284. package/src/grammar/transformTopology.js +4 -0
  285. package/src/grammar/transforms.js +394 -28
  286. package/src/grammar/weightedStatistics.js +374 -0
  287. package/src/grammar/window.js +221 -16
  288. package/src/layout/aspect.js +80 -0
  289. package/src/layout/facets.js +59 -5
  290. package/src/layout/labels.js +351 -7
  291. package/src/layout/legend.js +4 -4
  292. package/src/layout/legendItems.js +6 -6
  293. package/src/layout/text.js +10 -9
  294. package/src/layout/title.js +9 -9
  295. package/src/materialization/bars/aggregate.js +25 -2
  296. package/src/materialization/bars/histogram.js +84 -24
  297. package/src/materialization/bars/ranged.js +19 -3
  298. package/src/materialization/bars/resolve.js +7 -4
  299. package/src/materialization/composition.js +3 -1
  300. package/src/materialization/coordinateBounds.js +204 -0
  301. package/src/materialization/dependencies.js +36 -0
  302. package/src/materialization/facetGuides/legacyCategorical.js +3 -0
  303. package/src/materialization/facetGuides/placement.js +9 -3
  304. package/src/materialization/facetGuides/preparation.js +13 -8
  305. package/src/materialization/facetHeaders.js +347 -0
  306. package/src/materialization/facets.js +15 -65
  307. package/src/materialization/guides/layout.js +3 -3
  308. package/src/materialization/guides/resources.js +26 -0
  309. package/src/materialization/legends.js +1 -1
  310. package/src/materialization/marks/index.js +11 -5
  311. package/src/materialization/marks/policies.js +2 -1
  312. package/src/materialization/rect.js +25 -12
  313. package/src/materialization/rectConfig.js +5 -1
  314. package/src/materialization/scaleGuideDependencies.js +10 -0
  315. package/src/materialization/scales/map.js +5 -3
  316. package/src/materialization/scales/policies/binnedPosition.js +2 -2
  317. package/src/materialization/scales/policies/offset.js +50 -8
  318. package/src/materialization/scales/policies/series.js +4 -1
  319. package/src/materialization/scales/resolve.js +22 -6
  320. package/src/materialization/selection/items/arc.js +28 -4
  321. package/src/materialization/selection/items/bar.js +30 -3
  322. package/src/materialization/selection/items/common.js +20 -5
  323. package/src/materialization/selection/items/rect.js +28 -4
  324. package/src/materialization/selection/path.js +1 -1
  325. package/src/materialization/selection/point.js +3 -1
  326. package/src/materialization/text.js +265 -14
  327. package/src/materialization/typography.js +31 -0
  328. package/src/mcp/cli.js +5 -3
  329. package/src/persistence/codec.js +65 -0
  330. package/src/persistence/validation.js +185 -0
  331. package/src/persistence.js +74 -0
  332. package/src/renderers/canvas/circle.js +2 -3
  333. package/src/renderers/canvas/index.js +5 -4
  334. package/src/renderers/canvas/line.js +2 -3
  335. package/src/renderers/canvas/native.js +4 -4
  336. package/src/renderers/canvas/path.js +2 -3
  337. package/src/renderers/canvas/rect.js +2 -3
  338. package/src/renderers/canvas/stroke.js +11 -0
  339. package/src/renderers/nativeBackend.js +10 -0
  340. package/src/renderers/options.js +13 -0
  341. package/src/renderers/pdf.js +25 -54
  342. package/src/renderers/png.js +22 -17
  343. package/src/renderers/svg.js +29 -14
  344. package/src/renderers/text.js +1 -11
  345. package/src/selectors/coordinates.js +3 -1
  346. package/src/selectors/datasets.js +38 -5
  347. package/src/selectors/layers.js +10 -4
  348. package/src/selectors/markOwners.js +46 -0
  349. package/src/selectors/scales.js +6 -4
  350. package/src/theme/defaults.js +103 -1
  351. package/src/version.js +2 -0
  352. package/types/accessibility.d.ts +37 -0
  353. package/types/basic.d.ts +10 -2
  354. package/types/diagnostics.d.ts +15 -0
  355. package/types/index.d.ts +90 -0
  356. package/types/pdf.d.ts +9 -0
  357. package/types/persistence.d.ts +7 -0
  358. package/types/png.d.ts +9 -0
  359. package/types/program.d.ts +944 -118
@@ -26,6 +26,11 @@ import {
26
26
  resolveTarget,
27
27
  sameValues
28
28
  } from "./resolve.js";
29
+ import { readLegendSampling } from "../sampling.js";
30
+ import {
31
+ reconcileLegendBlockTitleGraphic,
32
+ resolveEffectiveLegendBlockConfig
33
+ } from "../blocks.js";
29
34
 
30
35
  function finishLegend(program) {
31
36
  return hasLegendLane(program) ? program.rematerializeLegend() : program;
@@ -45,30 +50,40 @@ function resolveStandaloneLegendStep(args, kind) {
45
50
  return { op: kind === "size" ? "createSizeLegend" : "createStrokeWidthLegend", args: options };
46
51
  }
47
52
 
48
- export const rematerializeLegend = action(
53
+ export const rematerializeLegend = /* @__PURE__ */ action(
49
54
  { op: "rematerializeLegend", description: "Rematerialize every existing legend component." },
50
55
  withGuideLayoutValidation(function (args = {}) {
51
56
  noOptions(args, "rematerializeLegend");
52
57
  let next = this;
53
- const hasCategorical =
54
- this.guideConfigs.legend?.series !== undefined ||
55
- this.guideConfigs.legend?.color !== undefined;
56
- if (hasCategorical) {
57
- const { kind, config } = activeConfig(this);
58
- const definition = resolveCurrentDefinition(this, config);
59
- const symbol = config.inferredSymbol
60
- ? normalizeRecipe(resolveLegendSymbol(this, findLayer(this, config.target), definition.channels), kind)
61
- : config.symbol;
62
- const symbolChanged = !sameGuideValue(symbol, config.symbol);
58
+ const categoricalKinds = ["series", "color", "stroke"].filter(
59
+ kind => this.guideConfigs.legend?.[kind] !== undefined
60
+ );
61
+ for (const kind of categoricalKinds) {
62
+ const stored = next.guideConfigs.legend[kind];
63
+ const definition = resolveCurrentDefinition(next, stored);
64
+ const symbol = stored.inferredSymbol
65
+ ? normalizeRecipe(
66
+ resolveLegendSymbol(
67
+ next,
68
+ findLayer(next, stored.target),
69
+ definition.channels,
70
+ undefined,
71
+ kind
72
+ ),
73
+ kind,
74
+ { internal: true }
75
+ )
76
+ : stored.symbol;
77
+ const symbolChanged = !sameGuideValue(symbol, stored.symbol);
63
78
  const changed = symbolChanged ||
64
- !sameValues(config.channels, definition.channels) ||
65
- !sameValues(config.domain, definition.domain) ||
66
- !sameValues(config.scales, definition.scales) ||
67
- config.field !== definition.field ||
68
- config.title !== definition.title;
79
+ !sameValues(stored.channels, definition.channels) ||
80
+ !sameValues(stored.domain, definition.domain) ||
81
+ !sameValues(stored.scales, definition.scales) ||
82
+ stored.field !== definition.field ||
83
+ stored.title !== definition.title;
69
84
  next = changed
70
- ? this._withLegendConfig(kind, {
71
- ...config,
85
+ ? next._withLegendConfig(kind, {
86
+ ...stored,
72
87
  symbol,
73
88
  channels: definition.channels,
74
89
  scales: definition.scales,
@@ -76,10 +91,17 @@ export const rematerializeLegend = action(
76
91
  title: definition.title,
77
92
  domain: definition.domain
78
93
  })
79
- : this;
94
+ : next;
95
+ const config = resolveEffectiveLegendBlockConfig(
96
+ next,
97
+ kind,
98
+ next.guideConfigs.legend[kind]
99
+ );
100
+ next = reconcileLegendBlockTitleGraphic(next, kind,
101
+ config.titleVisible !== false);
80
102
  if (kind === "series") {
81
103
  if (!sameValues(
82
- this.semanticSpec.guides.legend.series.scales,
104
+ next.semanticSpec.guides.legend.series.scales,
83
105
  definition.scales
84
106
  )) {
85
107
  next = next.editSemantic({
@@ -87,45 +109,56 @@ export const rematerializeLegend = action(
87
109
  value: definition.scales
88
110
  });
89
111
  }
90
- if (this.semanticSpec.guides.legend.series.title !== definition.title) {
112
+ if (next.semanticSpec.guides.legend.series.title !== definition.title) {
91
113
  next = next.editSemantic({
92
114
  property: "guide.legend.series.title",
93
115
  value: definition.title
94
116
  });
95
117
  }
96
118
  } else {
97
- if (this.semanticSpec.guides.legend.color.scale !== definition.scales[0]) {
119
+ if (next.semanticSpec.guides.legend[kind].scale !== definition.scales[0]) {
98
120
  next = next.editSemantic({
99
- property: "guide.legend.color.scale",
121
+ property: `guide.legend.${kind}.scale`,
100
122
  value: definition.scales[0]
101
123
  });
102
124
  }
103
- if (this.semanticSpec.guides.legend.color.title !== definition.title) {
125
+ if (next.semanticSpec.guides.legend[kind].title !== definition.title) {
104
126
  next = next.editSemantic({
105
- property: "guide.legend.color.title",
127
+ property: `guide.legend.${kind}.title`,
106
128
  value: definition.title
107
129
  });
108
130
  }
109
131
  }
110
132
  if (symbolChanged) {
111
- resolveLayout(next, next.guideConfigs.legend[kind]);
112
- next = reconcileCategoricalSymbols(next, config, next.guideConfigs.legend[kind]);
133
+ resolveLayout(next, config);
134
+ next = reconcileCategoricalSymbols(next, stored, config);
135
+ }
136
+ if (config.border !== false) {
137
+ next = next.rematerializeLegendBackground({ kind });
113
138
  }
114
- if (config.border !== false) next = next.rematerializeLegendBackground();
115
139
  next = next
116
- .rematerializeLegendSymbols()
117
- .rematerializeLegendLabels();
118
- if (config.titleVisible !== false) next = next.rematerializeLegendTitle();
140
+ .rematerializeLegendSymbols({ kind })
141
+ .rematerializeLegendLabels({ kind });
142
+ if (config.titleVisible !== false) {
143
+ next = next.rematerializeLegendTitle({ kind });
144
+ }
119
145
  const hasHighlight = Object.values(
120
146
  next.materializationConfigs.highlights ?? {}
121
147
  ).some(highlight => highlight.target === config.target);
122
- if (hasHighlight) next = next.rematerializeLegendHighlights();
148
+ if (hasHighlight) next = next.rematerializeLegendHighlights({ kind });
123
149
  }
124
150
  for (const policy of legendResourcePolicies()) {
125
151
  if (
126
152
  policy.rematerializeOp !== undefined &&
127
153
  this.guideConfigs.legend?.[policy.kind] !== undefined
128
154
  ) {
155
+ const config = resolveEffectiveLegendBlockConfig(
156
+ next,
157
+ policy.kind,
158
+ next.guideConfigs.legend[policy.kind]
159
+ );
160
+ next = reconcileLegendBlockTitleGraphic(next, policy.kind,
161
+ config.titleVisible !== false);
129
162
  next = next[policy.rematerializeOp]();
130
163
  }
131
164
  }
@@ -148,14 +181,21 @@ export function resolveCategoricalLegendConfig(program, args = {}) {
148
181
  args.title,
149
182
  args.order === undefined ? undefined : normalizeLegendOrder(args.order)
150
183
  );
184
+ const inferredSymbol = args.symbol === undefined || args.symbol === "auto";
151
185
  const options = normalizeOptions({ ...args,
152
- symbol: resolveLegendSymbol(program, layer, definition.channels, args.symbol)
153
- }, definition.kind);
186
+ symbol: resolveLegendSymbol(
187
+ program,
188
+ layer,
189
+ definition.channels,
190
+ args.symbol,
191
+ definition.kind
192
+ )
193
+ }, definition.kind, { internalSymbol: inferredSymbol });
154
194
  const config = {
155
195
  target: layer.id,
156
196
  ...definition,
157
197
  inferredTitle: !Object.hasOwn(args, "title"),
158
- inferredSymbol: args.symbol === undefined || args.symbol === "auto",
198
+ inferredSymbol,
159
199
  position: options.position,
160
200
  align: options.align,
161
201
  direction: options.direction,
@@ -173,15 +213,14 @@ export function resolveCategoricalLegendConfig(program, args = {}) {
173
213
  return config;
174
214
  }
175
215
 
176
- export const createCategoricalLegend = action(
216
+ export const createCategoricalLegend = /* @__PURE__ */ action(
177
217
  { op: "createCategoricalLegend", description: "Create one categorical legend block." },
178
218
  withGuideLayoutValidation(function (args = {}) {
179
219
  const config = resolveCategoricalLegendConfig(this, args);
180
- if (
181
- this.semanticSpec.guides.legend?.series !== undefined ||
182
- this.semanticSpec.guides.legend?.color !== undefined
183
- ) {
184
- throw new Error("createCategoricalLegend requires a missing legend.");
220
+ if (this.semanticSpec.guides.legend?.[config.kind] !== undefined) {
221
+ throw new Error(
222
+ `createCategoricalLegend requires a missing legend for ${config.kind}.`
223
+ );
185
224
  }
186
225
  resolveLayout(this, config);
187
226
  return createCategoricalLegendFromConfig(this, config,
@@ -195,12 +234,12 @@ export function resolveLegendCreationPlan(program, args = {}, layers = program.s
195
234
  const channels = args.channels;
196
235
  if (channels !== undefined) validateLegendChannels(channels, "createLegend");
197
236
  const standalone = [
198
- ["size", isSizeLegendPoint, ["color", "shape", "strokeDash", "opacity"]],
237
+ ["size", isSizeLegendPoint, ["color", "stroke", "shape", "strokeDash", "opacity"]],
199
238
  ["strokeWidth", isStrokeWidthLegendLayer, args.target === undefined
200
- ? ["color", "shape", "strokeDash", "size", "opacity"]
201
- : ["color", "shape", "strokeDash", "opacity"]],
239
+ ? ["color", "stroke", "shape", "strokeDash", "size", "opacity"]
240
+ : ["color", "stroke", "shape", "strokeDash", "opacity"]],
202
241
  ["opacity", isOpacityLegendLayer,
203
- ["color", "shape", "strokeDash", "size"]]
242
+ ["color", "stroke", "shape", "strokeDash", "size"]]
204
243
  ];
205
244
  for (const [kind, eligible, otherChannels] of standalone) {
206
245
  const explicit = channels?.length === 1 && channels[0] === kind;
@@ -240,6 +279,47 @@ export function resolveLegendCreationPlan(program, args = {}, layers = program.s
240
279
  ) {
241
280
  return { steps: [{ op: "createIntervalLegend", args }], finish: "auto" };
242
281
  }
282
+ const continuousStrokeCandidates = candidates.filter(layer => {
283
+ const scale = findSemanticScale(program, layer.encoding?.stroke?.scale);
284
+ return scale?.type === "sequential";
285
+ });
286
+ const continuousStroke = requestedCandidate(
287
+ program,
288
+ args.target,
289
+ continuousStrokeCandidates
290
+ );
291
+ if (
292
+ (channels?.length === 1 && channels[0] === "stroke" && continuousStroke) ||
293
+ (channels === undefined && continuousStroke &&
294
+ !["color", "shape", "strokeDash", "size", "opacity", "strokeWidth"].some(
295
+ channel => continuousStroke.encoding?.[channel]?.scale !== undefined
296
+ ))
297
+ ) {
298
+ return {
299
+ steps: [{ op: "createStrokeGradientLegend", args: {
300
+ ...args,
301
+ channels: ["stroke"]
302
+ } }],
303
+ finish: "auto"
304
+ };
305
+ }
306
+ const intervalStrokeCandidates = candidates.filter(layer => {
307
+ const scale = findSemanticScale(program, layer.encoding?.stroke?.scale);
308
+ return ["quantize", "quantile", "threshold"].includes(scale?.type);
309
+ });
310
+ if (
311
+ (channels?.length === 1 && channels[0] === "stroke" &&
312
+ intervalStrokeCandidates.length > 0) ||
313
+ (channels === undefined && intervalStrokeCandidates.length === 1)
314
+ ) {
315
+ return {
316
+ steps: [{ op: "createStrokeIntervalLegend", args: {
317
+ ...args,
318
+ channels: ["stroke"]
319
+ } }],
320
+ finish: "auto"
321
+ };
322
+ }
243
323
  const wantsShape = channels?.includes("shape") === true;
244
324
  const wantsSize = channels?.includes("size") === true;
245
325
  const pointCandidates = candidates.filter(layer =>
@@ -247,7 +327,7 @@ export function resolveLegendCreationPlan(program, args = {}, layers = program.s
247
327
  (channels === undefined
248
328
  ? ["color", "shape"].some(channel => layer.encoding?.[channel]?.scale !== undefined)
249
329
  : (wantsShape || wantsSize) && channels.every(channel =>
250
- ["color", "shape", "size"].includes(channel) && layer.encoding?.[channel]?.scale !== undefined))
330
+ ["color", "stroke", "shape", "size"].includes(channel) && layer.encoding?.[channel]?.scale !== undefined))
251
331
  );
252
332
  const requestedPoint = requestedCandidate(program, args.target, pointCandidates);
253
333
  const inferredSize = channels === undefined && pointCandidates.some(isSizeLegendPoint);
@@ -255,6 +335,11 @@ export function resolveLegendCreationPlan(program, args = {}, layers = program.s
255
335
  throw new Error("Combined size legend requires one eligible point mark or an explicit target.");
256
336
  }
257
337
  if (requestedPoint !== undefined) {
338
+ if (Object.hasOwn(args, "values")) {
339
+ throw new Error(
340
+ "Combined and categorical legends require a channel block selector for exact values."
341
+ );
342
+ }
258
343
  const { count, ...categoricalArgs } = args;
259
344
  const combined = requestedPoint.encoding?.size?.scale !== undefined &&
260
345
  (channels === undefined || wantsSize);
@@ -264,7 +349,7 @@ export function resolveLegendCreationPlan(program, args = {}, layers = program.s
264
349
  if (combined && categoricalArgs.layout === "legacy-bottom") {
265
350
  throw new Error('Combined size legends require layout "edge".');
266
351
  }
267
- const inferredChannels = ["color", "shape"].filter(
352
+ const inferredChannels = ["color", "stroke", "shape"].filter(
268
353
  channel => requestedPoint.encoding?.[channel]?.scale !== undefined
269
354
  );
270
355
  const { format: sizeFormat, ...categoricalLabels } = categoricalArgs.labels ?? {};
@@ -294,7 +379,8 @@ export function resolveLegendCreationPlan(program, args = {}, layers = program.s
294
379
  if (existing !== undefined && existing.target !== size.target) {
295
380
  throw new Error("Combined point series legend requires the active size legend to share its target.");
296
381
  }
297
- if (existing !== undefined && count !== undefined && existing.count !== count) {
382
+ if (existing !== undefined && count !== undefined &&
383
+ readLegendSampling(existing).count !== count) {
298
384
  throw new Error("Existing size legend count must be edited before recreating the categorical block.");
299
385
  }
300
386
  }
@@ -317,28 +403,28 @@ export function applyLegendCreationPlan(program, plan) {
317
403
  : plan.finish === "auto" ? finishLegend(next) : next;
318
404
  }
319
405
 
320
- export const createLegend = action(
406
+ export const createLegend = /* @__PURE__ */ action(
321
407
  { op: "createLegend", description: "Create an inferred legend for selected channels." },
322
408
  withGuideLayoutValidation(function (args = {}) {
323
409
  return applyLegendCreationPlan(this, resolveLegendCreationPlan(this, args));
324
410
  })
325
411
  );
326
412
 
327
- export const removeCategoricalLegend = action(
413
+ export const removeCategoricalLegend = /* @__PURE__ */ action(
328
414
  {
329
415
  op: "removeCategoricalLegend",
330
416
  description: "Remove the active categorical legend and its concrete components."
331
417
  },
332
418
  withGuideLayoutValidation(function (args = {}) {
333
419
  noOptions(args, "removeCategoricalLegend");
334
- const entries = ["series", "color"]
420
+ const entries = ["series", "color", "stroke"]
335
421
  .filter(kind => this.guideConfigs.legend?.[kind] !== undefined);
336
422
  if (entries.length === 0) return this;
337
423
  if (entries.length !== 1) {
338
424
  throw new Error("removeCategoricalLegend requires one active categorical legend.");
339
425
  }
340
426
  const kind = entries[0];
341
- const prefix = kind === "series" ? "seriesLegend" : "colorLegend";
427
+ const prefix = `${kind}Legend`;
342
428
  const targets = Object.keys(this.graphicSpec.objects)
343
429
  .filter(id => id.startsWith(prefix));
344
430
  let next = this.editSemantic({
@@ -1,41 +1,49 @@
1
+ import { editGraphicProperties } from "../../../primitives/graphicProperties.js";
1
2
  import { action } from "../../../../core/action.js";
2
- import { formatVisibleText } from "../../../../core/textMetrics.js";
3
- import { activeConfig, graphic, noOptions, resolveLayout } from "./layout.js";
3
+ import {
4
+ activeConfig,
5
+ categoricalLegendLabels,
6
+ graphic,
7
+ noOptions,
8
+ resolveLayout
9
+ } from "./layout.js";
4
10
  import { resolveCategoricalLegendPlacement } from "../lifecycle.js";
5
11
 
6
- export const rematerializeLegendLabels = action(
12
+ export const rematerializeLegendLabels = /* @__PURE__ */ action(
7
13
  { op: "rematerializeLegendLabels", description: "Rematerialize categorical legend labels." },
8
14
  function (args = {}) {
9
15
  noOptions(args, "rematerializeLegendLabels");
10
- const { config } = activeConfig(this);
16
+ const { config } = activeConfig(this, args.kind);
11
17
  const id = graphic(config, "Labels");
12
18
  if (this.graphicSpec.objects[id]?.type !== "text") {
13
19
  throw new Error("rematerializeLegendLabels requires existing legend labels.");
14
20
  }
15
21
  const layout = resolveLayout(this, config);
16
- return this
17
- .editGraphics({ target: id, property: "length", value: config.domain.length })
18
- .editGraphics({ target: id, property: "x", value: layout.labelX })
19
- .editGraphics({ target: id, property: "y", value: layout.itemY })
22
+ return editGraphicProperties(editGraphicProperties(this, id, {
23
+ length: config.domain.length,
24
+ x: layout.labelX,
25
+ y: layout.itemY
26
+ })
20
27
  .editGraphics({
21
28
  target: id,
22
29
  property: "text",
23
- value: config.domain.map(formatVisibleText)
24
- })
25
- .editGraphics({ target: id, property: "fill", value: config.labels.color })
26
- .editGraphics({ target: id, property: "fontSize", value: config.labels.fontSize })
27
- .editGraphics({ target: id, property: "fontFamily", value: config.labels.fontFamily })
28
- .editGraphics({ target: id, property: "fontWeight", value: config.labels.fontWeight })
29
- .editGraphics({ target: id, property: "textAlign", value: "left" })
30
- .editGraphics({ target: id, property: "textBaseline", value: "middle" });
30
+ value: categoricalLegendLabels(config)
31
+ }), id, {
32
+ fill: config.labels.color,
33
+ fontSize: config.labels.fontSize,
34
+ fontFamily: config.labels.fontFamily,
35
+ fontWeight: config.labels.fontWeight,
36
+ textAlign: "left",
37
+ textBaseline: "middle"
38
+ });
31
39
  }
32
40
  );
33
41
 
34
- export const createLegendLabels = action(
42
+ export const createLegendLabels = /* @__PURE__ */ action(
35
43
  { op: "createLegendLabels", description: "Create categorical legend labels." },
36
44
  function (args = {}) {
37
45
  noOptions(args, "createLegendLabels");
38
- const { config } = activeConfig(this);
46
+ const { config } = activeConfig(this, args.kind);
39
47
  const id = graphic(config, "Labels");
40
48
  if (this.graphicSpec.objects[id] !== undefined) {
41
49
  throw new Error("createLegendLabels requires missing legend labels.");
@@ -47,28 +55,29 @@ export const createLegendLabels = action(
47
55
  length: config.domain.length,
48
56
  ...resolveCategoricalLegendPlacement(this)
49
57
  })
50
- .rematerializeLegendLabels();
58
+ .rematerializeLegendLabels({ kind: config.kind });
51
59
  }
52
60
  );
53
61
 
54
- export const rematerializeLegendTitle = action(
62
+ export const rematerializeLegendTitle = /* @__PURE__ */ action(
55
63
  { op: "rematerializeLegendTitle", description: "Rematerialize the categorical legend title." },
56
64
  function (args = {}) {
57
65
  noOptions(args, "rematerializeLegendTitle");
58
- const { config } = activeConfig(this);
66
+ const { config } = activeConfig(this, args.kind);
59
67
  const id = graphic(config, "Title");
60
68
  if (this.graphicSpec.objects[id]?.type !== "text") {
61
69
  throw new Error("rematerializeLegendTitle requires an existing legend title.");
62
70
  }
63
71
  const layout = resolveLayout(this, config);
64
- return this
65
- .editGraphics({ target: id, property: "x", value: layout.titleX })
66
- .editGraphics({ target: id, property: "y", value: layout.titleY })
67
- .editGraphics({ target: id, property: "text", value: config.title })
68
- .editGraphics({ target: id, property: "fill", value: config.titleStyle.color })
69
- .editGraphics({ target: id, property: "fontSize", value: config.titleStyle.fontSize })
70
- .editGraphics({ target: id, property: "fontFamily", value: config.titleStyle.fontFamily })
71
- .editGraphics({ target: id, property: "fontWeight", value: config.titleStyle.fontWeight })
72
+ return editGraphicProperties(this, id, {
73
+ x: layout.titleX,
74
+ y: layout.titleY,
75
+ text: config.title,
76
+ fill: config.titleStyle.color,
77
+ fontSize: config.titleStyle.fontSize,
78
+ fontFamily: config.titleStyle.fontFamily,
79
+ fontWeight: config.titleStyle.fontWeight
80
+ })
72
81
  .editGraphics({
73
82
  target: id,
74
83
  property: "textAlign",
@@ -81,11 +90,11 @@ export const rematerializeLegendTitle = action(
81
90
  }
82
91
  );
83
92
 
84
- export const createLegendTitle = action(
93
+ export const createLegendTitle = /* @__PURE__ */ action(
85
94
  { op: "createLegendTitle", description: "Create the categorical legend title." },
86
95
  function (args = {}) {
87
96
  noOptions(args, "createLegendTitle");
88
- const { config } = activeConfig(this);
97
+ const { config } = activeConfig(this, args.kind);
89
98
  const id = graphic(config, "Title");
90
99
  if (this.graphicSpec.objects[id] !== undefined) {
91
100
  throw new Error("createLegendTitle requires a missing legend title.");
@@ -96,15 +105,15 @@ export const createLegendTitle = action(
96
105
  type: "text",
97
106
  ...resolveCategoricalLegendPlacement(this)
98
107
  })
99
- .rematerializeLegendTitle();
108
+ .rematerializeLegendTitle({ kind: config.kind });
100
109
  }
101
110
  );
102
111
 
103
- export const rematerializeLegendBackground = action(
112
+ export const rematerializeLegendBackground = /* @__PURE__ */ action(
104
113
  { op: "rematerializeLegendBackground", description: "Rematerialize the legend background." },
105
114
  function (args = {}) {
106
115
  noOptions(args, "rematerializeLegendBackground");
107
- const { config } = activeConfig(this);
116
+ const { config } = activeConfig(this, args.kind);
108
117
  if (config.border === false) {
109
118
  throw new Error("rematerializeLegendBackground requires border configuration.");
110
119
  }
@@ -113,22 +122,23 @@ export const rematerializeLegendBackground = action(
113
122
  throw new Error("rematerializeLegendBackground requires an existing background.");
114
123
  }
115
124
  const layout = resolveLayout(this, config).background;
116
- return this
117
- .editGraphics({ target: id, property: "x", value: layout.x })
118
- .editGraphics({ target: id, property: "y", value: layout.y })
119
- .editGraphics({ target: id, property: "width", value: layout.width })
120
- .editGraphics({ target: id, property: "height", value: layout.height })
121
- .editGraphics({ target: id, property: "fill", value: config.border.background })
122
- .editGraphics({ target: id, property: "stroke", value: config.border.color })
123
- .editGraphics({ target: id, property: "strokeWidth", value: config.border.lineWidth });
125
+ return editGraphicProperties(this, id, {
126
+ x: layout.x,
127
+ y: layout.y,
128
+ width: layout.width,
129
+ height: layout.height,
130
+ fill: config.border.background,
131
+ stroke: config.border.color,
132
+ strokeWidth: config.border.lineWidth
133
+ });
124
134
  }
125
135
  );
126
136
 
127
- export const createLegendBackground = action(
137
+ export const createLegendBackground = /* @__PURE__ */ action(
128
138
  { op: "createLegendBackground", description: "Create the legend background rect." },
129
139
  function (args = {}) {
130
140
  noOptions(args, "createLegendBackground");
131
- const { config } = activeConfig(this);
141
+ const { config } = activeConfig(this, args.kind);
132
142
  const id = graphic(config, "Background");
133
143
  if (this.graphicSpec.objects[id] !== undefined) {
134
144
  throw new Error("createLegendBackground requires a missing background.");
@@ -139,6 +149,6 @@ export const createLegendBackground = action(
139
149
  type: "rect",
140
150
  ...resolveCategoricalLegendPlacement(this)
141
151
  })
142
- .rematerializeLegendBackground();
152
+ .rematerializeLegendBackground({ kind: config.kind });
143
153
  }
144
154
  );
@@ -68,40 +68,44 @@ function createLegendHighlightAction() {
68
68
  },
69
69
  function (args = {}) {
70
70
  noOptions(args, "rematerializeLegendHighlights");
71
- const hasCategorical =
72
- this.guideConfigs.legend?.series !== undefined ||
73
- this.guideConfigs.legend?.color !== undefined;
74
- if (!hasCategorical) return this;
75
- const { config } = activeConfig(this);
76
- const highlights = Object.values(
77
- this.materializationConfigs.highlights ?? {}
78
- ).map(highlight => ({
79
- highlight,
80
- states: exactLegendSelection(this, config, highlight)
81
- })).filter(entry => entry.states !== undefined);
82
- if (highlights.length === 0) return this;
71
+ const kinds = args.kind === undefined
72
+ ? ["series", "color", "stroke"].filter(
73
+ kind => this.guideConfigs.legend?.[kind] !== undefined
74
+ )
75
+ : [args.kind];
76
+ let next = this;
77
+ for (const kind of kinds) {
78
+ const { config } = activeConfig(next, kind);
79
+ const highlights = Object.values(
80
+ next.materializationConfigs.highlights ?? {}
81
+ ).map(highlight => ({
82
+ highlight,
83
+ states: exactLegendSelection(next, config, highlight)
84
+ })).filter(entry => entry.states !== undefined);
85
+ if (highlights.length === 0) continue;
83
86
 
84
- let next = this.rematerializeLegendSymbols();
85
- for (const { highlight, states } of highlights) {
86
- for (const layer of config.symbol.layers) {
87
- const id = symbolGraphic(config, layer.type);
88
- const graphic = next.graphicSpec.objects[id];
89
- const selectedStyle = legendLayerStyle(layer, highlight.style);
90
- const dimOpacity = highlight.dimOthers === false
91
- ? undefined
92
- : highlight.dimOthers.opacity;
93
- next = next.editGraphics({
94
- target: id,
95
- property: "items",
96
- value: graphic.items.map((child, index) => ({
97
- type: child.type ?? graphic.type,
98
- properties: states[index]
99
- ? { ...child.properties, ...selectedStyle }
100
- : dimOpacity === undefined
101
- ? child.properties
102
- : { ...child.properties, opacity: dimOpacity }
103
- }))
104
- });
87
+ next = next.rematerializeLegendSymbols({ kind: config.kind });
88
+ for (const { highlight, states } of highlights) {
89
+ for (const layer of config.symbol.layers) {
90
+ const id = symbolGraphic(config, layer.type);
91
+ const graphic = next.graphicSpec.objects[id];
92
+ const selectedStyle = legendLayerStyle(layer, highlight.style);
93
+ const dimOpacity = highlight.dimOthers === false
94
+ ? undefined
95
+ : highlight.dimOthers.opacity;
96
+ next = next.editGraphics({
97
+ target: id,
98
+ property: "items",
99
+ value: graphic.items.map((child, index) => ({
100
+ type: child.type ?? graphic.type,
101
+ properties: states[index]
102
+ ? { ...child.properties, ...selectedStyle }
103
+ : dimOpacity === undefined
104
+ ? child.properties
105
+ : { ...child.properties, opacity: dimOpacity }
106
+ }))
107
+ });
108
+ }
105
109
  }
106
110
  }
107
111
  return next;