@bicharts/chart-host 0.5.93 → 0.5.95
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.
|
@@ -1868,6 +1868,10 @@ function deltaE2000(c1, c2) {
|
|
|
1868
1868
|
const tL = dLp / SL, tC = dCp / SC, tH = dHp / SH;
|
|
1869
1869
|
return Math.sqrt(tL * tL + tC * tC + tH * tH + RT * tC * tH);
|
|
1870
1870
|
}
|
|
1871
|
+
function deltaEHex(a4, b2) {
|
|
1872
|
+
const la = hexToLab(a4), lb = hexToLab(b2);
|
|
1873
|
+
return la && lb ? deltaE2000(la, lb) : NaN;
|
|
1874
|
+
}
|
|
1871
1875
|
|
|
1872
1876
|
// src/colourSpread.ts
|
|
1873
1877
|
var SAME_SHADE_DELTA_E = 5;
|
|
@@ -3223,6 +3227,7 @@ export {
|
|
|
3223
3227
|
MIN_HIT_BAND_PX,
|
|
3224
3228
|
censusHitBands,
|
|
3225
3229
|
hitBandFlag,
|
|
3230
|
+
deltaEHex,
|
|
3226
3231
|
SAME_SHADE_DELTA_E,
|
|
3227
3232
|
MIN_RAMP_FILLS,
|
|
3228
3233
|
censusColourSpread,
|
package/dist/index.mjs
CHANGED
|
@@ -81,6 +81,7 @@ import {
|
|
|
81
81
|
createMarkResolver,
|
|
82
82
|
ctmScaleOf,
|
|
83
83
|
decideLabelColor,
|
|
84
|
+
deltaEHex,
|
|
84
85
|
ensureCrossfilterHitTargets,
|
|
85
86
|
explainRenderFailure,
|
|
86
87
|
fitReadingFor,
|
|
@@ -116,7 +117,7 @@ import {
|
|
|
116
117
|
toRGBA,
|
|
117
118
|
u,
|
|
118
119
|
unpinScrolledAxis
|
|
119
|
-
} from "./chunk-
|
|
120
|
+
} from "./chunk-IIBDCQJM.mjs";
|
|
120
121
|
import "./chunk-A2GMXZP7.mjs";
|
|
121
122
|
import "./chunk-GHODWOAL.mjs";
|
|
122
123
|
|
|
@@ -1940,9 +1941,16 @@ Mt.config = function(t2) {
|
|
|
1940
1941
|
|
|
1941
1942
|
// src/palette.ts
|
|
1942
1943
|
var MIN_DELTA_E = 10;
|
|
1943
|
-
|
|
1944
|
+
var LEGIBLE_LIGHTNESS = [15, 85];
|
|
1945
|
+
function pickDistinctColorFromSeed(seedHex, existing, minDistance = MIN_DELTA_E, options = {}) {
|
|
1944
1946
|
const existingCs = [...existing].map((c2) => Mt(c2));
|
|
1945
1947
|
const seed = Mt(seedHex);
|
|
1948
|
+
const band = options.lightnessBand === void 0 ? LEGIBLE_LIGHTNESS : options.lightnessBand;
|
|
1949
|
+
const legible = (c2) => {
|
|
1950
|
+
if (!band) return true;
|
|
1951
|
+
const l2 = c2.hsl()[2];
|
|
1952
|
+
return l2 > band[0] && l2 < band[1];
|
|
1953
|
+
};
|
|
1946
1954
|
const minDeltaE = (c2) => existingCs.length === 0 ? Number.POSITIVE_INFINITY : Math.min(...existingCs.map((c22) => c2.deltaE(c22, "CIE2000")));
|
|
1947
1955
|
if (minDeltaE(seed) >= minDistance) {
|
|
1948
1956
|
const hex = seed.hex();
|
|
@@ -1951,6 +1959,7 @@ function pickDistinctColorFromSeed(seedHex, existing, minDistance = MIN_DELTA_E)
|
|
|
1951
1959
|
let bestCandidate = seed;
|
|
1952
1960
|
let bestMinDist = minDeltaE(seed);
|
|
1953
1961
|
const tryVariant = (c2) => {
|
|
1962
|
+
if (!legible(c2)) return null;
|
|
1954
1963
|
const d2 = minDeltaE(c2);
|
|
1955
1964
|
if (d2 >= minDistance) {
|
|
1956
1965
|
const hex = c2.hex();
|
|
@@ -1976,10 +1985,10 @@ function pickDistinctColorFromSeed(seedHex, existing, minDistance = MIN_DELTA_E)
|
|
|
1976
1985
|
}
|
|
1977
1986
|
return { hex: seed.hex(), registerHex: bestCandidate.hex() };
|
|
1978
1987
|
}
|
|
1979
|
-
function buildPaletteFromSeed(seedHex, count, minDistance = MIN_DELTA_E) {
|
|
1980
|
-
return buildPalette(() => seedHex, count, minDistance);
|
|
1988
|
+
function buildPaletteFromSeed(seedHex, count, minDistance = MIN_DELTA_E, options = {}) {
|
|
1989
|
+
return buildPalette(() => seedHex, count, minDistance, options);
|
|
1981
1990
|
}
|
|
1982
|
-
function buildPalette(seedFor, count, minDistance = MIN_DELTA_E) {
|
|
1991
|
+
function buildPalette(seedFor, count, minDistance = MIN_DELTA_E, options = {}) {
|
|
1983
1992
|
const out = [];
|
|
1984
1993
|
if (!(count > 0)) return out;
|
|
1985
1994
|
const issued = /* @__PURE__ */ new Set();
|
|
@@ -1998,7 +2007,7 @@ function buildPalette(seedFor, count, minDistance = MIN_DELTA_E) {
|
|
|
1998
2007
|
if (!seedHex) seedHex = lastSeed;
|
|
1999
2008
|
if (!seedHex) break;
|
|
2000
2009
|
lastSeed = seedHex;
|
|
2001
|
-
const { hex, registerHex } = pickDistinctColorFromSeed(seedHex, issued, minDistance);
|
|
2010
|
+
const { hex, registerHex } = pickDistinctColorFromSeed(seedHex, issued, minDistance, options);
|
|
2002
2011
|
let chosen = hex;
|
|
2003
2012
|
let register = registerHex;
|
|
2004
2013
|
if (distance(chosen) < minDistance) {
|
|
@@ -2027,6 +2036,33 @@ function buildPalette(seedFor, count, minDistance = MIN_DELTA_E) {
|
|
|
2027
2036
|
}
|
|
2028
2037
|
return out;
|
|
2029
2038
|
}
|
|
2039
|
+
function orderMostDistinct(lead, pool) {
|
|
2040
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2041
|
+
const valid = [];
|
|
2042
|
+
const take = (c2) => {
|
|
2043
|
+
const s2 = typeof c2 === "string" ? c2.trim() : "";
|
|
2044
|
+
if (!s2 || !Number.isFinite(deltaEHex(s2, s2))) return;
|
|
2045
|
+
const key = s2.toLowerCase();
|
|
2046
|
+
if (seen.has(key)) return;
|
|
2047
|
+
seen.add(key);
|
|
2048
|
+
valid.push(s2);
|
|
2049
|
+
};
|
|
2050
|
+
take(lead);
|
|
2051
|
+
for (const c2 of pool ?? []) take(c2);
|
|
2052
|
+
if (!valid.length) return [];
|
|
2053
|
+
const out = [valid[0]];
|
|
2054
|
+
const rest = valid.slice(1);
|
|
2055
|
+
const nearest = rest.map((c2) => deltaEHex(c2, out[0]));
|
|
2056
|
+
while (rest.length) {
|
|
2057
|
+
let best = 0;
|
|
2058
|
+
for (let i2 = 1; i2 < rest.length; i2++) if (nearest[i2] > nearest[best]) best = i2;
|
|
2059
|
+
const picked = rest.splice(best, 1)[0];
|
|
2060
|
+
nearest.splice(best, 1);
|
|
2061
|
+
out.push(picked);
|
|
2062
|
+
for (let i2 = 0; i2 < rest.length; i2++) nearest[i2] = Math.min(nearest[i2], deltaEHex(rest[i2], picked));
|
|
2063
|
+
}
|
|
2064
|
+
return out;
|
|
2065
|
+
}
|
|
2030
2066
|
function normalise(seed) {
|
|
2031
2067
|
return typeof seed === "string" ? seed.trim() : "";
|
|
2032
2068
|
}
|
|
@@ -2092,6 +2128,7 @@ export {
|
|
|
2092
2128
|
LABEL_CONTRAST_CAP,
|
|
2093
2129
|
LABEL_CONTRAST_DONE_ATTR,
|
|
2094
2130
|
LEGEND_MARK_CLASS,
|
|
2131
|
+
LEGIBLE_LIGHTNESS,
|
|
2095
2132
|
LIFT_SELECTED_CLASS,
|
|
2096
2133
|
LIGHT_TEXT,
|
|
2097
2134
|
MARK_CLASS,
|
|
@@ -2149,6 +2186,7 @@ export {
|
|
|
2149
2186
|
createMarkResolver,
|
|
2150
2187
|
ctmScaleOf,
|
|
2151
2188
|
decideLabelColor,
|
|
2189
|
+
deltaEHex,
|
|
2152
2190
|
ensureCrossfilterHitTargets,
|
|
2153
2191
|
explainRenderFailure,
|
|
2154
2192
|
filterFitsChooser,
|
|
@@ -2179,6 +2217,7 @@ export {
|
|
|
2179
2217
|
noopViewStateProvider,
|
|
2180
2218
|
normaliseAggregation,
|
|
2181
2219
|
normalizeFilterTerm,
|
|
2220
|
+
orderMostDistinct,
|
|
2182
2221
|
orderRefusalsForDisplay,
|
|
2183
2222
|
periodTickSuppressesFeedback,
|
|
2184
2223
|
pickDistinctColorFromSeed,
|
package/dist/react.mjs
CHANGED
package/dist/types/index.d.ts
CHANGED
|
@@ -27,4 +27,5 @@ export { SCROLL_SLACK_PX, MAX_FRAME_GROW_FACTOR, PHANTOM_FRACTION, FIT_CONTENT_S
|
|
|
27
27
|
export { fitRenderedChart, fitReadingFor, measureContainerBoxes, svgInkReach, ctmScaleOf, type FitReading, type InkReach, type FitRenderedChartOptions, type FitRenderedChartResult, } from "./fitDom";
|
|
28
28
|
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";
|
|
29
29
|
export { pinScrolledAxis, unpinScrolledAxis, type AxisPinReport } from "./fitDom";
|
|
30
|
-
export { MIN_DELTA_E, pickDistinctColorFromSeed, buildPalette, buildPaletteFromSeed, type PaletteResult, type SeedForSlot, } from "./palette";
|
|
30
|
+
export { MIN_DELTA_E, pickDistinctColorFromSeed, buildPalette, buildPaletteFromSeed, orderMostDistinct, LEGIBLE_LIGHTNESS, type PaletteResult, type SeedForSlot, type PaletteOptions, } from "./palette";
|
|
31
|
+
export { deltaEHex } from "./deltaE";
|
package/dist/types/palette.d.ts
CHANGED
|
@@ -4,6 +4,30 @@
|
|
|
4
4
|
* swatches; JND (just-noticeable-difference) at this scale is ≈5.
|
|
5
5
|
*/
|
|
6
6
|
export declare const MIN_DELTA_E = 10;
|
|
7
|
+
/**
|
|
8
|
+
* THE LIGHTNESS A COLOUR THE WALK INVENTS MUST FALL INSIDE (HSL lightness, 0-100, exclusive).
|
|
9
|
+
*
|
|
10
|
+
* The walk's lightness step moves the seed up to 45 points, so from a light seed it could land on
|
|
11
|
+
* a near-white - and a categorical mark that is near-white on a white page, or near-black on a dark
|
|
12
|
+
* one, is not a colour a reader can find. Measured before this band existed: a green seed's
|
|
13
|
+
* twenty-slot palette put `#ebf4e5` (lightness 93) in slot 7, and a pool of Office theme colours led
|
|
14
|
+
* by a red put `#d5e5f4` (90) in slot 17.
|
|
15
|
+
*
|
|
16
|
+
* INVENTED COLOURS ONLY. A seed the host supplied comes back as supplied, however light or dark -
|
|
17
|
+
* that is a report author's decision - and the spare-colour sweep keeps its own, narrower band.
|
|
18
|
+
*/
|
|
19
|
+
export declare const LEGIBLE_LIGHTNESS: readonly [number, number];
|
|
20
|
+
export interface PaletteOptions {
|
|
21
|
+
/**
|
|
22
|
+
* The lightness band invented colours must sit in. Defaults to `LEGIBLE_LIGHTNESS`.
|
|
23
|
+
*
|
|
24
|
+
* `null` walks as the package did before the band existed, colour for colour. It exists for a
|
|
25
|
+
* host that STORED palettes the older walk produced and has to recognise one again by
|
|
26
|
+
* re-deriving it: change the walk under such a host and every stored palette silently stops
|
|
27
|
+
* matching. Nothing should draw with it.
|
|
28
|
+
*/
|
|
29
|
+
lightnessBand?: readonly [number, number] | null;
|
|
30
|
+
}
|
|
7
31
|
/**
|
|
8
32
|
* Pick a hex color for a palette slot.
|
|
9
33
|
*
|
|
@@ -30,7 +54,7 @@ export interface PaletteResult {
|
|
|
30
54
|
/** Hex color to register in the palette-issued set (may differ from `hex` on the give-up fallback). */
|
|
31
55
|
readonly registerHex: string;
|
|
32
56
|
}
|
|
33
|
-
export declare function pickDistinctColorFromSeed(seedHex: string, existing: ReadonlySet<string>, minDistance?: number): PaletteResult;
|
|
57
|
+
export declare function pickDistinctColorFromSeed(seedHex: string, existing: ReadonlySet<string>, minDistance?: number, options?: PaletteOptions): PaletteResult;
|
|
34
58
|
/**
|
|
35
59
|
* A whole categorical palette from ONE seed colour.
|
|
36
60
|
*
|
|
@@ -49,7 +73,7 @@ export declare function pickDistinctColorFromSeed(seedHex: string, existing: Rea
|
|
|
49
73
|
* Deterministic: same seed and count in, same array out, which is what lets a host PERSIST the
|
|
50
74
|
* result and re-derive it later without the colours shifting under a saved chart.
|
|
51
75
|
*/
|
|
52
|
-
export declare function buildPaletteFromSeed(seedHex: string, count: number, minDistance?: number): string[];
|
|
76
|
+
export declare function buildPaletteFromSeed(seedHex: string, count: number, minDistance?: number, options?: PaletteOptions): string[];
|
|
53
77
|
/**
|
|
54
78
|
* WHERE EACH SLOT'S BASE COLOUR COMES FROM. Called with the slot index, 0-based.
|
|
55
79
|
*
|
|
@@ -75,4 +99,32 @@ export type SeedForSlot = (slot: number) => string | null | undefined;
|
|
|
75
99
|
* A host with no such API - an Excel workbook offers exactly one resolvable accent - passes a
|
|
76
100
|
* constant provider and the walk does the spreading. That is a fallback, not the design.
|
|
77
101
|
*/
|
|
78
|
-
export declare function buildPalette(seedFor: SeedForSlot, count: number, minDistance?: number): string[];
|
|
102
|
+
export declare function buildPalette(seedFor: SeedForSlot, count: number, minDistance?: number, options?: PaletteOptions): string[];
|
|
103
|
+
/**
|
|
104
|
+
* A POOL OF ACCEPTABLE COLOURS, ORDERED SO THE MOST DISTINCT COME FIRST.
|
|
105
|
+
*
|
|
106
|
+
* For a host that knows WHICH colours belong to the surface but not in what ORDER to use them.
|
|
107
|
+
* Excel is the case that needed it: a workbook's theme offers a grid of accents and their tints,
|
|
108
|
+
* none of which is ranked, and the order a host invents decides what a two- or five-series chart
|
|
109
|
+
* looks like. The order Office itself lists its accents in puts two blues four slots apart, so a
|
|
110
|
+
* five-series chart following that list gives two of its series nearly the same colour - which
|
|
111
|
+
* reads as one series.
|
|
112
|
+
*
|
|
113
|
+
* NEVER USE THIS ON AN AUTHORED ORDER. A report theme's slot order is somebody's decision, and
|
|
114
|
+
* `buildPalette` exists to honour it verbatim; re-sorting it here would override the author with
|
|
115
|
+
* arithmetic. This is only for a pool a host assembled itself.
|
|
116
|
+
*
|
|
117
|
+
* The method is greedy max-min (the farthest-point walk used for categorical colour sets): keep
|
|
118
|
+
* the lead, then repeatedly take the pool colour whose NEAREST already-chosen colour is farthest
|
|
119
|
+
* away, by CIEDE2000. Two properties follow and the tests pin both. The distance each pick adds is
|
|
120
|
+
* never larger than the one before it, so the early slots, which every chart uses, are the best
|
|
121
|
+
* separated. And every input colour is returned unchanged, so the palette is still the surface's
|
|
122
|
+
* own colours, only re-ordered.
|
|
123
|
+
*
|
|
124
|
+
* Deterministic: ties go to the colour listed earlier in the pool. Duplicates (case-insensitive)
|
|
125
|
+
* and anything that is not `#rgb` / `#rrggbb` are dropped, because a colour that cannot be
|
|
126
|
+
* measured cannot be placed. A blank or unmeasurable lead means "start from the pool's first
|
|
127
|
+
* colour". Feed the result to `buildPalette`, which still guarantees the minimum separation when
|
|
128
|
+
* two pool colours are too close to share a legend.
|
|
129
|
+
*/
|
|
130
|
+
export declare function orderMostDistinct(lead: string | null | undefined, pool: readonly string[]): string[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bicharts/chart-host",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.95",
|
|
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",
|