@pharos-ds/charts 0.0.0-stage → 1.0.10
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 +73 -0
- package/LICENSE +24 -0
- package/README.md +117 -2
- package/dist/Chart.d.ts +62 -0
- package/dist/Chart.d.ts.map +1 -0
- package/dist/Chart.js +401 -0
- package/dist/ChartAxis.d.ts +37 -0
- package/dist/ChartAxis.d.ts.map +1 -0
- package/dist/ChartAxis.js +233 -0
- package/dist/ChartContext.d.ts +4 -0
- package/dist/ChartContext.d.ts.map +1 -0
- package/dist/ChartContext.js +15 -0
- package/dist/ChartGrid.d.ts +26 -0
- package/dist/ChartGrid.d.ts.map +1 -0
- package/dist/ChartGrid.js +118 -0
- package/dist/ChartLegend.d.ts +14 -0
- package/dist/ChartLegend.d.ts.map +1 -0
- package/dist/ChartLegend.js +62 -0
- package/dist/ChartSwatch.d.ts +36 -0
- package/dist/ChartSwatch.d.ts.map +1 -0
- package/dist/ChartSwatch.js +85 -0
- package/dist/ChartTooltip.d.ts +46 -0
- package/dist/ChartTooltip.d.ts.map +1 -0
- package/dist/ChartTooltip.js +330 -0
- package/dist/charts.css +33 -0
- package/dist/dataTable.d.ts +56 -0
- package/dist/dataTable.d.ts.map +1 -0
- package/dist/dataTable.js +88 -0
- package/dist/formatters.d.ts +37 -0
- package/dist/formatters.d.ts.map +1 -0
- package/dist/formatters.js +156 -0
- package/dist/getChartColors.d.ts +67 -0
- package/dist/getChartColors.d.ts.map +1 -0
- package/dist/getChartColors.js +191 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/layout.d.ts +40 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +322 -0
- package/dist/legend.d.ts +17 -0
- package/dist/legend.d.ts.map +1 -0
- package/dist/legend.js +40 -0
- package/dist/markColor.d.ts +20 -0
- package/dist/markColor.d.ts.map +1 -0
- package/dist/markColor.js +56 -0
- package/dist/marks/area.d.ts +19 -0
- package/dist/marks/area.d.ts.map +1 -0
- package/dist/marks/area.js +121 -0
- package/dist/marks/band.d.ts +9 -0
- package/dist/marks/band.d.ts.map +1 -0
- package/dist/marks/band.js +64 -0
- package/dist/marks/bar.d.ts +21 -0
- package/dist/marks/bar.d.ts.map +1 -0
- package/dist/marks/bar.js +161 -0
- package/dist/marks/candlestick.d.ts +16 -0
- package/dist/marks/candlestick.d.ts.map +1 -0
- package/dist/marks/candlestick.js +110 -0
- package/dist/marks/dot.d.ts +19 -0
- package/dist/marks/dot.d.ts.map +1 -0
- package/dist/marks/dot.js +98 -0
- package/dist/marks/dotGL.d.ts +16 -0
- package/dist/marks/dotGL.d.ts.map +1 -0
- package/dist/marks/dotGL.js +231 -0
- package/dist/marks/dotGLInteractive.d.ts +35 -0
- package/dist/marks/dotGLInteractive.d.ts.map +1 -0
- package/dist/marks/dotGLInteractive.js +584 -0
- package/dist/marks/errorBar.d.ts +29 -0
- package/dist/marks/errorBar.d.ts.map +1 -0
- package/dist/marks/errorBar.js +95 -0
- package/dist/marks/heatmapGL.d.ts +27 -0
- package/dist/marks/heatmapGL.d.ts.map +1 -0
- package/dist/marks/heatmapGL.js +335 -0
- package/dist/marks/index.d.ts +13 -0
- package/dist/marks/index.d.ts.map +1 -0
- package/dist/marks/index.js +14 -0
- package/dist/marks/line.d.ts +18 -0
- package/dist/marks/line.d.ts.map +1 -0
- package/dist/marks/line.js +83 -0
- package/dist/marks/referenceLine.d.ts +37 -0
- package/dist/marks/referenceLine.d.ts.map +1 -0
- package/dist/marks/referenceLine.js +204 -0
- package/dist/marks/streamGL.d.ts +41 -0
- package/dist/marks/streamGL.d.ts.map +1 -0
- package/dist/marks/streamGL.js +317 -0
- package/dist/tooltip.d.ts +38 -0
- package/dist/tooltip.d.ts.map +1 -0
- package/dist/tooltip.js +57 -0
- package/dist/types.d.ts +193 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +70 -0
- package/dist/useChartColors.d.ts +16 -0
- package/dist/useChartColors.d.ts.map +1 -0
- package/dist/useChartColors.js +37 -0
- package/dist/utils.d.ts +15 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +31 -0
- package/dist/webgl.d.ts +77 -0
- package/dist/webgl.d.ts.map +1 -0
- package/dist/webgl.js +235 -0
- package/package.json +71 -3
- package/src/Chart.doc.mjs +174 -0
- package/src/Chart.test.tsx +273 -0
- package/src/Chart.tsx +535 -0
- package/src/ChartAxis.tsx +289 -0
- package/src/ChartContext.ts +18 -0
- package/src/ChartGrid.tsx +136 -0
- package/src/ChartLegend.tsx +77 -0
- package/src/ChartSwatch.doc.mjs +17 -0
- package/src/ChartSwatch.tsx +89 -0
- package/src/ChartTooltip.tsx +452 -0
- package/src/charts.css.d.ts +3 -0
- package/src/dataTable.test.ts +86 -0
- package/src/dataTable.ts +124 -0
- package/src/formatters.ts +167 -0
- package/src/getChartColors.test.ts +114 -0
- package/src/getChartColors.ts +282 -0
- package/src/index.ts +88 -0
- package/src/layout.ts +372 -0
- package/src/legend.ts +44 -0
- package/src/markColor.ts +70 -0
- package/src/marks/area.tsx +142 -0
- package/src/marks/band.tsx +86 -0
- package/src/marks/bar.tsx +210 -0
- package/src/marks/candlestick.tsx +128 -0
- package/src/marks/dot.tsx +119 -0
- package/src/marks/dotGL.tsx +274 -0
- package/src/marks/dotGLInteractive.tsx +660 -0
- package/src/marks/errorBar.tsx +119 -0
- package/src/marks/heatmapGL.tsx +392 -0
- package/src/marks/index.ts +17 -0
- package/src/marks/line.tsx +118 -0
- package/src/marks/referenceLine.tsx +252 -0
- package/src/marks/streamGL.tsx +381 -0
- package/src/tooltip.ts +73 -0
- package/src/types.ts +212 -0
- package/src/useChartColors.ts +41 -0
- package/src/utils.ts +38 -0
- package/src/webgl.ts +262 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file formatters.ts
|
|
5
|
+
* @output Built-in, locale-aware tick/value format utilities for common data types
|
|
6
|
+
* @position Utility; consumed by ChartAxis via tickFormat prop
|
|
7
|
+
*
|
|
8
|
+
* Formatters degrade gracefully: non-finite numbers (NaN/±Infinity) and
|
|
9
|
+
* unparseable values are passed through as-is rather than producing garbage
|
|
10
|
+
* like "InfinityB", and no input throws. Numeric/date formatting is delegated
|
|
11
|
+
* to cached `Intl` formatters so output is locale-aware and rounding is correct
|
|
12
|
+
* across the full magnitude range (K/M/B/T and beyond).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** Lazily create a value once and cache it — keeps `Intl` construction off the import path. */
|
|
16
|
+
function once(create) {
|
|
17
|
+
let cached = null;
|
|
18
|
+
return () => (cached ??= {
|
|
19
|
+
value: create()
|
|
20
|
+
}).value;
|
|
21
|
+
}
|
|
22
|
+
const getCompactFormat = once(() => new Intl.NumberFormat(undefined, {
|
|
23
|
+
notation: 'compact',
|
|
24
|
+
maximumFractionDigits: 1
|
|
25
|
+
}));
|
|
26
|
+
const getPercentFormat = once(() => new Intl.NumberFormat(undefined, {
|
|
27
|
+
style: 'percent',
|
|
28
|
+
maximumFractionDigits: 1
|
|
29
|
+
}));
|
|
30
|
+
const getShortDateFormat = once(() => new Intl.DateTimeFormat(undefined, {
|
|
31
|
+
month: 'short',
|
|
32
|
+
day: 'numeric'
|
|
33
|
+
}));
|
|
34
|
+
const getMonthYearFormat = once(() => new Intl.DateTimeFormat(undefined, {
|
|
35
|
+
month: 'short',
|
|
36
|
+
year: 'numeric'
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Compact number formatter (e.g. 1200 → "1.2K", 1500000 → "1.5M", 1e12 → "1T").
|
|
41
|
+
*
|
|
42
|
+
* Non-numeric values (e.g. category labels) and non-finite numbers pass through
|
|
43
|
+
* unchanged, so it is safe to use as a general-purpose tick formatter.
|
|
44
|
+
*/
|
|
45
|
+
export function compactNumber(value) {
|
|
46
|
+
const n = Number(value);
|
|
47
|
+
if (!Number.isFinite(n)) {
|
|
48
|
+
return String(value);
|
|
49
|
+
}
|
|
50
|
+
return getCompactFormat().format(n);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Currency formatter factory. Returns a tick format function.
|
|
55
|
+
*
|
|
56
|
+
* The symbol prefixes the magnitude and the sign leads the symbol, so negatives
|
|
57
|
+
* read as "-$1.5K" rather than "$-1.5K". Large values use compact notation.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```
|
|
61
|
+
* <ChartAxis tickFormat={currency()} /> // $1.5K
|
|
62
|
+
* <ChartAxis tickFormat={currency('€')} /> // €1.5K
|
|
63
|
+
* <ChartAxis tickFormat={currency('¥')} /> // ¥1.5K
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export function currency(symbol = '$') {
|
|
67
|
+
return value => {
|
|
68
|
+
const n = Number(value);
|
|
69
|
+
if (!Number.isFinite(n)) {
|
|
70
|
+
return String(value);
|
|
71
|
+
}
|
|
72
|
+
const sign = n < 0 ? '-' : '';
|
|
73
|
+
const abs = Math.abs(n);
|
|
74
|
+
const body = abs >= 1000 ? getCompactFormat().format(abs) : abs.toLocaleString();
|
|
75
|
+
return `${sign}${symbol}${body}`;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Percent formatter (e.g. 0.45 → "45%", 1.2 → "120%", 0.005 → "0.5%").
|
|
81
|
+
*
|
|
82
|
+
* The input is a ratio; it is multiplied by 100 and localized. Whole percents
|
|
83
|
+
* render without a decimal, and up to one fractional digit is shown otherwise.
|
|
84
|
+
*/
|
|
85
|
+
export function percent(value) {
|
|
86
|
+
const n = Number(value);
|
|
87
|
+
if (!Number.isFinite(n)) {
|
|
88
|
+
return String(value);
|
|
89
|
+
}
|
|
90
|
+
return getPercentFormat().format(n);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Coerce a value into a Date.
|
|
95
|
+
*
|
|
96
|
+
* Accepts a `Date`, epoch milliseconds (number or numeric string), or a date
|
|
97
|
+
* string (ISO 8601, RFC 2822, …). Returns an invalid Date for anything else so
|
|
98
|
+
* callers can pass the original value through unchanged.
|
|
99
|
+
*
|
|
100
|
+
* Date-only ISO strings (`YYYY-MM-DD`) are constructed at local midnight rather
|
|
101
|
+
* than the spec's UTC midnight, so the calendar day survives local formatting
|
|
102
|
+
* in negative-offset timezones (`"2024-01-05"` stays "Jan 5", not "Jan 4").
|
|
103
|
+
*/
|
|
104
|
+
function toDate(value) {
|
|
105
|
+
if (value instanceof Date) {
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
108
|
+
if (typeof value === 'number') {
|
|
109
|
+
return new Date(value);
|
|
110
|
+
}
|
|
111
|
+
if (typeof value === 'string') {
|
|
112
|
+
const s = value.trim();
|
|
113
|
+
if (s === '') {
|
|
114
|
+
return new Date(NaN);
|
|
115
|
+
}
|
|
116
|
+
const dateOnly = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s);
|
|
117
|
+
if (dateOnly) {
|
|
118
|
+
const year = Number(dateOnly[1]);
|
|
119
|
+
const month = Number(dateOnly[2]);
|
|
120
|
+
const day = Number(dateOnly[3]);
|
|
121
|
+
const local = new Date(year, month - 1, day);
|
|
122
|
+
// Reject out-of-range parts (e.g. "2024-02-31") that the Date constructor
|
|
123
|
+
// would otherwise silently roll into a different day; treat them as
|
|
124
|
+
// invalid so the raw value passes through unchanged.
|
|
125
|
+
if (local.getFullYear() !== year || local.getMonth() !== month - 1 || local.getDate() !== day) {
|
|
126
|
+
return new Date(NaN);
|
|
127
|
+
}
|
|
128
|
+
return local;
|
|
129
|
+
}
|
|
130
|
+
const asNumber = Number(s);
|
|
131
|
+
return new Date(Number.isFinite(asNumber) ? asNumber : s);
|
|
132
|
+
}
|
|
133
|
+
return new Date(NaN);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Date formatter — short date (e.g. "Jan 5", "Mar 12").
|
|
138
|
+
*/
|
|
139
|
+
export function shortDate(value) {
|
|
140
|
+
const d = toDate(value);
|
|
141
|
+
if (Number.isNaN(d.getTime())) {
|
|
142
|
+
return String(value);
|
|
143
|
+
}
|
|
144
|
+
return getShortDateFormat().format(d);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Date formatter — month/year (e.g. "Jan 2024", "Mar 2025").
|
|
149
|
+
*/
|
|
150
|
+
export function monthYear(value) {
|
|
151
|
+
const d = toDate(value);
|
|
152
|
+
if (Number.isNaN(d.getTime())) {
|
|
153
|
+
return String(value);
|
|
154
|
+
}
|
|
155
|
+
return getMonthYearFormat().format(d);
|
|
156
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file getChartColors.ts
|
|
3
|
+
* @output Pure function to resolve chart colors from a theme — no React required
|
|
4
|
+
* @position Foundation utility; consumed by useChartColors and non-React contexts
|
|
5
|
+
*
|
|
6
|
+
* Takes a token resolver function (or a defined theme + mode) and returns
|
|
7
|
+
* the full chart colors API. The useChartColors hook wraps this.
|
|
8
|
+
*
|
|
9
|
+
* Palettes degrade gracefully for any count: the categorical scale wraps
|
|
10
|
+
* around its slots, and the sequential/diverging scales pick hand-tuned token
|
|
11
|
+
* stops when enough exist and interpolate across the ramp when more are asked
|
|
12
|
+
* for. The theme entry point resolves through the canonical token resolver, so
|
|
13
|
+
* data tokens fall back to their defaults even when a theme doesn't override
|
|
14
|
+
* them.
|
|
15
|
+
*/
|
|
16
|
+
import { type DefinedTheme } from '@pharos-ds/core/theme';
|
|
17
|
+
/** Hue names available in the sequential ramps */
|
|
18
|
+
export type SequentialHue = 'blue' | 'shamrock' | 'orange' | 'pink' | 'purple' | 'red' | 'teal' | 'yellow' | 'gray';
|
|
19
|
+
/** The chart colors API returned by both getChartColors and useChartColors */
|
|
20
|
+
export interface ChartColorsAPI {
|
|
21
|
+
categorical(n: number): string[];
|
|
22
|
+
sequential: Record<SequentialHue, (n: number) => string[]>;
|
|
23
|
+
diverging: {
|
|
24
|
+
positiveNegative(n: number): string[];
|
|
25
|
+
coldHot(n: number): string[];
|
|
26
|
+
custom(neg: SequentialHue, pos: SequentialHue, n: number, midpoint?: string): string[];
|
|
27
|
+
};
|
|
28
|
+
semantic: {
|
|
29
|
+
positive: string;
|
|
30
|
+
negative: string;
|
|
31
|
+
warning: string;
|
|
32
|
+
neutral: string;
|
|
33
|
+
};
|
|
34
|
+
/** Structural colors for chart chrome — axes, grid, ticks, labels */
|
|
35
|
+
structural: {
|
|
36
|
+
axis: string;
|
|
37
|
+
grid: string;
|
|
38
|
+
tick: string;
|
|
39
|
+
label: string;
|
|
40
|
+
};
|
|
41
|
+
alpha(hex: string, opacity: number): string;
|
|
42
|
+
}
|
|
43
|
+
/** A function that resolves a token name to its current value */
|
|
44
|
+
export type TokenResolver = (name: string) => string;
|
|
45
|
+
/**
|
|
46
|
+
* Build chart colors from a token resolver function.
|
|
47
|
+
* This is the lowest-level API — pass any function that maps token names to values.
|
|
48
|
+
*/
|
|
49
|
+
export declare function getChartColorsFromResolver(resolve: TokenResolver): ChartColorsAPI;
|
|
50
|
+
/**
|
|
51
|
+
* Build chart colors from a defined theme and color mode.
|
|
52
|
+
*
|
|
53
|
+
* Use this in non-React contexts — WebGL setup, SSR, tests, Node scripts.
|
|
54
|
+
*
|
|
55
|
+
* Resolves through the canonical theme token resolver, so data tokens fall back
|
|
56
|
+
* to their defaults when the theme doesn't override them, and both
|
|
57
|
+
* `light-dark()` strings and `[light, dark]` tuples are handled for `mode`.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```
|
|
61
|
+
* import { neutralTheme } from '@pharos-ds/theme-neutral';
|
|
62
|
+
* const colors = getChartColors(neutralTheme, 'light');
|
|
63
|
+
* colors.categorical(5)
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare function getChartColors(theme: DefinedTheme, mode?: 'light' | 'dark'): ChartColorsAPI;
|
|
67
|
+
//# sourceMappingURL=getChartColors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getChartColors.d.ts","sourceRoot":"","sources":["../src/getChartColors.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAqB,KAAK,YAAY,EAAC,MAAM,uBAAuB,CAAC;AAY5E,kDAAkD;AAClD,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,UAAU,GACV,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,KAAK,GACL,MAAM,GACN,QAAQ,GACR,MAAM,CAAC;AAEX,8EAA8E;AAC9E,MAAM,WAAW,cAAc;IAC7B,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC,CAAC;IAC3D,SAAS,EAAE;QACT,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QACtC,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAC7B,MAAM,CACJ,GAAG,EAAE,aAAa,EAClB,GAAG,EAAE,aAAa,EAClB,CAAC,EAAE,MAAM,EACT,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,EAAE,CAAC;KACb,CAAC;IACF,QAAQ,EAAE;QACR,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,qEAAqE;IACrE,UAAU,EAAE;QACV,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CAC7C;AAED,iEAAiE;AACjE,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;AAoGrD;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,aAAa,GACrB,cAAc,CA8EhB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,YAAY,EACnB,IAAI,GAAE,OAAO,GAAG,MAAgB,GAC/B,cAAc,CAGhB"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file getChartColors.ts
|
|
5
|
+
* @output Pure function to resolve chart colors from a theme — no React required
|
|
6
|
+
* @position Foundation utility; consumed by useChartColors and non-React contexts
|
|
7
|
+
*
|
|
8
|
+
* Takes a token resolver function (or a defined theme + mode) and returns
|
|
9
|
+
* the full chart colors API. The useChartColors hook wraps this.
|
|
10
|
+
*
|
|
11
|
+
* Palettes degrade gracefully for any count: the categorical scale wraps
|
|
12
|
+
* around its slots, and the sequential/diverging scales pick hand-tuned token
|
|
13
|
+
* stops when enough exist and interpolate across the ramp when more are asked
|
|
14
|
+
* for. The theme entry point resolves through the canonical token resolver, so
|
|
15
|
+
* data tokens fall back to their defaults even when a theme doesn't override
|
|
16
|
+
* them.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { resolveThemeTokens } from '@pharos-ds/core/theme';
|
|
20
|
+
import { parseHex, parseColor, formatHex, formatColor } from '@pharos-ds/core/utils';
|
|
21
|
+
|
|
22
|
+
// =============================================================================
|
|
23
|
+
// Types
|
|
24
|
+
// =============================================================================
|
|
25
|
+
|
|
26
|
+
/** Hue names available in the sequential ramps */
|
|
27
|
+
|
|
28
|
+
/** The chart colors API returned by both getChartColors and useChartColors */
|
|
29
|
+
|
|
30
|
+
/** A function that resolves a token name to its current value */
|
|
31
|
+
|
|
32
|
+
// =============================================================================
|
|
33
|
+
// Internals
|
|
34
|
+
// =============================================================================
|
|
35
|
+
|
|
36
|
+
const CATEGORICAL_TOKENS = ['--color-data-categorical-blue', '--color-data-categorical-purple', '--color-data-categorical-orange', '--color-data-categorical-yellow', '--color-data-categorical-gray', '--color-data-categorical-green', '--color-data-categorical-pink', '--color-data-categorical-cyan', '--color-data-categorical-red', '--color-data-categorical-teal', '--color-data-categorical-brown', '--color-data-categorical-indigo'];
|
|
37
|
+
const SEQUENTIAL_HUES = ['blue', 'shamrock', 'orange', 'pink', 'purple', 'red', 'teal', 'yellow', 'gray'];
|
|
38
|
+
|
|
39
|
+
/** Interpolate between two hex colors in sRGB. Non-hex input falls back to the nearer endpoint. */
|
|
40
|
+
function lerpHex(a, b, t) {
|
|
41
|
+
const ca = parseHex(a);
|
|
42
|
+
const cb = parseHex(b);
|
|
43
|
+
if (ca === null || cb === null) {
|
|
44
|
+
return t < 0.5 ? a : b;
|
|
45
|
+
}
|
|
46
|
+
const mix = (x, y) => x + (y - x) * t;
|
|
47
|
+
return formatHex(mix(ca.r, cb.r), mix(ca.g, cb.g), mix(ca.b, cb.b));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Sample `n` colors from an ordered ramp of stops.
|
|
52
|
+
* - `n <= stops.length` → picks the nearest exact stops, preserving the
|
|
53
|
+
* hand-tuned design-token values.
|
|
54
|
+
* - `n > stops.length` → interpolates across the ramp so any count is supported
|
|
55
|
+
* instead of silently capping at the number of stops.
|
|
56
|
+
* - `n === 1` → the middle stop, a single representative tone.
|
|
57
|
+
*/
|
|
58
|
+
function sampleRamp(stops, n) {
|
|
59
|
+
const len = stops.length;
|
|
60
|
+
// A non-finite count (e.g. Infinity) would make the Array.from calls below
|
|
61
|
+
// attempt a ~2**53 element allocation and hang, so treat it as empty.
|
|
62
|
+
if (!Number.isFinite(n) || n <= 0 || len === 0) {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
if (n === 1) {
|
|
66
|
+
return [stops[Math.floor((len - 1) / 2)]];
|
|
67
|
+
}
|
|
68
|
+
if (len === 1) {
|
|
69
|
+
return Array.from({
|
|
70
|
+
length: n
|
|
71
|
+
}, () => stops[0]);
|
|
72
|
+
}
|
|
73
|
+
if (n <= len) {
|
|
74
|
+
return Array.from({
|
|
75
|
+
length: n
|
|
76
|
+
}, (_, i) => stops[Math.round(i * (len - 1) / (n - 1))]);
|
|
77
|
+
}
|
|
78
|
+
return Array.from({
|
|
79
|
+
length: n
|
|
80
|
+
}, (_, i) => {
|
|
81
|
+
const t = i * (len - 1) / (n - 1);
|
|
82
|
+
const lo = Math.floor(t);
|
|
83
|
+
const frac = t - lo;
|
|
84
|
+
return frac === 0 ? stops[lo] : lerpHex(stops[lo], stops[lo + 1], frac);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Apply an opacity to a concrete CSS color (hex — with or without the leading
|
|
90
|
+
* `#` — `rgb()`/`rgba()`, named). The `opacity` argument always wins over any
|
|
91
|
+
* alpha already in the color. Unparseable input (e.g. `var()`) is returned
|
|
92
|
+
* unchanged.
|
|
93
|
+
*/
|
|
94
|
+
function hexAlpha(hex, opacity) {
|
|
95
|
+
// parseColor requires the `#` for hex; fall back to parseHex so bare hex
|
|
96
|
+
// strings (`0064E0`) keep working as they did before #3739.
|
|
97
|
+
const rgba = parseColor(hex) ?? parseHex(hex);
|
|
98
|
+
if (rgba === null) {
|
|
99
|
+
return hex;
|
|
100
|
+
}
|
|
101
|
+
const a = Number.isFinite(opacity) ? Math.max(0, Math.min(1, opacity)) : 1;
|
|
102
|
+
return formatColor({
|
|
103
|
+
...rgba,
|
|
104
|
+
a
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// =============================================================================
|
|
109
|
+
// Public API
|
|
110
|
+
// =============================================================================
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Build chart colors from a token resolver function.
|
|
114
|
+
* This is the lowest-level API — pass any function that maps token names to values.
|
|
115
|
+
*/
|
|
116
|
+
export function getChartColorsFromResolver(resolve) {
|
|
117
|
+
const categorical = CATEGORICAL_TOKENS.map(t => resolve(t));
|
|
118
|
+
const ramp = hue => [5, 4, 3, 2, 1].map(step => resolve(`--color-data-${hue}-${step}`));
|
|
119
|
+
const gray1 = resolve('--color-data-gray-1');
|
|
120
|
+
function buildDiverging(negHue, posHue, n, midpoint = gray1) {
|
|
121
|
+
if (n <= 0) {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
if (n === 1) {
|
|
125
|
+
return [midpoint];
|
|
126
|
+
}
|
|
127
|
+
const half = Math.floor(n / 2);
|
|
128
|
+
const hasCenter = n % 2 === 1;
|
|
129
|
+
const negSide = sampleRamp(ramp(negHue), half);
|
|
130
|
+
const posSide = sampleRamp(ramp(posHue), half).reverse();
|
|
131
|
+
if (hasCenter) {
|
|
132
|
+
return [...negSide, midpoint, ...posSide];
|
|
133
|
+
}
|
|
134
|
+
return [...negSide, ...posSide];
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
categorical(n) {
|
|
138
|
+
// A non-finite count (e.g. Infinity) would make the wrap-around Array.from
|
|
139
|
+
// below attempt a ~2**53 element allocation and hang, so treat it as empty.
|
|
140
|
+
if (!Number.isFinite(n) || n <= 0 || categorical.length === 0) {
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
// Wrap around the palette so any series count gets a deterministic color.
|
|
144
|
+
return Array.from({
|
|
145
|
+
length: n
|
|
146
|
+
}, (_, i) => categorical[i % categorical.length]);
|
|
147
|
+
},
|
|
148
|
+
sequential: Object.fromEntries(SEQUENTIAL_HUES.map(hue => [hue, n => sampleRamp(ramp(hue), n)])),
|
|
149
|
+
diverging: {
|
|
150
|
+
positiveNegative: n => buildDiverging('shamrock', 'red', n),
|
|
151
|
+
coldHot: n => buildDiverging('blue', 'red', n),
|
|
152
|
+
custom: (neg, pos, n, mid) => buildDiverging(neg, pos, n, mid)
|
|
153
|
+
},
|
|
154
|
+
semantic: {
|
|
155
|
+
positive: resolve('--color-data-categorical-green'),
|
|
156
|
+
negative: resolve('--color-data-categorical-red'),
|
|
157
|
+
warning: resolve('--color-data-categorical-orange'),
|
|
158
|
+
neutral: resolve('--color-data-neutral')
|
|
159
|
+
},
|
|
160
|
+
structural: {
|
|
161
|
+
axis: resolve('--color-border-emphasized'),
|
|
162
|
+
grid: resolve('--color-border'),
|
|
163
|
+
tick: resolve('--color-border-emphasized'),
|
|
164
|
+
label: resolve('--color-text-secondary')
|
|
165
|
+
},
|
|
166
|
+
alpha: hexAlpha
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Build chart colors from a defined theme and color mode.
|
|
172
|
+
*
|
|
173
|
+
* Use this in non-React contexts — WebGL setup, SSR, tests, Node scripts.
|
|
174
|
+
*
|
|
175
|
+
* Resolves through the canonical theme token resolver, so data tokens fall back
|
|
176
|
+
* to their defaults when the theme doesn't override them, and both
|
|
177
|
+
* `light-dark()` strings and `[light, dark]` tuples are handled for `mode`.
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```
|
|
181
|
+
* import { neutralTheme } from '@pharos-ds/theme-neutral';
|
|
182
|
+
* const colors = getChartColors(neutralTheme, 'light');
|
|
183
|
+
* colors.categorical(5)
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
export function getChartColors(theme, mode = 'light') {
|
|
187
|
+
const tokens = resolveThemeTokens(theme, {
|
|
188
|
+
mode
|
|
189
|
+
});
|
|
190
|
+
return getChartColorsFromResolver(name => tokens[name] ?? '');
|
|
191
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export { Chart, type ChartProps } from './Chart';
|
|
2
|
+
export { ChartAxis, type ChartAxisProps } from './ChartAxis';
|
|
3
|
+
export { ChartGrid, type ChartGridProps } from './ChartGrid';
|
|
4
|
+
export { ChartLegend, type ChartLegendProps, type LegendPosition, type LegendAlignment, } from './ChartLegend';
|
|
5
|
+
export { type LegendItem } from './legend';
|
|
6
|
+
export { ChartSwatch, swatchVariantForType, type ChartSwatchProps, type ChartSwatchVariant, } from './ChartSwatch';
|
|
7
|
+
export { ChartTooltip, type ChartTooltipProps, type ChartTooltipPlacement, } from './ChartTooltip';
|
|
8
|
+
export { type TooltipSeriesValue } from './tooltip';
|
|
9
|
+
export { bar, line, dot, area, band, candlestick, errorBar, referenceLine, dotGL, dotGLInteractive, heatmapGL, streamGL, } from './marks';
|
|
10
|
+
export type { BarOptions, LineOptions, DotOptions, AreaOptions, BandOptions, CandlestickOptions, ErrorBarOptions, ReferenceLineOptions, DotGLOptions, DotGLInteractiveOptions, HeatmapGLOptions, StreamGLOptions, StreamGLHandle, } from './marks';
|
|
11
|
+
export { useChart } from './ChartContext';
|
|
12
|
+
export type { SeriesDef, ChartContext, ChartPointerEvent, ResolvedPoint, ResolvedPositions, SeriesContext, ChartMargin, ChartScale, YBaseline, ColorAccessor, } from './types';
|
|
13
|
+
export { useChartColors } from './useChartColors';
|
|
14
|
+
export { getChartColors, getChartColorsFromResolver, type ChartColorsAPI, type SequentialHue, type TokenResolver, } from './getChartColors';
|
|
15
|
+
export { compactNumber, currency, percent, shortDate, monthYear, } from './formatters';
|
|
16
|
+
export { isBandScale, xPixel } from './utils';
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,KAAK,EAAE,KAAK,UAAU,EAAC,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAC,SAAS,EAAE,KAAK,cAAc,EAAC,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAC,SAAS,EAAE,KAAK,cAAc,EAAC,MAAM,aAAa,CAAC;AAC3D,OAAO,EACL,WAAW,EACX,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,eAAe,GACrB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAC,KAAK,UAAU,EAAC,MAAM,UAAU,CAAC;AACzC,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACxB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,YAAY,EACZ,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,GAC3B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAC,KAAK,kBAAkB,EAAC,MAAM,WAAW,CAAC;AAClD,OAAO,EACL,GAAG,EACH,IAAI,EACJ,GAAG,EACH,IAAI,EACJ,IAAI,EACJ,WAAW,EACX,QAAQ,EACR,aAAa,EACb,KAAK,EACL,gBAAgB,EAChB,SAAS,EACT,QAAQ,GACT,MAAM,SAAS,CAAC;AACjB,YAAY,EACV,UAAU,EACV,WAAW,EACX,UAAU,EACV,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,YAAY,EACZ,uBAAuB,EACvB,gBAAgB,EAChB,eAAe,EACf,cAAc,GACf,MAAM,SAAS,CAAC;AACjB,OAAO,EAAC,QAAQ,EAAC,MAAM,gBAAgB,CAAC;AACxC,YAAY,EACV,SAAS,EACT,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,UAAU,EACV,SAAS,EACT,aAAa,GACd,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAC;AAChD,OAAO,EACL,cAAc,EACd,0BAA0B,EAC1B,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,GACnB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,aAAa,EACb,QAAQ,EACR,OAAO,EACP,SAAS,EACT,SAAS,GACV,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAC,WAAW,EAAE,MAAM,EAAC,MAAM,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
export { Chart } from "./Chart.js";
|
|
4
|
+
export { ChartAxis } from "./ChartAxis.js";
|
|
5
|
+
export { ChartGrid } from "./ChartGrid.js";
|
|
6
|
+
export { ChartLegend } from "./ChartLegend.js";
|
|
7
|
+
export { ChartSwatch, swatchVariantForType } from "./ChartSwatch.js";
|
|
8
|
+
export { ChartTooltip } from "./ChartTooltip.js";
|
|
9
|
+
export { bar, line, dot, area, band, candlestick, errorBar, referenceLine, dotGL, dotGLInteractive, heatmapGL, streamGL } from "./marks/index.js";
|
|
10
|
+
export { useChart } from "./ChartContext.js";
|
|
11
|
+
// Color palette — theme-aware data-visualization colors
|
|
12
|
+
export { useChartColors } from "./useChartColors.js";
|
|
13
|
+
export { getChartColors, getChartColorsFromResolver } from "./getChartColors.js";
|
|
14
|
+
|
|
15
|
+
// Tick / value formatters
|
|
16
|
+
export { compactNumber, currency, percent, shortDate, monthYear } from "./formatters.js";
|
|
17
|
+
|
|
18
|
+
// Scale utilities
|
|
19
|
+
export { isBandScale, xPixel } from "./utils.js";
|
package/dist/layout.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file layout.ts
|
|
3
|
+
* @output Computes scales, stacking, grouping, then delegates to each series.resolve()
|
|
4
|
+
* @position Called by Chart root — the single layout pass
|
|
5
|
+
*
|
|
6
|
+
* The layout engine does NOT know about specific mark types.
|
|
7
|
+
* It handles cross-series coordination (scales, stacking, grouping)
|
|
8
|
+
* then calls each series' own resolve() method.
|
|
9
|
+
*/
|
|
10
|
+
import { scaleLinear, scaleBand } from 'd3-scale';
|
|
11
|
+
import type { SeriesDef, ChartScale, ResolvedPoint, YBaseline } from './types';
|
|
12
|
+
export interface LayoutInput {
|
|
13
|
+
data: Record<string, unknown>[];
|
|
14
|
+
xKey: string;
|
|
15
|
+
series: SeriesDef[];
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
/** How the y-domain is derived when `yDomain` is not provided. Default 'auto'. */
|
|
19
|
+
yBaseline?: YBaseline;
|
|
20
|
+
/** Explicit y-domain [min, max]. Authoritative — no baseline/headroom/nice(). */
|
|
21
|
+
yDomain?: [number, number];
|
|
22
|
+
/**
|
|
23
|
+
* Explicit x-domain [min, max] for numeric/linear x. Authoritative and honored
|
|
24
|
+
* even when `data` is empty (stable streaming window). Ignored for band scales.
|
|
25
|
+
*/
|
|
26
|
+
xDomain?: [number, number];
|
|
27
|
+
}
|
|
28
|
+
export interface LayoutResult {
|
|
29
|
+
xScale: ChartScale;
|
|
30
|
+
yScale: ReturnType<typeof scaleLinear<number, number>>;
|
|
31
|
+
/**
|
|
32
|
+
* Categorical y band scale, built when a series declares `layout.yBandKey`
|
|
33
|
+
* (e.g. a heatmap's rows). When present the y-axis is categorical and the
|
|
34
|
+
* linear `yScale` is unused.
|
|
35
|
+
*/
|
|
36
|
+
yBandScale?: ReturnType<typeof scaleBand<string>>;
|
|
37
|
+
resolved: Map<string, ResolvedPoint[]>;
|
|
38
|
+
}
|
|
39
|
+
export declare function computeLayout({ data, xKey, series, width, height, yBaseline, yDomain, xDomain, }: LayoutInput): LayoutResult;
|
|
40
|
+
//# sourceMappingURL=layout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../src/layout.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH,OAAO,EAAC,WAAW,EAAE,SAAS,EAAC,MAAM,UAAU,CAAC;AAEhD,OAAO,KAAK,EACV,SAAS,EACT,UAAU,EACV,aAAa,EAEb,SAAS,EACV,MAAM,SAAS,CAAC;AASjB,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,iFAAiF;IACjF,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACvD;;;;OAIG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IAClD,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC;CACxC;AAqCD,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,KAAK,EACL,MAAM,EACN,SAAS,EACT,OAAO,EACP,OAAO,GACR,EAAE,WAAW,GAAG,YAAY,CA6Q5B"}
|