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.
- package/CHANGELOG.md +56 -0
- package/README.md +6 -2
- package/knowledge/action-cards.json +1 -1
- package/knowledge/intent-taxonomy.json +1 -1
- package/knowledge/mcp-resources.json +1 -1
- package/package.json +1 -1
- package/src/actions/canvas/fitting.js +6 -2
- package/src/actions/charts/heatmap.js +4 -3
- package/src/actions/data/derived.js +11 -1
- package/src/actions/data/edit.js +2 -0
- package/src/actions/data/index.js +4 -0
- package/src/actions/data/regression.js +24 -16
- package/src/actions/data/regressionStatistics.js +73 -0
- package/src/actions/data/shared.js +5 -2
- package/src/actions/distributions/revision.js +24 -18
- package/src/actions/encodings/color/continuous.js +7 -1
- package/src/actions/encodings/remove.js +11 -6
- package/src/actions/encodings/shared.js +26 -24
- package/src/actions/facets/actions.js +18 -7
- package/src/actions/facets/guides.js +2 -1
- package/src/actions/guides/axes/axes.js +30 -11
- package/src/actions/guides/axes/axis.js +58 -8
- package/src/actions/guides/axes/components.js +13 -13
- package/src/actions/guides/axes/edit.js +36 -24
- package/src/actions/guides/axes/labels.js +37 -27
- package/src/actions/guides/axes/lines.js +22 -20
- package/src/actions/guides/axes/remove.js +10 -6
- package/src/actions/guides/axes/tickGroups.js +8 -7
- package/src/actions/guides/axes/ticks.js +31 -22
- package/src/actions/guides/axes/titles.js +53 -60
- package/src/actions/guides/facadeAxes.js +33 -24
- package/src/actions/guides/facadeLegend.js +3 -3
- package/src/actions/guides/grids/grid.js +1 -1
- package/src/actions/guides/grids/resolve.js +48 -51
- package/src/actions/guides/legends/blocks.js +43 -19
- package/src/actions/guides/legends/categorical/actions.js +32 -9
- package/src/actions/guides/legends/categorical/components.js +3 -10
- package/src/actions/guides/legends/categorical/index.js +2 -0
- package/src/actions/guides/legends/categorical/layout.js +9 -13
- package/src/actions/guides/legends/categorical/options.js +8 -2
- package/src/actions/guides/legends/categorical/recipes.js +7 -3
- package/src/actions/guides/legends/categorical/resolve.js +9 -2
- package/src/actions/guides/legends/categorical/symbols.js +22 -8
- package/src/actions/guides/legends/colorSize.js +110 -0
- package/src/actions/guides/legends/combine.js +37 -0
- package/src/actions/guides/legends/continuous/colorSymbols.js +1 -1
- package/src/actions/guides/legends/continuous/common.js +1 -1
- package/src/actions/guides/legends/continuous/gradient.js +27 -13
- package/src/actions/guides/legends/continuous/opacity.js +16 -11
- package/src/actions/guides/legends/creation.js +2 -0
- package/src/actions/guides/legends/edit.js +1 -1
- package/src/actions/guides/legends/focused.js +6 -5
- package/src/actions/guides/legends/lane.js +1 -1
- package/src/actions/guides/legends/lifecycle.js +2 -1
- package/src/actions/guides/legends/remove.js +6 -3
- package/src/actions/guides/legends/size.js +16 -6
- package/src/actions/guides/legends/strokeWidth.js +2 -0
- package/src/actions/guides/legends/target.js +2 -79
- package/src/actions/guides/legends/transition.js +24 -5
- package/src/actions/guides/tickValues.js +8 -5
- package/src/actions/marks/area/materialize.js +1 -190
- package/src/actions/marks/bar/edit.js +23 -12
- package/src/actions/marks/bar/materialize.js +6 -1
- package/src/actions/marks/point/materialize.js +5 -1
- package/src/actions/marks/remove.js +10 -10
- package/src/actions/primitives/createGraphics.js +3 -2
- package/src/actions/primitives/editGraphics.js +15 -11
- package/src/actions/primitives/graphicProperties.js +1 -6
- package/src/actions/primitives/semanticAction.js +27 -1
- package/src/actions/primitives/semanticValidation/guide.js +11 -1
- package/src/actions/primitives/semanticValidation/layer.js +4 -0
- package/src/actions/regression/edit.js +15 -3
- package/src/actions/scales/channels.js +2 -2
- package/src/actions/scales/consumers/common.js +7 -1
- package/src/actions/scales/create.js +3 -3
- package/src/actions/scales/definitions.js +1 -1
- package/src/actions/scales/edit.js +1 -1
- package/src/actions/scales/editPolicy.js +3 -0
- package/src/actions/scales/patch.js +1 -1
- package/src/actions/scales/quantitativeColor.js +8 -3
- package/src/actions/theme/reconcile.js +50 -23
- package/src/core/compositionState.js +2 -2
- package/src/core/graphicProperties.js +1 -0
- package/src/core/programState.js +4 -0
- package/src/core/resourceReferences.js +2 -2
- package/src/core/validation.js +9 -0
- package/src/core/vocabulary.js +3 -0
- package/src/grammar/aggregate.js +3 -2
- package/src/grammar/areaSeries.js +1 -415
- package/src/grammar/axisTextPlacement.js +1 -0
- package/src/grammar/colorSizeLegend.js +39 -0
- package/src/grammar/computed.js +23 -1
- package/src/grammar/datasetSchema.js +10 -2
- package/src/grammar/displayLabels.js +8 -0
- package/src/grammar/facets/guides.js +54 -31
- package/src/grammar/facets/legendIntent.js +65 -0
- package/src/grammar/guideDash.js +1 -0
- package/src/grammar/interval.js +22 -6
- package/src/grammar/legendBlocks.js +1 -0
- package/src/grammar/legendBorder.js +9 -0
- package/src/grammar/legendClip.js +19 -0
- package/src/grammar/legendCombinations.js +45 -0
- package/src/grammar/legendGradient.js +9 -0
- package/src/grammar/legendGroups.js +1 -1
- package/src/grammar/legendOpacity.js +6 -0
- package/src/grammar/pathSeries.js +2 -2
- package/src/grammar/pointShapes.js +16 -2
- package/src/grammar/regression/derive.js +9 -1
- package/src/grammar/regression/models.js +60 -0
- package/src/grammar/regression/parameters.js +9 -1
- package/src/grammar/regression/statistics.js +64 -0
- package/src/grammar/scales/color.js +1 -1
- package/src/grammar/scales/colorConsumers.js +6 -3
- package/src/grammar/scales/definition.js +1 -1
- package/src/grammar/scales/types.js +1 -1
- package/src/grammar/schemas/concreteGraphic.js +3 -0
- package/src/grammar/schemas/graphic.js +3 -0
- package/src/grammar/schemas/graphicBounds.js +31 -7
- package/src/grammar/schemas/semanticPath.js +17 -2
- package/src/grammar/statistics/confidenceInterval.js +1 -101
- package/src/grammar/summary.js +1 -320
- package/src/grammar/timeUnit.js +6 -4
- package/src/grammar/transformTopology.js +1 -0
- package/src/grammar/transforms.js +11 -1
- package/src/inspectionDescriptors.js +1 -1
- package/src/layout/guideCollisions.js +9 -1
- package/src/layout/legendGroup.js +1 -1
- package/src/materialization/compositionSnapshot.js +2 -7
- package/src/materialization/coordinateBounds.js +17 -4
- package/src/materialization/facetGuides/edit.js +30 -0
- package/src/materialization/facetGuides/layout.js +1 -1
- package/src/materialization/facetGuides/owner.js +125 -0
- package/src/materialization/facetGuides/ownerLayout.js +71 -0
- package/src/materialization/facetGuides/placement.js +52 -6
- package/src/materialization/facetGuides/preparation.js +7 -0
- package/src/materialization/facets.js +1 -1
- package/src/materialization/graphicHierarchy.js +5 -1
- package/src/materialization/graphicTranslation.js +73 -0
- package/src/materialization/guides/layout.js +1 -1
- package/src/materialization/guides/resources.js +1 -1
- package/src/materialization/legendBorder.js +28 -0
- package/src/materialization/legendClip.js +76 -0
- package/src/materialization/legendGroups.js +1 -1
- package/src/materialization/legends.js +1 -1
- package/src/materialization/marks/grouping.js +1 -1
- package/src/materialization/scaleGuideDependencies.js +13 -14
- package/src/materialization/scales/map.js +3 -0
- package/src/materialization/scales/resolve.js +2 -0
- package/src/materialization/typography.js +10 -2
- package/src/persistence/validation.js +174 -1
- package/src/persistence.js +1 -1
- package/src/renderers/canvas/index.js +20 -10
- package/src/renderers/canvas/isolated.js +166 -0
- package/src/renderers/canvas/limits.js +3 -0
- package/src/renderers/canvas/rect.js +1 -1
- package/src/renderers/pdf.js +2 -2
- package/src/renderers/png.js +2 -2
- package/src/renderers/svg.js +10 -3
- package/src/selectors/axes.js +1 -0
- package/src/selectors/graphicCapacity.js +38 -0
- package/src/version.js +1 -1
- package/types/basic.d.ts +2 -0
- package/types/index.d.ts +7 -0
- 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
|
|
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 (/
|
|
204
|
-
if (/
|
|
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 (/
|
|
207
|
-
if (/
|
|
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\.(
|
|
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:${
|
|
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
|
|
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.${
|
|
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.${
|
|
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.${
|
|
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.${
|
|
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,
|
|
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
|
|
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};
|
package/src/core/programState.js
CHANGED
|
@@ -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 (
|
|
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 (
|
|
211
|
+
if (["scale", "colorScale", "sizeScale"].includes(key)) {
|
|
212
212
|
append(references, reference(
|
|
213
213
|
"scale", value, family, ownerId, relativePath
|
|
214
214
|
));
|
package/src/core/validation.js
CHANGED
|
@@ -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)) {
|
package/src/core/vocabulary.js
CHANGED
|
@@ -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"]);
|
package/src/grammar/aggregate.js
CHANGED
|
@@ -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
|
}
|