@bicharts/chart-host 0.5.89 → 0.5.90

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/dist/react.mjs CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  createChartHost,
5
5
  geoFromCache,
6
6
  loadGeo
7
- } from "./chunk-NWMMNXH2.mjs";
7
+ } from "./chunk-GUCTCDEO.mjs";
8
8
  import "./chunk-A2GMXZP7.mjs";
9
9
  import "./chunk-GHODWOAL.mjs";
10
10
 
@@ -0,0 +1,75 @@
1
+ /**
2
+ * CIE2000 distance at which two fills stop being separable at a glance, side by side on a chart.
3
+ *
4
+ * Not a tuned parameter — a perceptual constant. 1.0 is the textbook just-noticeable difference
5
+ * under laboratory conditions with the two patches touching; 5 is the conventional "clearly
6
+ * different to an ordinary observer in ordinary conditions", which is what a reader scanning a
7
+ * choropleth is. Erring high is the safe direction: it makes the census UNDERstate crowding.
8
+ */
9
+ export declare const SAME_SHADE_DELTA_E = 5;
10
+ /**
11
+ * Fewer distinct fills than this and there is nothing to characterise.
12
+ *
13
+ * Four is the floor for the collinearity test below, not a judgement: the two extremes anchor the
14
+ * line, so a fourth fill is the second interior point and the first one that can disagree.
15
+ */
16
+ export declare const MIN_RAMP_FILLS = 4;
17
+ /**
18
+ * How far off a straight line a set of fills may sit and still be a RAMP.
19
+ *
20
+ * THIS IS WHAT SEPARATES A VALUE-ENCODED COLOUR CHANNEL FROM A CATEGORICAL ONE, and counting
21
+ * fills cannot do it. The obvious "at least six distinct colours" rule fails at exactly the
22
+ * wrong moment: a five-bin quantile scale — the recommended REMEDY for the washout this census
23
+ * exists to find — has five fills, so a count floor would have excluded the charts that did the
24
+ * right thing and biased the prevalence it was built to measure.
25
+ *
26
+ * A sequential ramp is, by construction, a path between two colours: every fill lies close to the
27
+ * segment joining its extremes, so `d(lo,x) + d(x,hi)` barely exceeds `d(lo,hi)`. A categorical
28
+ * scheme is chosen for mutual separation and its members sit well off that line. Measured in
29
+ * CIE2000 over the standard schemes, the two populations do not overlap:
30
+ *
31
+ * ```text
32
+ * interpolateBlues (5, 8 and 40 stops) mean slack -0.05 SEQUENTIAL
33
+ * interpolateReds (8) mean slack +0.06 SEQUENTIAL
34
+ * viridis (8) mean slack +0.02 SEQUENTIAL
35
+ * schemeTableau10 (5 and 8) mean slack +0.24 categorical
36
+ * RdBu diverging (7) mean slack +0.35 two arms, not one line
37
+ * ```
38
+ *
39
+ * 0.15 sits in the four-fold gap. A DIVERGING ramp landing outside is correct rather than a miss:
40
+ * it is genuinely two paths from a neutral middle, and measuring it as one would report the arm
41
+ * that happened to be longer. It is excluded and said so, not mismeasured.
42
+ */
43
+ export declare const RAMP_SLACK_MAX = 0.15;
44
+ export interface ColourSpreadCensus {
45
+ /** Marks carrying a resolvable, non-transparent fill. */
46
+ marks: number;
47
+ /** Distinct fills among them, before quantisation. */
48
+ distinctFills: number;
49
+ /** Share (0-100) of marks whose fill is within SAME_SHADE_DELTA_E of the most crowded fill. */
50
+ crowdShare: number;
51
+ /** Largest CIE2000 distance between any two sampled fills — the ramp's perceptual reach. */
52
+ spanDeltaE: number;
53
+ }
54
+ /**
55
+ * How much of this chart is one shade?
56
+ *
57
+ * Never throws. Telemetry must not be the reason a delivered render fails, and an environment
58
+ * that cannot report a fill reports nothing rather than a number that describes the environment.
59
+ *
60
+ * KNOWN LIMIT, stated rather than hidden: a DIVERGING ramp is measured the same way as any
61
+ * other, so a chart whose two arms are each well spread but which crowds around its neutral
62
+ * midpoint reports honestly, while one that crowds at BOTH ends reports the larger arm only. That
63
+ * understates rather than overstates, which is the direction an audit should err.
64
+ */
65
+ export declare function censusColourSpread(container: any, doc?: any): ColourSpreadCensus;
66
+ /**
67
+ * The always-on behaviour tag, or "" when the census had nothing to say.
68
+ *
69
+ * A BUCKETED MEASUREMENT, NOT A VERDICT. `ramp:d3:crowd60` says three in five marks are one
70
+ * shade; it does not say that is wrong. Twenty-point buckets are coarse enough to read as a
71
+ * distribution off a few hundred renders and fine enough to separate "a bit clustered" from the
72
+ * failure this exists for, and the whole family is one `LIKE 'ramp:d3:crowd%'` away — which is
73
+ * the denominator any rate needs. When the distribution is in, a cut can be argued from it.
74
+ */
75
+ export declare function colourSpreadFlag(c: ColourSpreadCensus): string;
@@ -2,6 +2,7 @@ import { type RenderOptions, type ViewStateProvider } from "./contract";
2
2
  import { type ResolveOptionsInput } from "./defaults";
3
3
  import { type MarkCensus } from "./blankRender";
4
4
  import { type HitBandCensus } from "./hitBands";
5
+ import { type ColourSpreadCensus } from "./colourSpread";
5
6
  import { type FitRenderedChartOptions, type FitRenderedChartResult } from "./fitDom";
6
7
  import { type LabelContrastOptions, type LabelContrastReport } from "./labelContrastDom";
7
8
  export type RenderFn = (container: HTMLElement, data: any, options: RenderOptions) => void;
@@ -92,6 +93,21 @@ export interface ChartHostConfig {
92
93
  * number only the browser knows, and this reads the COMPUTED width off the rendered element.
93
94
  */
94
95
  onHitBandCensus?: (census: HitBandCensus) => void;
96
+ /**
97
+ * HOW MUCH OF THIS CHART IS ONE SHADE (2026-09-09)? Runs after every render, counts only.
98
+ *
99
+ * The sibling question to the hit-band census, on the colour channel: a right-skewed measure
100
+ * on a linear ramp is accurate and unreadable, because the outliers eat the ramp and the
101
+ * dense bulk lands in one indistinguishable tint. It measures the OUTCOME, never the scale,
102
+ * so a well-spread scale of any kind reports well and a curved or hand-rolled ramp needs no
103
+ * special case; it declines outright on a categorical palette or a diverging ramp, which are
104
+ * a different question wearing the same numbers.
105
+ *
106
+ * The other half a server-side check cannot do. A code rule reads `scaleLinear()` and a
107
+ * domain built from `d3.max`; whether THAT washes out depends on the data, and the shape's
108
+ * own skew is measured on the raw column while the ramp encodes an aggregate.
109
+ */
110
+ onColourSpreadCensus?: (census: ColourSpreadCensus) => void;
95
111
  /** This chart declares time keyframes: frame one is allowed to be empty, so no blank verdict
96
112
  * is issued for it. */
97
113
  animated?: boolean;
@@ -21,6 +21,7 @@ export { applyLabelContrast, LABEL_CONTRAST_DONE_ATTR, LABEL_CONTRAST_CAP, type
21
21
  export { decideLabelColor, toRGBA, compositeOver, relativeLuminance, contrastRatio, isPillBackdropAlpha, pillBacksGlyph, backingHoldsGlyph, cellSuppressesNormalize, glyphSampleGrid, DARK_TEXT, LIGHT_TEXT, MIN_CONTRAST, WHITE_TEXT_BG_LUM, PILL_MIN_ALPHA, PILL_OPAQUE_ALPHA, PILL_MIN_COVERAGE, PAGE_MATCH_TOLERANCE, BACKING_MAJORITY, GLYPH_SAMPLE_N, type LabelDecision, } from "./labelContrast";
22
22
  export { censusMarks, isBlankRender, blankRenderFlag, type MarkCensus, type BlankVerdictInput } from "./blankRender";
23
23
  export { censusHitBands, hitBandFlag, MIN_HIT_BAND_PX, type HitBandCensus } from "./hitBands";
24
+ export { censusColourSpread, colourSpreadFlag, SAME_SHADE_DELTA_E, MIN_RAMP_FILLS, type ColourSpreadCensus } from "./colourSpread";
24
25
  export { SCROLL_SLACK_PX, MAX_FRAME_GROW_FACTOR, PHANTOM_FRACTION, FIT_CONTENT_SELECTOR, needsScroll, contentExtentOf, scrollFitFor, planFrameGrow, isPhantomBox, type MeasuredBox, type ContentExtent, type ScrollFit, type FrameGrowPlan, } from "./fit";
25
26
  export { fitRenderedChart, fitReadingFor, measureContainerBoxes, svgInkReach, ctmScaleOf, type FitReading, type InkReach, type FitRenderedChartOptions, type FitRenderedChartResult, } from "./fitDom";
26
27
  export { AXIS_PIN_MIN_LABELS, AXIS_PIN_BAND_PAD_PX, AXIS_PIN_MAX_BAND_FRACTION, AXIS_PIN_TRACK_REACH_PX, isHorizontalLabelRow, labelBand, planAxisPin, axisPinPlacement, type LabelRowBox, type AxisBand, type AxisPinCandidate, type AxisPinPlan, type AxisPinEdge, } from "./fit";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bicharts/chart-host",
3
- "version": "0.5.89",
3
+ "version": "0.5.90",
4
4
  "description": "Run a BIC-generated D3 chart in any web host: compiles the generated render() function, applies the shared option defaults, resolves mark clicks (through tooltip overlays), owns the selection affordance, and translates row indices between cross-filtered charts. The same contract the BIC Power BI visual implements, minus Power BI. React bindings at @bicharts/chart-host/react.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",