@matthieumordrel/chart-studio 0.2.1 → 0.2.3
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/README.md +24 -0
- package/dist/core/chart-capabilities.d.mts +48 -0
- package/dist/core/chart-capabilities.mjs +55 -0
- package/dist/core/{colors.d.ts → colors.d.mts} +5 -3
- package/dist/core/colors.mjs +55 -0
- package/dist/core/config-utils.mjs +79 -0
- package/dist/core/date-utils.mjs +49 -0
- package/dist/core/define-chart-schema.d.mts +106 -0
- package/dist/core/define-chart-schema.mjs +47 -0
- package/dist/core/formatting.mjs +349 -0
- package/dist/core/infer-columns.d.mts +9 -0
- package/dist/core/infer-columns.mjs +481 -0
- package/dist/core/metric-utils.d.mts +13 -0
- package/dist/core/metric-utils.mjs +121 -0
- package/dist/core/pipeline-data-points.mjs +212 -0
- package/dist/core/pipeline-helpers.mjs +85 -0
- package/dist/core/{pipeline.d.ts → pipeline.d.mts} +21 -24
- package/dist/core/pipeline.mjs +153 -0
- package/dist/core/types.d.mts +957 -0
- package/dist/core/use-chart-options.d.mts +64 -0
- package/dist/core/use-chart-options.mjs +7 -0
- package/dist/core/use-chart-resolvers.mjs +34 -0
- package/dist/core/{use-chart.d.ts → use-chart.d.mts} +12 -9
- package/dist/core/use-chart.mjs +299 -0
- package/dist/index.d.mts +10 -0
- package/dist/index.mjs +8 -0
- package/dist/ui/chart-axis-ticks.mjs +65 -0
- package/dist/ui/{chart-canvas.d.ts → chart-canvas.d.mts} +13 -6
- package/dist/ui/chart-canvas.mjs +461 -0
- package/dist/ui/chart-context.d.mts +92 -0
- package/dist/ui/chart-context.mjs +112 -0
- package/dist/ui/{chart-date-range-badge.d.ts → chart-date-range-badge.d.mts} +10 -4
- package/dist/ui/chart-date-range-badge.mjs +49 -0
- package/dist/ui/chart-date-range-panel.d.mts +18 -0
- package/dist/ui/chart-date-range-panel.mjs +208 -0
- package/dist/ui/{chart-date-range.d.ts → chart-date-range.d.mts} +10 -4
- package/dist/ui/chart-date-range.mjs +67 -0
- package/dist/ui/chart-debug.d.mts +17 -0
- package/dist/ui/chart-debug.mjs +169 -0
- package/dist/ui/chart-dropdown.mjs +92 -0
- package/dist/ui/{chart-filters-panel.d.ts → chart-filters-panel.d.mts} +12 -5
- package/dist/ui/chart-filters-panel.mjs +132 -0
- package/dist/ui/{chart-filters.d.ts → chart-filters.d.mts} +10 -4
- package/dist/ui/chart-filters.mjs +48 -0
- package/dist/ui/chart-group-by-selector.d.mts +14 -0
- package/dist/ui/chart-group-by-selector.mjs +29 -0
- package/dist/ui/{chart-metric-panel.d.ts → chart-metric-panel.d.mts} +12 -5
- package/dist/ui/chart-metric-panel.mjs +172 -0
- package/dist/ui/chart-metric-selector.d.mts +16 -0
- package/dist/ui/chart-metric-selector.mjs +50 -0
- package/dist/ui/chart-select.mjs +62 -0
- package/dist/ui/{chart-source-switcher.d.ts → chart-source-switcher.d.mts} +10 -4
- package/dist/ui/chart-source-switcher.mjs +54 -0
- package/dist/ui/chart-time-bucket-selector.d.mts +15 -0
- package/dist/ui/chart-time-bucket-selector.mjs +34 -0
- package/dist/ui/chart-toolbar-overflow.d.mts +28 -0
- package/dist/ui/chart-toolbar-overflow.mjs +209 -0
- package/dist/ui/chart-toolbar.d.mts +29 -0
- package/dist/ui/chart-toolbar.mjs +56 -0
- package/dist/ui/chart-type-selector.d.mts +14 -0
- package/dist/ui/chart-type-selector.mjs +33 -0
- package/dist/ui/chart-x-axis-selector.d.mts +14 -0
- package/dist/ui/chart-x-axis-selector.mjs +25 -0
- package/dist/ui/index.d.mts +19 -0
- package/dist/ui/index.mjs +18 -0
- package/dist/ui/toolbar-types.d.mts +7 -0
- package/dist/ui/toolbar-types.mjs +83 -0
- package/package.json +11 -10
- package/dist/core/chart-capabilities.d.ts +0 -60
- package/dist/core/chart-capabilities.d.ts.map +0 -1
- package/dist/core/chart-capabilities.js +0 -54
- package/dist/core/colors.d.ts.map +0 -1
- package/dist/core/colors.js +0 -54
- package/dist/core/config-utils.d.ts +0 -43
- package/dist/core/config-utils.d.ts.map +0 -1
- package/dist/core/config-utils.js +0 -80
- package/dist/core/date-utils.d.ts +0 -29
- package/dist/core/date-utils.d.ts.map +0 -1
- package/dist/core/date-utils.js +0 -58
- package/dist/core/define-chart-schema.d.ts +0 -105
- package/dist/core/define-chart-schema.d.ts.map +0 -1
- package/dist/core/define-chart-schema.js +0 -44
- package/dist/core/formatting.d.ts +0 -47
- package/dist/core/formatting.d.ts.map +0 -1
- package/dist/core/formatting.js +0 -396
- package/dist/core/index.d.ts +0 -17
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -12
- package/dist/core/infer-columns.d.ts +0 -6
- package/dist/core/infer-columns.d.ts.map +0 -1
- package/dist/core/infer-columns.js +0 -512
- package/dist/core/metric-utils.d.ts +0 -43
- package/dist/core/metric-utils.d.ts.map +0 -1
- package/dist/core/metric-utils.js +0 -141
- package/dist/core/pipeline-data-points.d.ts +0 -23
- package/dist/core/pipeline-data-points.d.ts.map +0 -1
- package/dist/core/pipeline-data-points.js +0 -235
- package/dist/core/pipeline-helpers.d.ts +0 -38
- package/dist/core/pipeline-helpers.d.ts.map +0 -1
- package/dist/core/pipeline-helpers.js +0 -97
- package/dist/core/pipeline.d.ts.map +0 -1
- package/dist/core/pipeline.js +0 -156
- package/dist/core/types.d.ts +0 -1109
- package/dist/core/types.d.ts.map +0 -1
- package/dist/core/types.js +0 -14
- package/dist/core/use-chart-options.d.ts +0 -66
- package/dist/core/use-chart-options.d.ts.map +0 -1
- package/dist/core/use-chart-options.js +0 -4
- package/dist/core/use-chart-resolvers.d.ts +0 -14
- package/dist/core/use-chart-resolvers.d.ts.map +0 -1
- package/dist/core/use-chart-resolvers.js +0 -41
- package/dist/core/use-chart.d.ts.map +0 -1
- package/dist/core/use-chart.js +0 -265
- package/dist/index.d.ts +0 -36
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -35
- package/dist/ui/chart-axis-ticks.d.ts +0 -35
- package/dist/ui/chart-axis-ticks.d.ts.map +0 -1
- package/dist/ui/chart-axis-ticks.js +0 -79
- package/dist/ui/chart-canvas.d.ts.map +0 -1
- package/dist/ui/chart-canvas.js +0 -337
- package/dist/ui/chart-context.d.ts +0 -89
- package/dist/ui/chart-context.d.ts.map +0 -1
- package/dist/ui/chart-context.js +0 -128
- package/dist/ui/chart-date-range-badge.d.ts.map +0 -1
- package/dist/ui/chart-date-range-badge.js +0 -30
- package/dist/ui/chart-date-range-panel.d.ts +0 -25
- package/dist/ui/chart-date-range-panel.d.ts.map +0 -1
- package/dist/ui/chart-date-range-panel.js +0 -125
- package/dist/ui/chart-date-range.d.ts.map +0 -1
- package/dist/ui/chart-date-range.js +0 -37
- package/dist/ui/chart-debug.d.ts +0 -10
- package/dist/ui/chart-debug.d.ts.map +0 -1
- package/dist/ui/chart-debug.js +0 -126
- package/dist/ui/chart-dropdown.d.ts +0 -35
- package/dist/ui/chart-dropdown.d.ts.map +0 -1
- package/dist/ui/chart-dropdown.js +0 -76
- package/dist/ui/chart-filters-panel.d.ts.map +0 -1
- package/dist/ui/chart-filters-panel.js +0 -46
- package/dist/ui/chart-filters.d.ts.map +0 -1
- package/dist/ui/chart-filters.js +0 -26
- package/dist/ui/chart-group-by-selector.d.ts +0 -8
- package/dist/ui/chart-group-by-selector.d.ts.map +0 -1
- package/dist/ui/chart-group-by-selector.js +0 -19
- package/dist/ui/chart-metric-panel.d.ts.map +0 -1
- package/dist/ui/chart-metric-panel.js +0 -118
- package/dist/ui/chart-metric-selector.d.ts +0 -10
- package/dist/ui/chart-metric-selector.d.ts.map +0 -1
- package/dist/ui/chart-metric-selector.js +0 -27
- package/dist/ui/chart-select.d.ts +0 -25
- package/dist/ui/chart-select.d.ts.map +0 -1
- package/dist/ui/chart-select.js +0 -35
- package/dist/ui/chart-source-switcher.d.ts.map +0 -1
- package/dist/ui/chart-source-switcher.js +0 -31
- package/dist/ui/chart-time-bucket-selector.d.ts +0 -9
- package/dist/ui/chart-time-bucket-selector.d.ts.map +0 -1
- package/dist/ui/chart-time-bucket-selector.js +0 -25
- package/dist/ui/chart-toolbar-overflow.d.ts +0 -29
- package/dist/ui/chart-toolbar-overflow.d.ts.map +0 -1
- package/dist/ui/chart-toolbar-overflow.js +0 -109
- package/dist/ui/chart-toolbar.d.ts +0 -45
- package/dist/ui/chart-toolbar.d.ts.map +0 -1
- package/dist/ui/chart-toolbar.js +0 -44
- package/dist/ui/chart-type-selector.d.ts +0 -8
- package/dist/ui/chart-type-selector.d.ts.map +0 -1
- package/dist/ui/chart-type-selector.js +0 -22
- package/dist/ui/chart-x-axis-selector.d.ts +0 -8
- package/dist/ui/chart-x-axis-selector.d.ts.map +0 -1
- package/dist/ui/chart-x-axis-selector.js +0 -14
- package/dist/ui/index.d.ts +0 -25
- package/dist/ui/index.d.ts.map +0 -1
- package/dist/ui/index.js +0 -23
- package/dist/ui/toolbar-types.d.ts +0 -43
- package/dist/ui/toolbar-types.d.ts.map +0 -1
- package/dist/ui/toolbar-types.js +0 -50
package/README.md
CHANGED
|
@@ -364,6 +364,30 @@ If you are importing the package source directly in a local playground or monore
|
|
|
364
364
|
|
|
365
365
|
If your app already uses shadcn-style tokens, also make sure tokens such as `background`, `foreground`, `muted`, `border`, `popover`, `primary`, `ring`, and optionally `chart-1` through `chart-5` are defined in your theme.
|
|
366
366
|
|
|
367
|
+
## On the Radar
|
|
368
|
+
|
|
369
|
+
These are known limitations and areas being considered for future versions. None of these are committed — they represent directions the library may grow based on real usage.
|
|
370
|
+
|
|
371
|
+
### Renderer flexibility
|
|
372
|
+
|
|
373
|
+
The UI layer currently only supports Recharts. If you want to use ECharts, Plotly, or another renderer, you can use the headless core but lose the built-in toolbar and canvas composition. A renderer adapter pattern for `<ChartCanvas>` could make the UI layer renderer-agnostic.
|
|
374
|
+
|
|
375
|
+
### Richer aggregation
|
|
376
|
+
|
|
377
|
+
The pipeline supports sum, avg, min, and max. Derived columns can access multiple fields of a single row (e.g. `row.revenue - row.cost`), but there is no support yet for metrics that depend on other rows or on aggregated results — things like "% of total", running totals, percentiles, or post-aggregation ratios (e.g. total revenue / total orders).
|
|
378
|
+
|
|
379
|
+
### Chart interactivity
|
|
380
|
+
|
|
381
|
+
There is currently no built-in support for drill-down, click-to-filter, brush selection, or linked charts. The headless state can be wired manually to achieve some of these, but first-class interactivity primitives would make this significantly easier.
|
|
382
|
+
|
|
383
|
+
### Multi-dataset composition
|
|
384
|
+
|
|
385
|
+
Each chart instance operates on a single flat dataset. Overlaying series from different schemas (e.g. revenue on the left Y-axis and headcount on the right) would require separate chart instances today. Dual-axis and cross-dataset composition are not yet supported.
|
|
386
|
+
|
|
387
|
+
### The double-call schema syntax
|
|
388
|
+
|
|
389
|
+
`defineChartSchema<Row>()()` uses a double function call as a workaround for TypeScript's lack of partial type argument inference. This lets you provide the row type explicitly while the column IDs are inferred automatically. It works well but can surprise newcomers — this will be revisited if TypeScript adds native support for partial inference.
|
|
390
|
+
|
|
367
391
|
## Release
|
|
368
392
|
|
|
369
393
|
- `bun run release:check`
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
//#region src/core/chart-capabilities.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* X-axis kinds understood by chart-studio when validating chart types.
|
|
4
|
+
*/
|
|
5
|
+
type ChartAxisType = 'date' | 'category' | 'boolean';
|
|
6
|
+
/**
|
|
7
|
+
* Declarative chart-type capability matrix.
|
|
8
|
+
*
|
|
9
|
+
* Centralizing these rules keeps UI controls and state validation aligned as
|
|
10
|
+
* more chart types are added later.
|
|
11
|
+
*/
|
|
12
|
+
type ChartTypeCapabilities = {
|
|
13
|
+
supportedXAxisTypes: readonly ChartAxisType[];
|
|
14
|
+
supportsGrouping: boolean;
|
|
15
|
+
supportsTimeBucketing: boolean;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Capabilities for each supported chart type.
|
|
19
|
+
*/
|
|
20
|
+
declare const CHART_TYPE_CONFIG: {
|
|
21
|
+
readonly bar: {
|
|
22
|
+
readonly supportedXAxisTypes: readonly ["date", "category", "boolean"];
|
|
23
|
+
readonly supportsGrouping: true;
|
|
24
|
+
readonly supportsTimeBucketing: true;
|
|
25
|
+
};
|
|
26
|
+
readonly line: {
|
|
27
|
+
readonly supportedXAxisTypes: readonly ["date"];
|
|
28
|
+
readonly supportsGrouping: true;
|
|
29
|
+
readonly supportsTimeBucketing: true;
|
|
30
|
+
};
|
|
31
|
+
readonly area: {
|
|
32
|
+
readonly supportedXAxisTypes: readonly ["date"];
|
|
33
|
+
readonly supportsGrouping: true;
|
|
34
|
+
readonly supportsTimeBucketing: true;
|
|
35
|
+
};
|
|
36
|
+
readonly pie: {
|
|
37
|
+
readonly supportedXAxisTypes: readonly ["category", "boolean"];
|
|
38
|
+
readonly supportsGrouping: false;
|
|
39
|
+
readonly supportsTimeBucketing: false;
|
|
40
|
+
};
|
|
41
|
+
readonly donut: {
|
|
42
|
+
readonly supportedXAxisTypes: readonly ["category", "boolean"];
|
|
43
|
+
readonly supportsGrouping: false;
|
|
44
|
+
readonly supportsTimeBucketing: false;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
//#endregion
|
|
48
|
+
export { CHART_TYPE_CONFIG, ChartAxisType, ChartTypeCapabilities };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { CHART_TYPE_ORDER } from "./config-utils.mjs";
|
|
2
|
+
//#region src/core/chart-capabilities.ts
|
|
3
|
+
/**
|
|
4
|
+
* Capabilities for each supported chart type.
|
|
5
|
+
*/
|
|
6
|
+
const CHART_TYPE_CONFIG = {
|
|
7
|
+
bar: {
|
|
8
|
+
supportedXAxisTypes: [
|
|
9
|
+
"date",
|
|
10
|
+
"category",
|
|
11
|
+
"boolean"
|
|
12
|
+
],
|
|
13
|
+
supportsGrouping: true,
|
|
14
|
+
supportsTimeBucketing: true
|
|
15
|
+
},
|
|
16
|
+
line: {
|
|
17
|
+
supportedXAxisTypes: ["date"],
|
|
18
|
+
supportsGrouping: true,
|
|
19
|
+
supportsTimeBucketing: true
|
|
20
|
+
},
|
|
21
|
+
area: {
|
|
22
|
+
supportedXAxisTypes: ["date"],
|
|
23
|
+
supportsGrouping: true,
|
|
24
|
+
supportsTimeBucketing: true
|
|
25
|
+
},
|
|
26
|
+
pie: {
|
|
27
|
+
supportedXAxisTypes: ["category", "boolean"],
|
|
28
|
+
supportsGrouping: false,
|
|
29
|
+
supportsTimeBucketing: false
|
|
30
|
+
},
|
|
31
|
+
donut: {
|
|
32
|
+
supportedXAxisTypes: ["category", "boolean"],
|
|
33
|
+
supportsGrouping: false,
|
|
34
|
+
supportsTimeBucketing: false
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Whether a chart type can represent the current chart state.
|
|
39
|
+
*/
|
|
40
|
+
function isChartTypeAvailable(chartType, availability) {
|
|
41
|
+
const { hasGroupBy, xAxisType } = availability;
|
|
42
|
+
if (xAxisType === null) return false;
|
|
43
|
+
const capabilities = CHART_TYPE_CONFIG[chartType];
|
|
44
|
+
if (!capabilities.supportedXAxisTypes.includes(xAxisType)) return false;
|
|
45
|
+
if (hasGroupBy && !capabilities.supportsGrouping) return false;
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Chart types valid for the current axis + feature combination.
|
|
50
|
+
*/
|
|
51
|
+
function getAvailableChartTypes(availability) {
|
|
52
|
+
return CHART_TYPE_ORDER.filter((chartType) => isChartTypeAvailable(chartType, availability));
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
export { CHART_TYPE_CONFIG, getAvailableChartTypes };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//#region src/core/colors.d.ts
|
|
1
2
|
/**
|
|
2
3
|
* Color assignment for chart series.
|
|
3
4
|
*
|
|
@@ -12,7 +13,7 @@
|
|
|
12
13
|
* @param useShadcn - Whether to use shadcn CSS variables (default: true)
|
|
13
14
|
* @returns CSS color value
|
|
14
15
|
*/
|
|
15
|
-
|
|
16
|
+
declare function getSeriesColor(index: number, useShadcn?: boolean): string;
|
|
16
17
|
/**
|
|
17
18
|
* Generate a color map for a list of group labels.
|
|
18
19
|
* Assigns colors deterministically based on array order.
|
|
@@ -21,5 +22,6 @@ export declare function getSeriesColor(index: number, useShadcn?: boolean): stri
|
|
|
21
22
|
* @param useShadcn - Whether to use shadcn CSS variables (default: true)
|
|
22
23
|
* @returns Map from group label to CSS color
|
|
23
24
|
*/
|
|
24
|
-
|
|
25
|
-
//#
|
|
25
|
+
declare function buildColorMap(groups: string[], useShadcn?: boolean): Map<string, string>;
|
|
26
|
+
//#endregion
|
|
27
|
+
export { buildColorMap, getSeriesColor };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
//#region src/core/colors.ts
|
|
2
|
+
/**
|
|
3
|
+
* Color assignment for chart series.
|
|
4
|
+
*
|
|
5
|
+
* Uses shadcn chart CSS variables (`--chart-1` through `--chart-5`) by default.
|
|
6
|
+
* Falls back to a built-in OKLCH palette when CSS variables are not available.
|
|
7
|
+
*/
|
|
8
|
+
/** Fallback palette using OKLCH for perceptually uniform colors. */
|
|
9
|
+
const FALLBACK_COLORS = [
|
|
10
|
+
"oklch(0.65 0.15 250)",
|
|
11
|
+
"oklch(0.65 0.15 350)",
|
|
12
|
+
"oklch(0.65 0.15 200)",
|
|
13
|
+
"oklch(0.65 0.15 70)",
|
|
14
|
+
"oklch(0.65 0.15 150)",
|
|
15
|
+
"oklch(0.65 0.15 30)",
|
|
16
|
+
"oklch(0.65 0.15 300)",
|
|
17
|
+
"oklch(0.65 0.15 120)",
|
|
18
|
+
"oklch(0.65 0.12 170)",
|
|
19
|
+
"oklch(0.65 0.12 220)"
|
|
20
|
+
];
|
|
21
|
+
/** Shadcn chart CSS variables (5 colors) with safe fallbacks. */
|
|
22
|
+
const SHADCN_CHART_COLORS = [
|
|
23
|
+
`var(--chart-1, var(--cs-chart-1, hsl(245 72% 57%)))`,
|
|
24
|
+
`var(--chart-2, var(--cs-chart-2, hsl(271 72% 55%)))`,
|
|
25
|
+
`var(--chart-3, var(--cs-chart-3, hsl(330 68% 54%)))`,
|
|
26
|
+
`var(--chart-4, var(--cs-chart-4, hsl(170 65% 38%)))`,
|
|
27
|
+
`var(--chart-5, var(--cs-chart-5, hsl(30 90% 54%)))`
|
|
28
|
+
];
|
|
29
|
+
/**
|
|
30
|
+
* Get a color for the Nth series.
|
|
31
|
+
* Cycles through the palette when index exceeds palette length.
|
|
32
|
+
*
|
|
33
|
+
* @param index - Zero-based series index
|
|
34
|
+
* @param useShadcn - Whether to use shadcn CSS variables (default: true)
|
|
35
|
+
* @returns CSS color value
|
|
36
|
+
*/
|
|
37
|
+
function getSeriesColor(index, useShadcn = true) {
|
|
38
|
+
const palette = useShadcn ? SHADCN_CHART_COLORS : FALLBACK_COLORS;
|
|
39
|
+
return palette[index % palette.length];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Generate a color map for a list of group labels.
|
|
43
|
+
* Assigns colors deterministically based on array order.
|
|
44
|
+
*
|
|
45
|
+
* @param groups - Array of group labels
|
|
46
|
+
* @param useShadcn - Whether to use shadcn CSS variables (default: true)
|
|
47
|
+
* @returns Map from group label to CSS color
|
|
48
|
+
*/
|
|
49
|
+
function buildColorMap(groups, useShadcn = true) {
|
|
50
|
+
const map = /* @__PURE__ */ new Map();
|
|
51
|
+
for (let i = 0; i < groups.length; i++) map.set(groups[i], getSeriesColor(i, useShadcn));
|
|
52
|
+
return map;
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
export { buildColorMap, getSeriesColor };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
//#region src/core/config-utils.ts
|
|
2
|
+
/**
|
|
3
|
+
* Build the visible runtime option list after applying config restrictions.
|
|
4
|
+
*
|
|
5
|
+
* The pipeline is always:
|
|
6
|
+
* 1. start from runtime-valid options
|
|
7
|
+
* 2. if `allowed` exists, keep only allowed entries in declared order
|
|
8
|
+
* 3. if `hidden` exists, subtract hidden entries
|
|
9
|
+
*
|
|
10
|
+
* When `fallbackToBaseIfEmpty` is enabled, required controls can recover their
|
|
11
|
+
* base runtime options if the config leaves nothing selectable for the active
|
|
12
|
+
* source. This recovery is intentionally separate from default resolution.
|
|
13
|
+
*/
|
|
14
|
+
function restrictConfiguredCollection(items, config, getKey, fallbackToBaseIfEmpty) {
|
|
15
|
+
const keyedItems = new Map(items.map((item) => [getKey(item), item]));
|
|
16
|
+
const allowedKeys = config?.allowed;
|
|
17
|
+
const hiddenKeys = config?.hidden ? new Set(config.hidden) : void 0;
|
|
18
|
+
const orderedItems = allowedKeys ? allowedKeys.flatMap((allowedKey) => {
|
|
19
|
+
const match = keyedItems.get(allowedKey);
|
|
20
|
+
return match ? [match] : [];
|
|
21
|
+
}) : [...items];
|
|
22
|
+
const visibleItems = hiddenKeys ? orderedItems.filter((item) => !hiddenKeys.has(getKey(item))) : orderedItems;
|
|
23
|
+
if (fallbackToBaseIfEmpty && visibleItems.length === 0) return [...items];
|
|
24
|
+
return visibleItems;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Restrict an ID-keyed option list with `allowed`/`hidden`.
|
|
28
|
+
*
|
|
29
|
+
* When `fallbackToBaseIfEmpty` is enabled, required controls keep their base
|
|
30
|
+
* runtime options even if the config does not match the active source.
|
|
31
|
+
*/
|
|
32
|
+
function restrictConfiguredIdOptions(options, config, fallbackToBaseIfEmpty = false) {
|
|
33
|
+
return restrictConfiguredCollection(options, config, (option) => option.id, fallbackToBaseIfEmpty);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolve one ID-based selection against the current option list.
|
|
37
|
+
*/
|
|
38
|
+
function resolveConfiguredIdSelection(currentValue, options, configuredDefault, fallbackValue, preferFirstAvailable = true) {
|
|
39
|
+
if (currentValue && options.some((option) => option.id === currentValue)) return currentValue;
|
|
40
|
+
if (configuredDefault && options.some((option) => option.id === configuredDefault)) return configuredDefault;
|
|
41
|
+
if (preferFirstAvailable) return options[0]?.id ?? fallbackValue;
|
|
42
|
+
return fallbackValue;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Restrict a primitive option list with `allowed`/`hidden`.
|
|
46
|
+
*/
|
|
47
|
+
function restrictConfiguredValues(values, config, fallbackToBaseIfEmpty = false) {
|
|
48
|
+
return restrictConfiguredCollection(values, config, (value) => value, fallbackToBaseIfEmpty);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Resolve one primitive selection against the current option list.
|
|
52
|
+
*/
|
|
53
|
+
function resolveConfiguredValue(currentValue, values, configuredDefault) {
|
|
54
|
+
if (values.includes(currentValue)) return currentValue;
|
|
55
|
+
if (configuredDefault && values.includes(configuredDefault)) return configuredDefault;
|
|
56
|
+
return values[0] ?? currentValue;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Ordered time buckets exposed by the headless API.
|
|
60
|
+
*/
|
|
61
|
+
const TIME_BUCKET_ORDER = [
|
|
62
|
+
"day",
|
|
63
|
+
"week",
|
|
64
|
+
"month",
|
|
65
|
+
"quarter",
|
|
66
|
+
"year"
|
|
67
|
+
];
|
|
68
|
+
/**
|
|
69
|
+
* Ordered chart types exposed by the headless API.
|
|
70
|
+
*/
|
|
71
|
+
const CHART_TYPE_ORDER = [
|
|
72
|
+
"bar",
|
|
73
|
+
"line",
|
|
74
|
+
"area",
|
|
75
|
+
"pie",
|
|
76
|
+
"donut"
|
|
77
|
+
];
|
|
78
|
+
//#endregion
|
|
79
|
+
export { CHART_TYPE_ORDER, TIME_BUCKET_ORDER, resolveConfiguredIdSelection, resolveConfiguredValue, restrictConfiguredIdOptions, restrictConfiguredValues };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
//#region src/core/date-utils.ts
|
|
2
|
+
/**
|
|
3
|
+
* Filter data by a date range on a specific date column.
|
|
4
|
+
* Both bounds are inclusive (to is extended to end of day).
|
|
5
|
+
*
|
|
6
|
+
* @param data - Raw data items
|
|
7
|
+
* @param dateColumn - The date column to filter on
|
|
8
|
+
* @param filter - Date range filter with from/to bounds
|
|
9
|
+
* @returns Filtered data items within the date range
|
|
10
|
+
*/
|
|
11
|
+
function filterByDateRange(data, dateColumn, filter) {
|
|
12
|
+
const { from, to } = filter;
|
|
13
|
+
if (!from && !to) return [...data];
|
|
14
|
+
const toEnd = to ? new Date(to.getFullYear(), to.getMonth(), to.getDate(), 23, 59, 59, 999) : null;
|
|
15
|
+
return data.filter((item) => {
|
|
16
|
+
const raw = dateColumn.accessor(item);
|
|
17
|
+
if (raw == null) return false;
|
|
18
|
+
const d = new Date(raw);
|
|
19
|
+
if (Number.isNaN(d.getTime())) return false;
|
|
20
|
+
if (from && d < from) return false;
|
|
21
|
+
if (toEnd && d > toEnd) return false;
|
|
22
|
+
return true;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Compute the min/max date range from data for a given date column.
|
|
27
|
+
*
|
|
28
|
+
* @param data - Data items to scan
|
|
29
|
+
* @param dateColumn - The date column to extract dates from
|
|
30
|
+
* @returns Object with min and max dates (both null if no valid dates)
|
|
31
|
+
*/
|
|
32
|
+
function computeDateRange(data, dateColumn) {
|
|
33
|
+
let min = null;
|
|
34
|
+
let max = null;
|
|
35
|
+
for (const item of data) {
|
|
36
|
+
const raw = dateColumn.accessor(item);
|
|
37
|
+
if (raw == null) continue;
|
|
38
|
+
const d = new Date(raw);
|
|
39
|
+
if (Number.isNaN(d.getTime())) continue;
|
|
40
|
+
if (!min || d < min) min = d;
|
|
41
|
+
if (!max || d > max) max = d;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
min,
|
|
45
|
+
max
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
export { computeDateRange, filterByDateRange };
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { ChartTypeConfig, DefinedChartSchema, ExactShape, FiltersConfig, GroupByConfig, MetricConfig, ResolvedFilterColumnIdFromSchema, ResolvedGroupByColumnIdFromSchema, ResolvedMetricColumnIdFromSchema, ResolvedXAxisColumnIdFromSchema, SchemaColumnsValidationShape, TimeBucketConfig, XAxisConfig } from "./types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/core/define-chart-schema.d.ts
|
|
4
|
+
type SchemaFromSections<T, TColumns extends Record<string, unknown> | undefined, TXAxis, TGroupBy, TFilters, TMetric, TChartType, TTimeBucket> = {
|
|
5
|
+
columns?: Extract<TColumns, Record<string, unknown> | undefined>;
|
|
6
|
+
xAxis?: Extract<TXAxis, XAxisConfig<ResolvedXAxisColumnIdFromSchema<T, {
|
|
7
|
+
columns?: TColumns;
|
|
8
|
+
}>> | undefined>;
|
|
9
|
+
groupBy?: Extract<TGroupBy, GroupByConfig<ResolvedGroupByColumnIdFromSchema<T, {
|
|
10
|
+
columns?: TColumns;
|
|
11
|
+
}>> | undefined>;
|
|
12
|
+
filters?: Extract<TFilters, FiltersConfig<ResolvedFilterColumnIdFromSchema<T, {
|
|
13
|
+
columns?: TColumns;
|
|
14
|
+
}>> | undefined>;
|
|
15
|
+
metric?: Extract<TMetric, MetricConfig<ResolvedMetricColumnIdFromSchema<T, {
|
|
16
|
+
columns?: TColumns;
|
|
17
|
+
}>> | undefined>;
|
|
18
|
+
chartType?: Extract<TChartType, ChartTypeConfig | undefined>;
|
|
19
|
+
timeBucket?: Extract<TTimeBucket, TimeBucketConfig | undefined>;
|
|
20
|
+
};
|
|
21
|
+
type DefineChartSchemaInput<T, TColumns extends Record<string, unknown> | undefined, TXAxis, TGroupBy, TFilters, TMetric, TChartType, TTimeBucket> = {
|
|
22
|
+
/**
|
|
23
|
+
* Shape the available chart columns.
|
|
24
|
+
*
|
|
25
|
+
* This is usually the most important part of the schema. Use it to:
|
|
26
|
+
* - rename inferred raw fields with `label`
|
|
27
|
+
* - force a field to a specific `type`
|
|
28
|
+
* - apply `format` or `formatter`
|
|
29
|
+
* - remove a raw field with `false`
|
|
30
|
+
* - add brand new derived columns with `kind: 'derived'`
|
|
31
|
+
*/
|
|
32
|
+
columns?: TColumns & ExactShape<SchemaColumnsValidationShape<T, NoInfer<TColumns>>, NoInfer<TColumns>>;
|
|
33
|
+
/**
|
|
34
|
+
* Restrict which resolved columns may be selected on the X-axis.
|
|
35
|
+
*
|
|
36
|
+
* Use this when you want to expose only a subset of possible X-axis fields.
|
|
37
|
+
*/
|
|
38
|
+
xAxis?: TXAxis & ExactShape<XAxisConfig<ResolvedXAxisColumnIdFromSchema<T, {
|
|
39
|
+
columns?: TColumns;
|
|
40
|
+
}>>, NoInfer<TXAxis>>;
|
|
41
|
+
/**
|
|
42
|
+
* Restrict which resolved columns may be used to split the chart into series.
|
|
43
|
+
*
|
|
44
|
+
* This powers grouped / multi-series charts.
|
|
45
|
+
*/
|
|
46
|
+
groupBy?: TGroupBy & ExactShape<GroupByConfig<ResolvedGroupByColumnIdFromSchema<T, {
|
|
47
|
+
columns?: TColumns;
|
|
48
|
+
}>>, NoInfer<TGroupBy>>;
|
|
49
|
+
/**
|
|
50
|
+
* Restrict which resolved columns appear in the filters UI.
|
|
51
|
+
*
|
|
52
|
+
* Only category and boolean-like columns are eligible here.
|
|
53
|
+
*/
|
|
54
|
+
filters?: TFilters & ExactShape<FiltersConfig<ResolvedFilterColumnIdFromSchema<T, {
|
|
55
|
+
columns?: TColumns;
|
|
56
|
+
}>>, NoInfer<TFilters>>;
|
|
57
|
+
/**
|
|
58
|
+
* Restrict which metrics and aggregate combinations remain selectable.
|
|
59
|
+
*
|
|
60
|
+
* Use this when you want to curate the metric dropdown rather than exposing
|
|
61
|
+
* every available numeric aggregate.
|
|
62
|
+
*/
|
|
63
|
+
metric?: TMetric & ExactShape<MetricConfig<ResolvedMetricColumnIdFromSchema<T, {
|
|
64
|
+
columns?: TColumns;
|
|
65
|
+
}>>, NoInfer<TMetric>>; /** Restrict which chart renderers are available to the user. */
|
|
66
|
+
chartType?: TChartType & ExactShape<ChartTypeConfig, NoInfer<TChartType>>;
|
|
67
|
+
/**
|
|
68
|
+
* Restrict which time buckets remain available for date X-axes.
|
|
69
|
+
*
|
|
70
|
+
* Example: allow only `'month'` and `'quarter'`.
|
|
71
|
+
*/
|
|
72
|
+
timeBucket?: TTimeBucket & ExactShape<TimeBucketConfig, NoInfer<TTimeBucket>>;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Define one explicit chart schema with strict exact-object checking.
|
|
76
|
+
*
|
|
77
|
+
* The schema is the single advanced authoring surface for chart-studio:
|
|
78
|
+
* `columns` can override or exclude inferred raw fields and also define derived
|
|
79
|
+
* columns, while the top-level control sections restrict the public chart
|
|
80
|
+
* contract.
|
|
81
|
+
*
|
|
82
|
+
* Typical shape:
|
|
83
|
+
*
|
|
84
|
+
* ```ts
|
|
85
|
+
* const schema = defineChartSchema<Row>()({
|
|
86
|
+
* columns: {
|
|
87
|
+
* createdAt: {type: 'date', label: 'Created'},
|
|
88
|
+
* revenue: {type: 'number', format: 'currency'},
|
|
89
|
+
* margin: {
|
|
90
|
+
* kind: 'derived',
|
|
91
|
+
* type: 'number',
|
|
92
|
+
* label: 'Margin',
|
|
93
|
+
* format: 'percent',
|
|
94
|
+
* accessor: row => row.profit / row.revenue,
|
|
95
|
+
* },
|
|
96
|
+
* },
|
|
97
|
+
* xAxis: {allowed: ['createdAt']},
|
|
98
|
+
* metric: {
|
|
99
|
+
* allowed: [{kind: 'aggregate', columnId: 'revenue', aggregate: 'sum'}],
|
|
100
|
+
* },
|
|
101
|
+
* })
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
declare function defineChartSchema<T>(): <const TColumns extends Record<string, unknown> | undefined = undefined, const TXAxis = undefined, const TGroupBy = undefined, const TFilters = undefined, const TMetric = undefined, const TChartType = undefined, const TTimeBucket = undefined>(schema: DefineChartSchemaInput<T, TColumns, TXAxis, TGroupBy, TFilters, TMetric, TChartType, TTimeBucket>) => DefinedChartSchema<T, SchemaFromSections<T, TColumns, TXAxis, TGroupBy, TFilters, TMetric, TChartType, TTimeBucket>>;
|
|
105
|
+
//#endregion
|
|
106
|
+
export { defineChartSchema };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
//#region src/core/define-chart-schema.ts
|
|
2
|
+
/**
|
|
3
|
+
* Define one explicit chart schema with strict exact-object checking.
|
|
4
|
+
*
|
|
5
|
+
* The schema is the single advanced authoring surface for chart-studio:
|
|
6
|
+
* `columns` can override or exclude inferred raw fields and also define derived
|
|
7
|
+
* columns, while the top-level control sections restrict the public chart
|
|
8
|
+
* contract.
|
|
9
|
+
*
|
|
10
|
+
* Typical shape:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* const schema = defineChartSchema<Row>()({
|
|
14
|
+
* columns: {
|
|
15
|
+
* createdAt: {type: 'date', label: 'Created'},
|
|
16
|
+
* revenue: {type: 'number', format: 'currency'},
|
|
17
|
+
* margin: {
|
|
18
|
+
* kind: 'derived',
|
|
19
|
+
* type: 'number',
|
|
20
|
+
* label: 'Margin',
|
|
21
|
+
* format: 'percent',
|
|
22
|
+
* accessor: row => row.profit / row.revenue,
|
|
23
|
+
* },
|
|
24
|
+
* },
|
|
25
|
+
* xAxis: {allowed: ['createdAt']},
|
|
26
|
+
* metric: {
|
|
27
|
+
* allowed: [{kind: 'aggregate', columnId: 'revenue', aggregate: 'sum'}],
|
|
28
|
+
* },
|
|
29
|
+
* })
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
function defineChartSchema() {
|
|
33
|
+
/**
|
|
34
|
+
* Brand one schema object while preserving its literal types.
|
|
35
|
+
*
|
|
36
|
+
* This is what lets the schema stay both strongly typed and editor-friendly
|
|
37
|
+
* when it is later passed to `useChart(...)`.
|
|
38
|
+
*/
|
|
39
|
+
return function defineSchema(schema) {
|
|
40
|
+
return {
|
|
41
|
+
...schema,
|
|
42
|
+
__chartSchemaBrand: "chart-schema-definition"
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { defineChartSchema };
|