ggaction 0.0.18 → 0.0.20

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 (164) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +6 -2
  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/package.json +1 -1
  7. package/src/actions/canvas/fitting.js +6 -2
  8. package/src/actions/charts/heatmap.js +4 -3
  9. package/src/actions/data/derived.js +11 -1
  10. package/src/actions/data/edit.js +2 -0
  11. package/src/actions/data/index.js +4 -0
  12. package/src/actions/data/regression.js +24 -16
  13. package/src/actions/data/regressionStatistics.js +73 -0
  14. package/src/actions/data/shared.js +5 -2
  15. package/src/actions/distributions/revision.js +24 -18
  16. package/src/actions/encodings/color/continuous.js +7 -1
  17. package/src/actions/encodings/remove.js +11 -6
  18. package/src/actions/encodings/shared.js +26 -24
  19. package/src/actions/facets/actions.js +18 -7
  20. package/src/actions/facets/guides.js +2 -1
  21. package/src/actions/guides/axes/axes.js +30 -11
  22. package/src/actions/guides/axes/axis.js +58 -8
  23. package/src/actions/guides/axes/components.js +13 -13
  24. package/src/actions/guides/axes/edit.js +36 -24
  25. package/src/actions/guides/axes/labels.js +37 -27
  26. package/src/actions/guides/axes/lines.js +22 -20
  27. package/src/actions/guides/axes/remove.js +10 -6
  28. package/src/actions/guides/axes/tickGroups.js +8 -7
  29. package/src/actions/guides/axes/ticks.js +31 -22
  30. package/src/actions/guides/axes/titles.js +53 -60
  31. package/src/actions/guides/facadeAxes.js +33 -24
  32. package/src/actions/guides/facadeLegend.js +3 -3
  33. package/src/actions/guides/grids/grid.js +1 -1
  34. package/src/actions/guides/grids/resolve.js +48 -51
  35. package/src/actions/guides/legends/blocks.js +43 -19
  36. package/src/actions/guides/legends/categorical/actions.js +32 -9
  37. package/src/actions/guides/legends/categorical/components.js +3 -10
  38. package/src/actions/guides/legends/categorical/index.js +2 -0
  39. package/src/actions/guides/legends/categorical/layout.js +9 -13
  40. package/src/actions/guides/legends/categorical/options.js +8 -2
  41. package/src/actions/guides/legends/categorical/recipes.js +7 -3
  42. package/src/actions/guides/legends/categorical/resolve.js +9 -2
  43. package/src/actions/guides/legends/categorical/symbols.js +22 -8
  44. package/src/actions/guides/legends/colorSize.js +110 -0
  45. package/src/actions/guides/legends/combine.js +37 -0
  46. package/src/actions/guides/legends/continuous/colorSymbols.js +1 -1
  47. package/src/actions/guides/legends/continuous/common.js +1 -1
  48. package/src/actions/guides/legends/continuous/gradient.js +27 -13
  49. package/src/actions/guides/legends/continuous/opacity.js +16 -11
  50. package/src/actions/guides/legends/creation.js +2 -0
  51. package/src/actions/guides/legends/edit.js +1 -1
  52. package/src/actions/guides/legends/focused.js +6 -5
  53. package/src/actions/guides/legends/lane.js +1 -1
  54. package/src/actions/guides/legends/lifecycle.js +2 -1
  55. package/src/actions/guides/legends/remove.js +6 -3
  56. package/src/actions/guides/legends/size.js +16 -6
  57. package/src/actions/guides/legends/strokeWidth.js +2 -0
  58. package/src/actions/guides/legends/target.js +2 -79
  59. package/src/actions/guides/legends/transition.js +24 -5
  60. package/src/actions/guides/tickValues.js +8 -5
  61. package/src/actions/marks/area/materialize.js +1 -190
  62. package/src/actions/marks/bar/edit.js +23 -12
  63. package/src/actions/marks/bar/materialize.js +6 -1
  64. package/src/actions/marks/point/materialize.js +5 -1
  65. package/src/actions/marks/remove.js +10 -10
  66. package/src/actions/primitives/createGraphics.js +3 -2
  67. package/src/actions/primitives/editGraphics.js +15 -11
  68. package/src/actions/primitives/graphicProperties.js +1 -6
  69. package/src/actions/primitives/semanticAction.js +27 -1
  70. package/src/actions/primitives/semanticValidation/guide.js +11 -1
  71. package/src/actions/primitives/semanticValidation/layer.js +4 -0
  72. package/src/actions/regression/edit.js +15 -3
  73. package/src/actions/scales/channels.js +2 -2
  74. package/src/actions/scales/consumers/common.js +7 -1
  75. package/src/actions/scales/create.js +3 -3
  76. package/src/actions/scales/definitions.js +1 -1
  77. package/src/actions/scales/edit.js +1 -1
  78. package/src/actions/scales/editPolicy.js +3 -0
  79. package/src/actions/scales/patch.js +1 -1
  80. package/src/actions/scales/quantitativeColor.js +8 -3
  81. package/src/actions/theme/reconcile.js +50 -23
  82. package/src/core/compositionState.js +2 -2
  83. package/src/core/graphicProperties.js +1 -0
  84. package/src/core/programState.js +4 -0
  85. package/src/core/resourceReferences.js +2 -2
  86. package/src/core/validation.js +9 -0
  87. package/src/core/vocabulary.js +3 -0
  88. package/src/grammar/aggregate.js +3 -2
  89. package/src/grammar/areaSeries.js +1 -415
  90. package/src/grammar/axisTextPlacement.js +1 -0
  91. package/src/grammar/colorSizeLegend.js +39 -0
  92. package/src/grammar/computed.js +23 -1
  93. package/src/grammar/datasetSchema.js +10 -2
  94. package/src/grammar/displayLabels.js +8 -0
  95. package/src/grammar/facets/guides.js +54 -31
  96. package/src/grammar/facets/legendIntent.js +65 -0
  97. package/src/grammar/guideDash.js +1 -0
  98. package/src/grammar/interval.js +22 -6
  99. package/src/grammar/legendBlocks.js +1 -0
  100. package/src/grammar/legendBorder.js +9 -0
  101. package/src/grammar/legendClip.js +19 -0
  102. package/src/grammar/legendCombinations.js +45 -0
  103. package/src/grammar/legendGradient.js +9 -0
  104. package/src/grammar/legendGroups.js +1 -1
  105. package/src/grammar/legendOpacity.js +6 -0
  106. package/src/grammar/pathSeries.js +2 -2
  107. package/src/grammar/pointShapes.js +16 -2
  108. package/src/grammar/regression/derive.js +9 -1
  109. package/src/grammar/regression/models.js +60 -0
  110. package/src/grammar/regression/parameters.js +9 -1
  111. package/src/grammar/regression/statistics.js +64 -0
  112. package/src/grammar/scales/color.js +1 -1
  113. package/src/grammar/scales/colorConsumers.js +6 -3
  114. package/src/grammar/scales/definition.js +1 -1
  115. package/src/grammar/scales/types.js +1 -1
  116. package/src/grammar/schemas/concreteGraphic.js +3 -0
  117. package/src/grammar/schemas/graphic.js +3 -0
  118. package/src/grammar/schemas/graphicBounds.js +31 -7
  119. package/src/grammar/schemas/semanticPath.js +17 -2
  120. package/src/grammar/statistics/confidenceInterval.js +1 -101
  121. package/src/grammar/summary.js +1 -320
  122. package/src/grammar/timeUnit.js +6 -4
  123. package/src/grammar/transformTopology.js +1 -0
  124. package/src/grammar/transforms.js +11 -1
  125. package/src/inspectionDescriptors.js +1 -1
  126. package/src/layout/guideCollisions.js +9 -1
  127. package/src/layout/legendGroup.js +1 -1
  128. package/src/materialization/compositionSnapshot.js +2 -7
  129. package/src/materialization/coordinateBounds.js +17 -4
  130. package/src/materialization/facetGuides/edit.js +30 -0
  131. package/src/materialization/facetGuides/layout.js +1 -1
  132. package/src/materialization/facetGuides/owner.js +125 -0
  133. package/src/materialization/facetGuides/ownerLayout.js +71 -0
  134. package/src/materialization/facetGuides/placement.js +52 -6
  135. package/src/materialization/facetGuides/preparation.js +7 -0
  136. package/src/materialization/facets.js +1 -1
  137. package/src/materialization/graphicHierarchy.js +5 -1
  138. package/src/materialization/graphicTranslation.js +73 -0
  139. package/src/materialization/guides/layout.js +1 -1
  140. package/src/materialization/guides/resources.js +1 -1
  141. package/src/materialization/legendBorder.js +28 -0
  142. package/src/materialization/legendClip.js +76 -0
  143. package/src/materialization/legendGroups.js +1 -1
  144. package/src/materialization/legends.js +1 -1
  145. package/src/materialization/marks/grouping.js +1 -1
  146. package/src/materialization/scaleGuideDependencies.js +13 -14
  147. package/src/materialization/scales/map.js +3 -0
  148. package/src/materialization/scales/resolve.js +2 -0
  149. package/src/materialization/typography.js +10 -2
  150. package/src/persistence/validation.js +174 -1
  151. package/src/persistence.js +1 -1
  152. package/src/renderers/canvas/index.js +20 -10
  153. package/src/renderers/canvas/isolated.js +166 -0
  154. package/src/renderers/canvas/limits.js +3 -0
  155. package/src/renderers/canvas/rect.js +1 -1
  156. package/src/renderers/pdf.js +2 -2
  157. package/src/renderers/png.js +2 -2
  158. package/src/renderers/svg.js +10 -3
  159. package/src/selectors/axes.js +1 -0
  160. package/src/selectors/graphicCapacity.js +38 -0
  161. package/src/version.js +1 -1
  162. package/types/basic.d.ts +2 -0
  163. package/types/index.d.ts +7 -0
  164. package/types/program.d.ts +220 -64
@@ -1,5 +1,5 @@
1
1
  import { isSourceOwnedText } from "../../../grammar/text.js";
2
- import { readSeriesIdentity } from "../../../grammar/pathSeries.js";
2
+ import { readSeriesIdentity, validatePathSeriesAppearance } from "../../../grammar/pathSeries.js";
3
3
  import { readAreaEndpoint } from "../../../grammar/areaEndpoints.js";
4
4
  import { readScaleField } from "../../../grammar/scales/index.js";
5
5
  import { findDataset } from "../../../selectors/datasets.js";
@@ -59,6 +59,12 @@ export function readConsumerFieldValues(
59
59
  if (consumer.layer.mark.type === "bar" && consumer.layer.encoding?.group !== undefined && ["xOffset", "yOffset"].includes(consumer.channel)) {
60
60
  return readSeriesIdentity(dataset.values, consumer.layer).values;
61
61
  }
62
+ if (consumer.layer.mark.type === "area" && consumer.channel === "color" && fieldType === "quantitative") {
63
+ validatePathSeriesAppearance(dataset.values, consumer.layer);
64
+ const identities = readSeriesIdentity(dataset.values, consumer.layer).values;
65
+ const values = readScaleField(dataset.values, field, fieldType);
66
+ return [...new Map(identities.map((identity, index) => [identity, values[index]])).values()];
67
+ }
62
68
  const parallel = consumer.role === "parallelDimension";
63
69
  if (!parallel && !isDirectCategoricalConsumer(consumer) &&
64
70
  fieldType !== "quantitative" && fieldType !== "temporal" &&
@@ -38,7 +38,7 @@ const CREATE_SCALE_OPTIONS = Object.freeze([
38
38
  "padding",
39
39
  "align",
40
40
  "palette",
41
- "interpolate", "midpoint",
41
+ "interpolate", "whitePoint", "gamma", "midpoint",
42
42
  "radialMapping", "unknown"
43
43
  ]);
44
44
 
@@ -88,7 +88,7 @@ export const createScale = /* @__PURE__ */ action(
88
88
  const id = validateUserId(args.id, "Scale id");
89
89
  const type = validateScaleType(args.type ?? "linear");
90
90
  const colorType = isColorScaleType(type) && (
91
- !["log", "sqrt", "symlog"].includes(type) || args.palette !== undefined ||
91
+ !["log", "pow", "sqrt", "symlog"].includes(type) || args.palette !== undefined ||
92
92
  args.interpolate !== undefined || (Array.isArray(args.range) && typeof args.range[0] === "string") ||
93
93
  args.range?.palette !== undefined
94
94
  );
@@ -171,7 +171,7 @@ export const createScale = /* @__PURE__ */ action(
171
171
 
172
172
  for (const property of [
173
173
  "emptyDomain", "nice", "zero", "clamp", "reverse", "base", "exponent", "constant",
174
- "paddingInner", "paddingOuter", "padding", "align", "interpolate", "midpoint",
174
+ "paddingInner", "paddingOuter", "padding", "align", "interpolate", "whitePoint", "gamma", "midpoint",
175
175
  "radialMapping", "unknown"
176
176
  ]) {
177
177
  if (definition[property] === undefined) continue;
@@ -1 +1 @@
1
- import{prepareScaleEdit as M}from"./editPolicy.js";import{validateUserId as m}from"../../core/identifiers.js";import{isPlainObject as W}from"../../core/immutable.js";import{validateKeys as u}from"../../core/validation.js";import{validateColorRange as U,isContinuousColorScaleType as w,normalizeTransformParameters as B,validateTransformedDomain as F,validateContinuousColorInterpolation as K,validateSequentialMidpoint as Q,validateLinearScaleType as z,validateOpacityRange as H,validateOrdinalDomain as E,validateOrdinalScaleType as N,validateScaleDomain as h,validateScaleRange as _,validateShapeRange as V,validateStrokeDashRange as Y,validateStrokeWidthRange as Z,validateSequentialColorRange as G,validateDiscretizedColorDomain as J,validateDiscretizedColorRange as X,validateTimeScaleType as ee,normalizeScaleDefinition as k,SCALE_ROLES as P,validateScalePropertyForType as A,validateScaleTypeForRole as b,isDiscretePositionScaleType as te,normalizeSizeScaleDefinition as ae,withScaleUnknown as O}from"../../grammar/scales/index.js";import{validateRadialRange as ne,validateThetaRange as ie}from"../../grammar/polar.js";import{findSemanticScale as p}from"../../selectors/scales.js";import{normalizeOffsetScalePolicy as re}from"../../grammar/bars/geometry.js";import{resolveRequestedOffsetPolicy as oe}from"../../materialization/scales/policies/offset.js";import{findScaleConsumers as ce}from"./consumers/index.js";const T=["id","type","domain","range","emptyDomain"],j=[...T,"unknown"],S=["clamp","reverse"],v=["nice","zero",...S],q=["base","exponent","constant"],de=[...T,...v,...q,"paddingInner","paddingOuter","padding","align","unknown"],C=[...T,"palette","unknown"],L=[...C,"interpolate","midpoint","base","constant",...S],le=[...T,...v,"unknown"],se=[...T,...v,...q],ue=[...j,"clamp","reverse","base","exponent"];function y(i){if(!W(i))throw new TypeError("Encoding scale must be a plain object.")}function D(i,e,t){for(const a of e){if(i[a]!==void 0&&typeof i[a]!="boolean")throw new TypeError(`Scale ${a} must be a boolean.`);t!==void 0&&i[a]!==void 0&&A(t,a)}}function R(i,e,t,a){for(const n of a){const r=e[n]??t?.[n];r!==void 0&&(i[n]=r)}return i}function x(i){if(i.palette!==void 0&&i.range!==void 0)throw new Error("Color scale cannot specify both palette and range.")}function Ce(i,e,t,a,n={}){y(a),u(a,de,"scale");const r=m(a.id??e,"Scale id"),o=p(i,r),c=["ordinal","nominal"].includes(t),f=t==="temporal"?"time":c?n.discreteType??"point":"linear",d=a.type??o?.type??f;if(t==="temporal")ee(d);else if(c){if(!te(d))throw new Error(`Scale type "${d}" is not valid for discrete position.`)}else e==="theta"?z(d):b(d,P.quantitativePosition);const l={id:r,...k({type:d,previous:o,patch:{...a,...n.radialMapping===void 0?{}:{radialMapping:n.radialMapping}},defaults:{...o===void 0&&n.nice!==void 0?{nice:n.nice}:{},...o===void 0&&n.zero!==void 0&&(d!=="log"||n.zero)?{zero:n.zero}:{}},retainCoreOnTypeChange:!0,retainCompatibleOnTypeChange:!0,validateDomain:(g,s)=>c?E(s):h(s),validateRange:(g,s)=>e==="theta"?ie(s):e==="radius"?ne(s):_(s)})};return O(l,{...o,...a},e)}function De(i,e,t="color"){y(e),u(e,C,"scale"),x(e);const a=m(e.id??t,"Scale id"),n=p(i,a),r=n?.type==="ordinal"?n:void 0,o=e.palette===void 0?e.range:{palette:e.palette};return O({id:a,type:N(e.type??r?.type??"ordinal"),domain:E(e.domain??r?.domain??"auto"),range:U(o??r?.range??"auto")},{...r,...e},t)}function fe(i,e){if(i==="auto")return i;if(!Array.isArray(i)||i.length!==2)throw new TypeError("Continuous color domain must contain two values or auto.");const t=i.map(a=>e==="quantitative"?a:typeof a=="string"?Date.parse(a):a);if(!t.every(Number.isFinite)||t[0]===t[1])throw new TypeError("Continuous color domain requires two distinct valid values.");return t}function me(i,e,t,a="color"){y(t),u(t,L,"scale"),x(t);const n=m(t.id??a,"Scale id"),r=p(i,n),o=w(r?.type)?r:void 0,c=t.type??o?.type??"sequential";if(!w(c))throw new Error(`Unsupported continuous color scale type "${c}".`);D(t,S);const f=t.palette===void 0?t.range:{palette:t.palette},d={id:n,type:c,domain:fe(t.domain??o?.domain??"auto",e),range:G(f??o?.range??{palette:"viridis"}),interpolate:K(t.interpolate??o?.interpolate??"rgb")};if(c!=="sequential"&&e!=="quantitative")throw new Error(`Scale type "${c}" requires quantitative color.`);const l=Object.fromEntries(["base","constant"].flatMap(s=>{const I=t[s]??o?.[s];return I===void 0?[]:[[s,I]]}));if(c!=="sequential"){const s=B(c,l);c==="log"&&(d.base=s.base),c==="symlog"&&(d.constant=s.constant),d.domain!=="auto"&&F(c,d.domain,l)}else if(Object.keys(l).length)throw new Error("Sequential color does not support transform parameters.");const g=Q(Object.hasOwn(t,"midpoint")?t.midpoint:o?.midpoint,c,d.domain);if(g!==void 0){if(e!=="quantitative")throw new Error("Scale midpoint requires quantitative color consumers.");d.midpoint=g}return O(R(d,t,o,S),{...o,...t},a)}function Re(i,e,t,a="color"){y(t);const n=p(i,t.id??a),r=n!==void 0&&(w(n.type)||["quantize","quantile","threshold"].includes(n.type)),o=t.type??(r?n.type:"sequential");if(r&&n.type!==o){if(u(t,w(o)?L:[...C,...S],"scale"),e!=="quantitative")throw new Error("Color scale type transition requires quantitative color.");return{id:n.id,...M(i,n,a,[],t)}}if(w(o))return me(i,e,t,a);if(u(t,[...C,...S],"scale"),e!=="quantitative")throw new Error(`Scale type "${o}" requires quantitative color.`);b(o,P.discretizedColor),x(t),D(t,S,o);const c=m(t.id??a,"Scale id"),f=n?.type===o?n:void 0,d=t.palette===void 0?t.range:{palette:t.palette},l={id:c,type:o,domain:J(o,t.domain??f?.domain??"auto"),range:X(d??f?.range??{palette:"viridis"})};return O(R(l,t,f,S),{...n,...t},a)}function $(i,e,t,a){y(e),u(e,j,"scale");const n=m(e.id??t,"Scale id"),r=p(i,n);return O({id:n,type:N(e.type??r?.type??"ordinal"),domain:E(e.domain??r?.domain??"auto"),range:a(e.range??r?.range??"auto")},{...r,...e},t)}function he(i,e){return $(i,e,"strokeDash",Y)}function Pe(i,e,t,a="quantitative"){if(y(t),e==="size"){u(t,ue,"scale");const n=m(t.id??e,"Scale id"),r=p(i,n),o=["nominal","ordinal"].includes(a),c=ae({previous:r,patch:{...t,type:t.type??r?.type??(o?"ordinal":"linear")}});if(c.type==="ordinal"!==o)throw new Error("Size scale type must match its field type.");return{id:n,...c}}return $(i,t,e,V)}function be(i,e){y(e),u(e,le,"scale");const t=m(e.id??"opacity","Scale id"),a=p(i,t),n=z(e.type??a?.type??"linear");D(e,v,n);const r={id:t,type:n,domain:h(e.domain??a?.domain??"auto"),range:H(e.range??a?.range??"auto")};return O(R(r,e,a,v),{...a,...e},"opacity")}function xe(i,e){y(e),u(e,se,"scale");const t=m(e.id??"strokeWidth","Scale id"),a=p(i,t),n=e.type??a?.type??"linear";b(n,P.quantitativePosition),D(e,v,n);for(const c of q)e[c]!==void 0&&A(n,c);const r=h(e.domain??a?.domain??"auto");if(r!=="auto"&&r.some(c=>c<0))throw new RangeError("StrokeWidth scale domain cannot contain negative values.");const o={id:t,type:n,domain:r,range:Z(e.range??a?.range??"auto")};return R(o,e,a,[...v,...q])}function Ie(i,e,t="xOffset"){y(e),u(e,["id","type","domain","range","reverse","padding","paddingInner","paddingOuter","align"],"scale");const a=m(e.id??t,"Scale id"),n=p(i,a),r=e.type??n?.type??"ordinal";if(r!=="ordinal")throw new Error(`Scale type "${r}" is not valid for ${t}.`);if(Object.hasOwn(e,"range")&&e.range!=="auto")throw new Error(`${t} scale range is derived from its parent categorical slot.`);const o=ce(i,a).filter(l=>l.channel===t),c=oe({scale:n,consumers:o,markConfigs:i.markConfigs,id:a,channel:t}),f=re(e,c,t),d=Object.fromEntries(Object.entries(e).filter(([l])=>!["id","padding"].includes(l)));return{id:a,...k({type:r,previous:n,patch:{...d,...f},allowOrdinalBandParameters:!0,validateDomain:(l,g)=>E(g),validateRange:(l,g)=>_(g)})}}export{Pe as resolveAppearanceScaleDefinition,De as resolveColorScaleDefinition,Ie as resolveOffsetScaleDefinition,be as resolveOpacityScaleDefinition,Ce as resolvePositionScaleDefinition,Re as resolveQuantitativeColorScaleDefinition,me as resolveSequentialColorScaleDefinition,he as resolveStrokeDashScaleDefinition,xe as resolveStrokeWidthScaleDefinition};
1
+ import{prepareScaleEdit as U}from"./editPolicy.js";import{validateUserId as p}from"../../core/identifiers.js";import{isPlainObject as B}from"../../core/immutable.js";import{validateKeys as f}from"../../core/validation.js";import{validateColorRange as F,isContinuousColorScaleType as E,normalizeTransformParameters as K,validateTransformedDomain as Q,validateContinuousColorInterpolation as H,normalizeColorInterpolationOptions as V,validateSequentialMidpoint as Y,validateLinearScaleType as _,validateOpacityRange as Z,validateOrdinalDomain as h,validateOrdinalScaleType as j,validateScaleDomain as P,validateScaleRange as k,validateShapeRange as G,validateStrokeDashRange as J,validateStrokeWidthRange as X,validateSequentialColorRange as ee,validateDiscretizedColorDomain as te,validateDiscretizedColorRange as ne,validateTimeScaleType as ae,normalizeScaleDefinition as A,SCALE_ROLES as R,validateScalePropertyForType as L,validateScaleTypeForRole as I,isDiscretePositionScaleType as ie,normalizeSizeScaleDefinition as re,withScaleUnknown as w}from"../../grammar/scales/index.js";import{validateRadialRange as oe,validateThetaRange as ce}from"../../grammar/polar.js";import{findSemanticScale as g}from"../../selectors/scales.js";import{normalizeOffsetScalePolicy as le}from"../../grammar/bars/geometry.js";import{resolveRequestedOffsetPolicy as de}from"../../materialization/scales/policies/offset.js";import{findScaleConsumers as se}from"./consumers/index.js";const C=["id","type","domain","range","emptyDomain"],M=[...C,"unknown"],S=["clamp","reverse"],O=["nice","zero",...S],q=["base","exponent","constant"],ue=[...C,...O,...q,"paddingInner","paddingOuter","padding","align","unknown"],D=[...C,"palette","unknown"],$=[...D,"interpolate","whitePoint","gamma","midpoint","base","exponent","constant",...S],fe=[...C,...O,"unknown"],me=[...C,...O,...q],pe=[...M,"clamp","reverse","base","exponent"];function y(i){if(!B(i))throw new TypeError("Encoding scale must be a plain object.")}function b(i,t,e){for(const n of t){if(i[n]!==void 0&&typeof i[n]!="boolean")throw new TypeError(`Scale ${n} must be a boolean.`);e!==void 0&&i[n]!==void 0&&L(e,n)}}function x(i,t,e,n){for(const a of n){const o=t[a]??e?.[a];o!==void 0&&(i[a]=o)}return i}function z(i){if(i.palette!==void 0&&i.range!==void 0)throw new Error("Color scale cannot specify both palette and range.")}function be(i,t,e,n,a={}){y(n),f(n,ue,"scale");const o=p(n.id??t,"Scale id"),r=g(i,o),c=["ordinal","nominal"].includes(e),m=e==="temporal"?"time":c?a.discreteType??"point":"linear",l=n.type??r?.type??m;if(e==="temporal")ae(l);else if(c){if(!ie(l))throw new Error(`Scale type "${l}" is not valid for discrete position.`)}else t==="theta"?_(l):I(l,R.quantitativePosition);const s={id:o,...A({type:l,previous:r,patch:{...n,...a.radialMapping===void 0?{}:{radialMapping:a.radialMapping}},defaults:{...r===void 0&&a.nice!==void 0?{nice:a.nice}:{},...r===void 0&&a.zero!==void 0&&(l!=="log"||a.zero)?{zero:a.zero}:{}},retainCoreOnTypeChange:!0,retainCompatibleOnTypeChange:!0,validateDomain:(v,u)=>c?h(u):P(u),validateRange:(v,u)=>t==="theta"?ce(u):t==="radius"?oe(u):k(u)})};return w(s,{...r,...n},t)}function xe(i,t,e="color"){y(t),f(t,D,"scale"),z(t);const n=p(t.id??e,"Scale id"),a=g(i,n),o=a?.type==="ordinal"?a:void 0,r=t.palette===void 0?t.range:{palette:t.palette};return w({id:n,type:j(t.type??o?.type??"ordinal"),domain:h(t.domain??o?.domain??"auto"),range:F(r??o?.range??"auto")},{...o,...t},e)}function ge(i,t){if(i==="auto")return i;if(!Array.isArray(i)||i.length!==2)throw new TypeError("Continuous color domain must contain two values or auto.");const e=i.map(n=>t==="quantitative"?n:typeof n=="string"?Date.parse(n):n);if(!e.every(Number.isFinite)||e[0]===e[1])throw new TypeError("Continuous color domain requires two distinct valid values.");return e}function ye(i,t,e,n="color"){y(e),f(e,$,"scale"),z(e);const a=p(e.id??n,"Scale id"),o=g(i,a),r=E(o?.type)?o:void 0,c=e.type??r?.type??"sequential";if(!E(c))throw new Error(`Unsupported continuous color scale type "${c}".`);b(e,S);const m=e.palette===void 0?e.range:{palette:e.palette},l={id:a,type:c,domain:ge(e.domain??r?.domain??"auto",t),range:ee(m??r?.range??{palette:"viridis"}),interpolate:H(e.interpolate??r?.interpolate??"rgb")},s=Object.hasOwn(e,"interpolate")&&e.interpolate!==r?.interpolate,v=Object.fromEntries(["whitePoint","gamma"].flatMap(d=>{const T=Object.hasOwn(e,d)?e[d]:s?void 0:r?.[d];return T===void 0?[]:[[d,T]]}));if(Object.assign(l,V(l.interpolate,v)),c!=="sequential"&&t!=="quantitative")throw new Error(`Scale type "${c}" requires quantitative color.`);const u=Object.fromEntries(["base","exponent","constant"].flatMap(d=>{const T=e[d]??(c===r?.type?r?.[d]:void 0);return T===void 0?[]:[[d,T]]}));if(c!=="sequential"){const d=K(c,u);c==="log"&&(l.base=d.base),c==="pow"&&(l.exponent=d.exponent),c==="symlog"&&(l.constant=d.constant),l.domain!=="auto"&&Q(c,l.domain,u)}else if(Object.keys(u).length)throw new Error("Sequential color does not support transform parameters.");const N=Y(Object.hasOwn(e,"midpoint")?e.midpoint:r?.midpoint,c,l.domain);if(N!==void 0){if(t!=="quantitative")throw new Error("Scale midpoint requires quantitative color consumers.");l.midpoint=N}return w(x(l,e,r,S),{...r,...e},n)}function Pe(i,t,e,n="color"){y(e);const a=g(i,e.id??n),o=a!==void 0&&(E(a.type)||["quantize","quantile","threshold"].includes(a.type)),r=e.type??(o?a.type:"sequential");if(o&&a.type!==r){if(f(e,E(r)?$:[...D,...S],"scale"),t!=="quantitative")throw new Error("Color scale type transition requires quantitative color.");return{id:a.id,...U(i,a,n,[],e)}}if(E(r))return ye(i,t,e,n);if(f(e,[...D,...S],"scale"),t!=="quantitative")throw new Error(`Scale type "${r}" requires quantitative color.`);I(r,R.discretizedColor),z(e),b(e,S,r);const c=p(e.id??n,"Scale id"),m=a?.type===r?a:void 0,l=e.palette===void 0?e.range:{palette:e.palette},s={id:c,type:r,domain:te(r,e.domain??m?.domain??"auto"),range:ne(l??m?.range??{palette:"viridis"})};return w(x(s,e,m,S),{...a,...e},n)}function W(i,t,e,n){y(t),f(t,M,"scale");const a=p(t.id??e,"Scale id"),o=g(i,a);return w({id:a,type:j(t.type??o?.type??"ordinal"),domain:h(t.domain??o?.domain??"auto"),range:n(t.range??o?.range??"auto")},{...o,...t},e)}function Re(i,t){return W(i,t,"strokeDash",J)}function Ie(i,t,e,n="quantitative"){if(y(e),t==="size"){f(e,pe,"scale");const a=p(e.id??t,"Scale id"),o=g(i,a),r=["nominal","ordinal"].includes(n),c=re({previous:o,patch:{...e,type:e.type??o?.type??(r?"ordinal":"linear")}});if(c.type==="ordinal"!==r)throw new Error("Size scale type must match its field type.");return{id:a,...c}}return W(i,e,t,G)}function ze(i,t){y(t),f(t,fe,"scale");const e=p(t.id??"opacity","Scale id"),n=g(i,e),a=_(t.type??n?.type??"linear");b(t,O,a);const o={id:e,type:a,domain:P(t.domain??n?.domain??"auto"),range:Z(t.range??n?.range??"auto")};return w(x(o,t,n,O),{...n,...t},"opacity")}function Ne(i,t){y(t),f(t,me,"scale");const e=p(t.id??"strokeWidth","Scale id"),n=g(i,e),a=t.type??n?.type??"linear";I(a,R.quantitativePosition),b(t,O,a);for(const c of q)t[c]!==void 0&&L(a,c);const o=P(t.domain??n?.domain??"auto");if(o!=="auto"&&o.some(c=>c<0))throw new RangeError("StrokeWidth scale domain cannot contain negative values.");const r={id:e,type:a,domain:o,range:X(t.range??n?.range??"auto")};return x(r,t,n,[...O,...q])}function _e(i,t,e="xOffset"){y(t),f(t,["id","type","domain","range","reverse","padding","paddingInner","paddingOuter","align"],"scale");const n=p(t.id??e,"Scale id"),a=g(i,n),o=t.type??a?.type??"ordinal";if(o!=="ordinal")throw new Error(`Scale type "${o}" is not valid for ${e}.`);if(Object.hasOwn(t,"range")&&t.range!=="auto")throw new Error(`${e} scale range is derived from its parent categorical slot.`);const r=se(i,n).filter(s=>s.channel===e),c=de({scale:a,consumers:r,markConfigs:i.markConfigs,id:n,channel:e}),m=le(t,c,e),l=Object.fromEntries(Object.entries(t).filter(([s])=>!["id","padding"].includes(s)));return{id:n,...A({type:o,previous:a,patch:{...l,...m},allowOrdinalBandParameters:!0,validateDomain:(s,v)=>h(v),validateRange:(s,v)=>k(v)})}}export{Ie as resolveAppearanceScaleDefinition,xe as resolveColorScaleDefinition,_e as resolveOffsetScaleDefinition,ze as resolveOpacityScaleDefinition,be as resolvePositionScaleDefinition,Pe as resolveQuantitativeColorScaleDefinition,ye as resolveSequentialColorScaleDefinition,Re as resolveStrokeDashScaleDefinition,Ne as resolveStrokeWidthScaleDefinition};
@@ -28,7 +28,7 @@ import { hasExactLegendSamplingForScale } from
28
28
  const OPTIONS = Object.freeze([
29
29
  "id", "type", "domain", "range", "emptyDomain", "nice", "zero", "clamp", "reverse",
30
30
  "base", "exponent", "constant", "paddingInner", "paddingOuter", "padding",
31
- "align", "interpolate", "midpoint", "unknown", "palette", "radialMapping"
31
+ "align", "interpolate", "whitePoint", "gamma", "midpoint", "unknown", "palette", "radialMapping"
32
32
  ]);
33
33
  const EDITABLE = Object.freeze(
34
34
  OPTIONS.filter(option => !["id", "palette"].includes(option))
@@ -12,6 +12,7 @@ import {
12
12
  validateDiscretizedColorDomain,
13
13
  validateDiscretizedColorRange,
14
14
  validateOrdinalDomain,
15
+ validateOrdinalRange,
15
16
  validateScaleDomain,
16
17
  validateScaleRange,
17
18
  validateScaleType,
@@ -66,6 +67,7 @@ function validateRangeForChannel(scale, channel, value) {
66
67
  if (channel === "strokeDash") return validateStrokeDashRange(value);
67
68
  if (channel === "size") return validateSizeRange(value);
68
69
  if (channel === "strokeWidth") return validateStrokeWidthRange(value);
70
+ if (channel === undefined) return validateOrdinalRange(value);
69
71
  return validateScaleRange(value);
70
72
  }
71
73
 
@@ -217,6 +219,7 @@ function normalizeDefinition(program, scale, channel, consumers, patch) {
217
219
  patch: normalizedPatch,
218
220
  retainCoreOnTypeChange: true,
219
221
  retainCompatibleOnTypeChange: true,
222
+ allowColorReset: true,
220
223
  allowOrdinalBandParameters: offset,
221
224
  validateDomain: (scaleType, value) =>
222
225
  isDiscretizedColorScaleType(scaleType)
@@ -1,7 +1,7 @@
1
1
  const SCALE_EDIT_PROPERTIES = Object.freeze([
2
2
  "type", "domain", "range", "emptyDomain", "nice", "zero", "clamp", "reverse",
3
3
  "base", "exponent", "constant", "paddingInner", "paddingOuter",
4
- "padding", "align", "interpolate", "midpoint", "unknown"
4
+ "padding", "align", "interpolate", "whitePoint", "gamma", "midpoint", "unknown"
5
5
  ]);
6
6
 
7
7
  function sameValue(left, right) {
@@ -7,7 +7,7 @@ const OPTIONS = Object.freeze([
7
7
  "type",
8
8
  "domain",
9
9
  "range",
10
- "interpolate", "midpoint", "base", "constant",
10
+ "interpolate", "whitePoint", "gamma", "midpoint", "base", "exponent", "constant",
11
11
  "clamp",
12
12
  "reverse",
13
13
  "unknown"
@@ -19,7 +19,7 @@ export const setQuantitativeColorScale = /* @__PURE__ */ closedAction(
19
19
  description: "Create or update an internal quantitative color scale."
20
20
  }, OPTIONS,
21
21
  function (args = {}) {
22
- if (!["sequential", "log", "sqrt", "symlog", "quantize", "quantile", "threshold"].includes(args.type)) {
22
+ if (!["sequential", "log", "pow", "sqrt", "symlog", "quantize", "quantile", "threshold"].includes(args.type)) {
23
23
  throw new Error(`Unsupported quantitative color scale type "${args.type}".`);
24
24
  }
25
25
  const existing = findSemanticScale(this, args.id);
@@ -30,11 +30,16 @@ export const setQuantitativeColorScale = /* @__PURE__ */ closedAction(
30
30
  return this.editScale(args);
31
31
  }
32
32
  let next = this;
33
+ for (const property of ["whitePoint", "gamma"]) {
34
+ if (existing?.[property] !== undefined && args[property] === undefined) {
35
+ next = next.editSemantic({ property: `scale[${args.id}].${property}`, remove: true });
36
+ }
37
+ }
33
38
  if (existing?.midpoint !== undefined && args.midpoint === undefined) {
34
39
  next = next.editSemantic({ property: `scale[${args.id}].midpoint`, remove: true });
35
40
  }
36
41
  for (const property of [
37
- "type", "domain", "range", "interpolate", "midpoint", "base", "constant", "clamp", "reverse", "unknown"
42
+ "type", "domain", "range", "interpolate", "whitePoint", "gamma", "midpoint", "base", "exponent", "constant", "clamp", "reverse", "unknown"
38
43
  ]) {
39
44
  if (!Object.hasOwn(args, property)) continue;
40
45
  if (existing?.[property] === args[property]) continue;
@@ -1,3 +1,4 @@
1
+ import { synchronizeFacetLegendAppearance } from "../../materialization/facetGuides/owner.js";
1
2
  import { rematerializeTypography } from "../../materialization/typography.js";
2
3
  import { AXIS_LABEL_FONT_SIZES, AXIS_TITLE_FONT_SIZE, themeTokens } from "../../theme/defaults.js";
3
4
  import {
@@ -182,7 +183,7 @@ function componentRole(program, id, property) {
182
183
  }
183
184
 
184
185
  function graphicRole(program, id, property) {
185
- const axisPart = /Axis(Labels|Titles?)$/u.exec(id);
186
+ const axisPart = /(?:Axis|^axis_.+__)(Labels|Titles?)$/u.exec(id);
186
187
  const scoped = axisPart === null ? undefined : axisTextRole(property, axisPart[1] === "Labels" ? "labels" : "title");
187
188
  if (scoped !== undefined) return scoped;
188
189
  if (property === "fontFamily") return "fontFamily";
@@ -200,11 +201,11 @@ function graphicRole(program, id, property) {
200
201
  }
201
202
  return layer.mark?.type === "text" ? "text" : "mark";
202
203
  }
203
- if (/AxisLines?$/u.test(id) && property === "stroke") return "axis";
204
- if (/AxisTicks$/u.test(id) && property === "stroke") return "mutedText";
204
+ if (/(?:Axis|^axis_.+__)Lines?$/u.test(id) && property === "stroke") return "axis";
205
+ if (/(?:Axis|^axis_.+__)Ticks$/u.test(id) && property === "stroke") return "mutedText";
205
206
  if (/^parallelAxisLabels$/u.test(id) && property === "fill") return "axis";
206
- if (/AxisLabels$/u.test(id) && property === "fill") return "text";
207
- if (/AxisTitles?$/u.test(id) && property === "fill") return "axisTitle";
207
+ if (/(?:Axis|^axis_.+__)Labels$/u.test(id) && property === "fill") return "text";
208
+ if (/(?:Axis|^axis_.+__)Titles?$/u.test(id) && property === "fill") return "axisTitle";
208
209
  if (/Grid(?:Lines|Circles)?$/u.test(id) && property === "stroke") return "grid";
209
210
  if (id === "chartTitle" && property === "fill") return "strongText";
210
211
  if (id === "chartSubtitle" && property === "fill") return "mutedText";
@@ -676,12 +677,13 @@ function clearRemovedResources(overrides, node) {
676
677
  clearPrefix(overrides, `g:${mark[1]}.`);
677
678
  return;
678
679
  }
679
- const axis = /^guide\.axis\.(x|y|theta|radius)$/u
680
+ const axis = /^guide\.axis\.([A-Za-z0-9_-]+)$/u
680
681
  .exec(current.args.property);
681
682
  if (axis !== null) {
682
683
  const prefix = axis[1] === "radius" ? "radial" : axis[1];
684
+ const graphicPrefix = ["x", "y", "theta", "radius"].includes(axis[1]) ? `${prefix}Axis` : `axis_${axis[1]}__`;
683
685
  clearPrefix(overrides, `c:guides.axis.${axis[1]}.`);
684
- clearPrefix(overrides, `g:${prefix}Axis`);
686
+ clearPrefix(overrides, `g:${graphicPrefix}`);
685
687
  return;
686
688
  }
687
689
  const grid = /^guide\.grid\.([^\.]+)$/u.exec(current.args.property);
@@ -737,7 +739,23 @@ function clearRecreatedOverrides(overrides, op, args) {
737
739
  }
738
740
  }
739
741
 
740
- function addAxisOverrides(overrides, op, args) {
742
+ function axisOverrideId(node, channel, args) {
743
+ if (args.target !== undefined || args.id !== undefined) return args.target ?? args.id;
744
+ const ids = new Set();
745
+ const visit = entry => {
746
+ if (["createGraphics", "editGraphics"].includes(entry.op)) {
747
+ const id = entry.args.target ?? entry.args.id;
748
+ const named = /^axis_(.+)__(Line|Ticks|Labels|Title)$/u.exec(id ?? "");
749
+ if (named !== null) ids.add(named[1]);
750
+ else if (new RegExp(`^${channel}Axis(?:Line|Ticks|Labels|Title)$`).test(id ?? "")) ids.add(channel);
751
+ }
752
+ entry.children?.forEach(visit);
753
+ };
754
+ if (node !== undefined) visit(node);
755
+ return ids.size === 1 ? [...ids][0] : channel;
756
+ }
757
+
758
+ function addAxisOverrides(overrides, op, args, node) {
741
759
  const match = /^(?:create|edit)(X|Y|Theta|Radial)Axis(Line|Ticks|Labels|Title)$/u
742
760
  .exec(op);
743
761
  if (match === null) return;
@@ -748,23 +766,25 @@ function addAxisOverrides(overrides, op, args) {
748
766
  Radial: "radius"
749
767
  }[match[1]];
750
768
  const component = match[2].toLowerCase();
769
+ const axisId = axisOverrideId(node, channel, args);
751
770
  const idPrefix = match[1] === "Radial" ? "radial" : channel;
752
771
  if (Object.hasOwn(args, "color")) {
753
- overrides.add(`c:guides.axis.${channel}.${component}.color`);
772
+ overrides.add(`c:guides.axis.${axisId}.${component}.color`);
754
773
  overrides.add(
755
- `g:${idPrefix}Axis${match[2]}.${component === "line" || component === "ticks" ? "stroke" : "fill"}`
774
+ `g:${axisId === channel ? `${idPrefix}Axis` : `axis_${axisId}__`}${match[2]}.${component === "line" || component === "ticks" ? "stroke" : "fill"}`
756
775
  );
757
776
  }
758
777
  for (const property of ["fontFamily", "fontSize"]) {
759
778
  if (["labels", "title"].includes(component) && Object.hasOwn(args, property)) {
760
- overrides.add(`c:guides.axis.${channel}.${component}.${property}`);
761
- overrides.add(`g:${idPrefix}Axis${match[2]}.${property}`);
779
+ overrides.add(`c:guides.axis.${axisId}.${component}.${property}`);
780
+ overrides.add(`g:${axisId === channel ? `${idPrefix}Axis` : `axis_${axisId}__`}${match[2]}.${property}`);
762
781
  }
763
782
  }
764
783
  }
765
784
 
766
- function addCompleteAxisOverride(overrides, name, args) {
785
+ function addCompleteAxisOverride(overrides, name, args, node) {
767
786
  const channel = { X: "x", Y: "y", Theta: "theta", Radial: "radius" }[name];
787
+ const axisId = axisOverrideId(node, channel, args);
768
788
  const idPrefix = name === "Radial" ? "radial" : channel;
769
789
  const components = {
770
790
  line: args.line,
@@ -779,21 +799,21 @@ function addCompleteAxisOverride(overrides, name, args) {
779
799
  for (const [component, options] of Object.entries(components)) {
780
800
  const suffix = `${component[0].toUpperCase()}${component.slice(1)}`;
781
801
  if (Object.hasOwn(options ?? {}, "color")) {
782
- overrides.add(`c:guides.axis.${channel}.${component}.color`);
802
+ overrides.add(`c:guides.axis.${axisId}.${component}.color`);
783
803
  overrides.add(
784
- `g:${idPrefix}Axis${suffix}.${component === "line" || component === "ticks" ? "stroke" : "fill"}`
804
+ `g:${axisId === channel ? `${idPrefix}Axis` : `axis_${axisId}__`}${suffix}.${component === "line" || component === "ticks" ? "stroke" : "fill"}`
785
805
  );
786
806
  }
787
807
  for (const property of ["fontFamily", "fontSize"]) {
788
808
  if (["labels", "title"].includes(component) && Object.hasOwn(options ?? {}, property)) {
789
- overrides.add(`c:guides.axis.${channel}.${component}.${property}`);
790
- overrides.add(`g:${idPrefix}Axis${suffix}.${property}`);
809
+ overrides.add(`c:guides.axis.${axisId}.${component}.${property}`);
810
+ overrides.add(`g:${axisId === channel ? `${idPrefix}Axis` : `axis_${axisId}__`}${suffix}.${property}`);
791
811
  }
792
812
  }
793
813
  }
794
814
  }
795
815
 
796
- function addCompleteAxisOverrides(overrides, op, args) {
816
+ function addCompleteAxisOverrides(overrides, op, args, node) {
797
817
  if (args.guides?.axes !== null && typeof args.guides?.axes === "object") {
798
818
  addCompleteAxisOverrides(overrides, "createAxes", args.guides.axes);
799
819
  }
@@ -801,10 +821,10 @@ function addCompleteAxisOverrides(overrides, op, args) {
801
821
  addCompleteAxisOverrides(overrides, "createAxes", args.axes);
802
822
  }
803
823
  const match = /^(?:create|edit)(X|Y|Theta|Radial)Axis$/u.exec(op);
804
- if (match !== null) addCompleteAxisOverride(overrides, match[1], args);
824
+ if (match !== null) addCompleteAxisOverride(overrides, match[1], args, node);
805
825
  const group = /^(?:create|edit)(X|Y)AxisTicksAndLabels$/u.exec(op);
806
826
  if (group !== null) {
807
- addCompleteAxisOverride(overrides, group[1], { ticksAndLabels: args });
827
+ addCompleteAxisOverride(overrides, group[1], { target: args.target, ticksAndLabels: args }, node);
808
828
  }
809
829
  if (op !== "createAxes") return;
810
830
  for (const [option, name] of [
@@ -814,7 +834,7 @@ function addCompleteAxisOverrides(overrides, op, args) {
814
834
  ["radius", "Radial"]
815
835
  ]) {
816
836
  if (args[option] && typeof args[option] === "object") {
817
- addCompleteAxisOverride(overrides, name, args[option]);
837
+ for (const entry of Array.isArray(args[option]) ? args[option] : [args[option]]) addCompleteAxisOverride(overrides, name, entry);
818
838
  }
819
839
  }
820
840
  }
@@ -977,8 +997,8 @@ function explicitOverrides(program, node) {
977
997
  addErrorBarOverrides(overrides, program, node);
978
998
  addErrorBandOverrides(overrides, program, node);
979
999
  addRegressionOverrides(overrides, node);
980
- addAxisOverrides(overrides, op, args);
981
- addCompleteAxisOverrides(overrides, op, args);
1000
+ addAxisOverrides(overrides, op, args, node);
1001
+ addCompleteAxisOverrides(overrides, op, args, node);
982
1002
  addGridOverrides(overrides, op, args);
983
1003
  addParallelOverrides(overrides, program, op, args);
984
1004
  if (["createTitle", "editTitle"].includes(op)) {
@@ -999,6 +1019,11 @@ function explicitOverrides(program, node) {
999
1019
  if (op === "editLegendBlock") {
1000
1020
  addLegendBlockOverrides(overrides, program, args);
1001
1021
  }
1022
+ const parentLegendOptions = op === "editFacetGuides" ? args.legend
1023
+ : ["facet", "facetGrid", "repeatCharts"].includes(op) ? args.guides?.legend : undefined;
1024
+ if (parentLegendOptions !== null && typeof parentLegendOptions === "object") {
1025
+ addLegendOverrides(overrides, program, parentLegendOptions);
1026
+ }
1002
1027
  if (op === "editFacetHeaders") {
1003
1028
  addFacetOverrides(overrides, program, args);
1004
1029
  }
@@ -1084,6 +1109,8 @@ export function reconcileProgramTheme(program, { source, metadata }) {
1084
1109
  next = next._withMaterializationConfig(["facets"], configs.value.facets);
1085
1110
  }
1086
1111
 
1112
+ if (configs.changed) next = synchronizeFacetLegendAppearance(next);
1113
+
1087
1114
  const facetsChanged = configs.value.facets !==
1088
1115
  themed.materializationConfigs.facets;
1089
1116
  const typographyChanged = sourceTokens.fontFamily !== targetTokens.fontFamily ||
@@ -202,10 +202,10 @@ function validateFacetSpec(compositionSpec) {
202
202
  !isPlainObject(guides) ||
203
203
  Object.keys(guides).some(key => !["axes", "legend"].includes(key)) ||
204
204
  !["each", "outer"].includes(guides.axes) ||
205
- ![false, "shared"].includes(guides.legend)
205
+ (![false, "shared"].includes(guides.legend) && !isPlainObject(guides.legend))
206
206
  ) {
207
207
  throw new Error(
208
- 'compositionSpec.facet.guides requires axes "each" or "outer" and legend false or "shared".'
208
+ 'compositionSpec.facet.guides requires axes "each" or "outer" and legend false, "shared", or parent legend options.'
209
209
  );
210
210
  }
211
211
  }
@@ -0,0 +1 @@
1
+ function n(e,t,i){for(const[r,c]of Object.entries(i))e=e.editGraphics({target:t,property:r,value:c});return e}export{n as editGraphicProperties};
@@ -44,6 +44,10 @@ export function releaseNamedResourceOwnership(program, { kind, id, dataOwner })
44
44
  materializationConfigs,
45
45
  ["calculations", "datasets", dataOwner?.current ?? id]
46
46
  ).value;
47
+ materializationConfigs = removeMaterializationConfig(
48
+ materializationConfigs,
49
+ ["regressionFits", dataOwner?.current ?? id]
50
+ ).value;
47
51
  const context = program.context.currentData === id
48
52
  ? freezeOwned({ ...program.context, currentData: undefined })
49
53
  : program.context;
@@ -97,7 +97,7 @@ export function collectSemanticScaleReferences(program) {
97
97
  }
98
98
  }
99
99
  collectKnownKeys(program.semanticSpec.guides, [], (key, value, path) => {
100
- if (key === "scale") {
100
+ if (["scale", "colorScale", "sizeScale"].includes(key)) {
101
101
  append(references, reference(
102
102
  "scale", value, "semanticGuide", "guides", path
103
103
  ));
@@ -208,7 +208,7 @@ export function collectGuideConfigReferences(program) {
208
208
  const relativePath = parentPath.length === 0
209
209
  ? path
210
210
  : path.slice(parentPath.length);
211
- if (key === "scale") {
211
+ if (["scale", "colorScale", "sizeScale"].includes(key)) {
212
212
  append(references, reference(
213
213
  "scale", value, family, ownerId, relativePath
214
214
  ));
@@ -1,5 +1,6 @@
1
1
  import { annotateError } from "./diagnostics.js";
2
2
  import { isPlainObject } from "./immutable.js";
3
+ import { getMarkGraphicTypes } from "./vocabulary.js";
3
4
 
4
5
  export const MAX_GENERATED_ITEMS = 10_000;
5
6
  export const MAX_WORK_ITEMS = 10_000_000;
@@ -30,6 +31,14 @@ export function validateWorkLimit(value, label) {
30
31
  return validateGeneratedItemLimit(value, label, MAX_WORK_ITEMS);
31
32
  }
32
33
 
34
+ export function validateGraphicItemLimit(value, capacity) {
35
+ const point = capacity.pointOwned && getMarkGraphicTypes("point").includes(capacity.graphicType);
36
+ const label = point ? "Point mark item count" : capacity.label;
37
+ const maximum = point ? 50_000 : MAX_GENERATED_ITEMS;
38
+ if (point) validateGeneratedItemLimit(capacity.rawCount, label, maximum);
39
+ return validateGeneratedItemLimit(value, label, maximum);
40
+ }
41
+
33
42
  export function validateKeys(value, supported, label) {
34
43
  for (const key of Object.keys(value)) {
35
44
  if (!supported.includes(key)) {
@@ -220,6 +220,7 @@ export const LEGEND_CHANNELS = Object.freeze([
220
220
  ]);
221
221
 
222
222
  export const LEGEND_CONFIG_KINDS = Object.freeze([
223
+ "colorSize",
223
224
  "series",
224
225
  "color",
225
226
  "stroke",
@@ -239,3 +240,5 @@ export function includesVocabulary(vocabulary, value) {
239
240
  export const PLOT_LEGEND_POSITIONS = Object.freeze(["top-left", "top-right", "bottom-left", "bottom-right"]);
240
241
  export const LEGEND_POSITIONS = Object.freeze(["left", "right", "top", "bottom", ...PLOT_LEGEND_POSITIONS]);
241
242
  export const LEGEND_BLOCK_LAYOUTS = Object.freeze(["row", "column", "wrap"]);
243
+
244
+ export const AXIS_RESOURCE_CHANNELS = Object.freeze(["x", "y", "theta", "radius", "parallel"]);
@@ -40,7 +40,7 @@ export function isAggregate(value) {
40
40
  return isScalarAggregate(value) || isParameterizedAggregate(value);
41
41
  }
42
42
 
43
- export function validateAggregate(value) {
43
+ export function validateAggregate(value, { allowBootstrap = false } = {}) {
44
44
  if (typeof value === "string") {
45
45
  if (!SCALAR_OPERATIONS.has(value)) {
46
46
  throw new Error(`Unsupported aggregate "${value}".`);
@@ -97,7 +97,8 @@ export function validateAggregate(value) {
97
97
  op: value.op,
98
98
  ...normalizeConfidenceInterval(value, {
99
99
  defaultMethod: "normal",
100
- label: "Aggregate confidence interval"
100
+ label: "Aggregate confidence interval",
101
+ allowBootstrap
101
102
  })
102
103
  };
103
104
  }