@bicharts/chart-host 0.5.93 → 0.5.94

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-BWOY6GDX.mjs";
120
+ } from "./chunk-IIBDCQJM.mjs";
120
121
  import "./chunk-A2GMXZP7.mjs";
121
122
  import "./chunk-GHODWOAL.mjs";
122
123
 
@@ -2027,6 +2028,33 @@ function buildPalette(seedFor, count, minDistance = MIN_DELTA_E) {
2027
2028
  }
2028
2029
  return out;
2029
2030
  }
2031
+ function orderMostDistinct(lead, pool) {
2032
+ const seen = /* @__PURE__ */ new Set();
2033
+ const valid = [];
2034
+ const take = (c2) => {
2035
+ const s2 = typeof c2 === "string" ? c2.trim() : "";
2036
+ if (!s2 || !Number.isFinite(deltaEHex(s2, s2))) return;
2037
+ const key = s2.toLowerCase();
2038
+ if (seen.has(key)) return;
2039
+ seen.add(key);
2040
+ valid.push(s2);
2041
+ };
2042
+ take(lead);
2043
+ for (const c2 of pool ?? []) take(c2);
2044
+ if (!valid.length) return [];
2045
+ const out = [valid[0]];
2046
+ const rest = valid.slice(1);
2047
+ const nearest = rest.map((c2) => deltaEHex(c2, out[0]));
2048
+ while (rest.length) {
2049
+ let best = 0;
2050
+ for (let i2 = 1; i2 < rest.length; i2++) if (nearest[i2] > nearest[best]) best = i2;
2051
+ const picked = rest.splice(best, 1)[0];
2052
+ nearest.splice(best, 1);
2053
+ out.push(picked);
2054
+ for (let i2 = 0; i2 < rest.length; i2++) nearest[i2] = Math.min(nearest[i2], deltaEHex(rest[i2], picked));
2055
+ }
2056
+ return out;
2057
+ }
2030
2058
  function normalise(seed) {
2031
2059
  return typeof seed === "string" ? seed.trim() : "";
2032
2060
  }
@@ -2149,6 +2177,7 @@ export {
2149
2177
  createMarkResolver,
2150
2178
  ctmScaleOf,
2151
2179
  decideLabelColor,
2180
+ deltaEHex,
2152
2181
  ensureCrossfilterHitTargets,
2153
2182
  explainRenderFailure,
2154
2183
  filterFitsChooser,
@@ -2179,6 +2208,7 @@ export {
2179
2208
  noopViewStateProvider,
2180
2209
  normaliseAggregation,
2181
2210
  normalizeFilterTerm,
2211
+ orderMostDistinct,
2182
2212
  orderRefusalsForDisplay,
2183
2213
  periodTickSuppressesFeedback,
2184
2214
  pickDistinctColorFromSeed,
package/dist/react.mjs CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  createChartHost,
5
5
  geoFromCache,
6
6
  loadGeo
7
- } from "./chunk-BWOY6GDX.mjs";
7
+ } from "./chunk-IIBDCQJM.mjs";
8
8
  import "./chunk-A2GMXZP7.mjs";
9
9
  import "./chunk-GHODWOAL.mjs";
10
10
 
@@ -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, type PaletteResult, type SeedForSlot, } from "./palette";
31
+ export { deltaEHex } from "./deltaE";
@@ -76,3 +76,31 @@ export type SeedForSlot = (slot: number) => string | null | undefined;
76
76
  * constant provider and the walk does the spreading. That is a fallback, not the design.
77
77
  */
78
78
  export declare function buildPalette(seedFor: SeedForSlot, count: number, minDistance?: number): string[];
79
+ /**
80
+ * A POOL OF ACCEPTABLE COLOURS, ORDERED SO THE MOST DISTINCT COME FIRST.
81
+ *
82
+ * For a host that knows WHICH colours belong to the surface but not in what ORDER to use them.
83
+ * Excel is the case that needed it: a workbook's theme offers a grid of accents and their tints,
84
+ * none of which is ranked, and the order a host invents decides what a two- or five-series chart
85
+ * looks like. The order Office itself lists its accents in puts two blues four slots apart, so a
86
+ * five-series chart following that list gives two of its series nearly the same colour - which
87
+ * reads as one series.
88
+ *
89
+ * NEVER USE THIS ON AN AUTHORED ORDER. A report theme's slot order is somebody's decision, and
90
+ * `buildPalette` exists to honour it verbatim; re-sorting it here would override the author with
91
+ * arithmetic. This is only for a pool a host assembled itself.
92
+ *
93
+ * The method is greedy max-min (the farthest-point walk used for categorical colour sets): keep
94
+ * the lead, then repeatedly take the pool colour whose NEAREST already-chosen colour is farthest
95
+ * away, by CIEDE2000. Two properties follow and the tests pin both. The distance each pick adds is
96
+ * never larger than the one before it, so the early slots, which every chart uses, are the best
97
+ * separated. And every input colour is returned unchanged, so the palette is still the surface's
98
+ * own colours, only re-ordered.
99
+ *
100
+ * Deterministic: ties go to the colour listed earlier in the pool. Duplicates (case-insensitive)
101
+ * and anything that is not `#rgb` / `#rrggbb` are dropped, because a colour that cannot be
102
+ * measured cannot be placed. A blank or unmeasurable lead means "start from the pool's first
103
+ * colour". Feed the result to `buildPalette`, which still guarantees the minimum separation when
104
+ * two pool colours are too close to share a legend.
105
+ */
106
+ 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.93",
3
+ "version": "0.5.94",
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",