@matthieumordrel/chart-studio 0.2.4 → 0.2.5
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 +42 -42
- package/dist/core/config-utils.mjs +3 -2
- package/dist/core/define-chart-schema.d.mts +26 -94
- package/dist/core/define-chart-schema.mjs +26 -34
- package/dist/core/infer-columns.d.mts +2 -2
- package/dist/core/infer-columns.mjs +4 -2
- package/dist/core/metric-utils.mjs +13 -5
- package/dist/core/schema-builder.mjs +335 -0
- package/dist/core/schema-builder.types.d.mts +279 -0
- package/dist/core/types.d.mts +24 -11
- package/dist/core/use-chart-options.d.mts +7 -4
- package/dist/core/use-chart.d.mts +4 -4
- package/dist/core/use-chart.mjs +20 -15
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/ui/chart-canvas.d.mts +7 -3
- package/dist/ui/chart-canvas.mjs +62 -22
- package/dist/ui/chart-context.d.mts +8 -4
- package/dist/ui/chart-debug.d.mts +6 -2
- package/dist/ui/chart-debug.mjs +5 -1
- package/dist/ui/chart-toolbar.d.mts +6 -2
- package/dist/ui/chart-toolbar.mjs +4 -0
- package/dist/ui/percent-stacked.mjs +36 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,6 +22,10 @@ You get:
|
|
|
22
22
|
- transformed chart data
|
|
23
23
|
- filtering, grouping, metrics, and time bucketing logic
|
|
24
24
|
|
|
25
|
+
Requirements:
|
|
26
|
+
|
|
27
|
+
- `react` >= 18.2.0
|
|
28
|
+
|
|
25
29
|
Install:
|
|
26
30
|
|
|
27
31
|
```bash
|
|
@@ -46,6 +50,12 @@ You get:
|
|
|
46
50
|
- `<ChartCanvas>`
|
|
47
51
|
- granular UI controls from `@matthieumordrel/chart-studio/ui`
|
|
48
52
|
|
|
53
|
+
Requirements:
|
|
54
|
+
|
|
55
|
+
- `react` >= 18.2.0
|
|
56
|
+
- `recharts` >= 3.0.0 (v2 is **not** supported)
|
|
57
|
+
- `lucide-react` >= 0.577.0 (optional, for toolbar icons)
|
|
58
|
+
|
|
49
59
|
Install:
|
|
50
60
|
|
|
51
61
|
```bash
|
|
@@ -88,7 +98,7 @@ export function JobsChart() {
|
|
|
88
98
|
## How It Works
|
|
89
99
|
|
|
90
100
|
1. Pass your raw data to `useChart()`.
|
|
91
|
-
2. Add an optional `schema` with `defineChartSchema<Row>()
|
|
101
|
+
2. Add an optional `schema` with `defineChartSchema<Row>()...` when you need labels, type overrides, derived columns, or control restrictions (allowed metrics, groupings, chart types, etc.).
|
|
92
102
|
3. Either render your own UI from the returned state, or use the components from `@matthieumordrel/chart-studio/ui`.
|
|
93
103
|
|
|
94
104
|
## Column Types
|
|
@@ -102,41 +112,39 @@ export function JobsChart() {
|
|
|
102
112
|
|
|
103
113
|
## Declarative Schema and Control Restrictions
|
|
104
114
|
|
|
105
|
-
If you want to expose only a subset of groupings, metrics, chart types, or axes, use `defineChartSchema<Row>()
|
|
115
|
+
If you want to expose only a subset of groupings, metrics, chart types, or axes, use the fluent `defineChartSchema<Row>()` builder:
|
|
106
116
|
|
|
107
117
|
```tsx
|
|
108
118
|
import { defineChartSchema, useChart } from '@matthieumordrel/chart-studio'
|
|
109
119
|
|
|
110
120
|
type Row = { periodEnd: string; segment: string; revenue: number; netIncome: number }
|
|
111
121
|
|
|
112
|
-
const schema = defineChartSchema<Row>()
|
|
113
|
-
columns
|
|
114
|
-
periodEnd
|
|
115
|
-
segment
|
|
116
|
-
revenue
|
|
117
|
-
netIncome
|
|
118
|
-
|
|
119
|
-
xAxis
|
|
120
|
-
groupBy
|
|
121
|
-
metric
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
timeBucket: { allowed: ['year', 'quarter', 'month'] }
|
|
130
|
-
})
|
|
122
|
+
const schema = defineChartSchema<Row>()
|
|
123
|
+
.columns((c) => [
|
|
124
|
+
c.date('periodEnd', { label: 'Period End' }),
|
|
125
|
+
c.category('segment'),
|
|
126
|
+
c.number('revenue'),
|
|
127
|
+
c.number('netIncome')
|
|
128
|
+
])
|
|
129
|
+
.xAxis((x) => x.allowed('periodEnd'))
|
|
130
|
+
.groupBy((g) => g.allowed('segment'))
|
|
131
|
+
.metric((m) =>
|
|
132
|
+
m
|
|
133
|
+
.count()
|
|
134
|
+
.aggregate('revenue', 'sum', 'avg')
|
|
135
|
+
.aggregate('netIncome', 'sum')
|
|
136
|
+
)
|
|
137
|
+
.chartType((t) => t.allowed('bar', 'line'))
|
|
138
|
+
.timeBucket((tb) => tb.allowed('year', 'quarter', 'month'))
|
|
131
139
|
|
|
132
140
|
const chart = useChart({ data, schema })
|
|
133
141
|
```
|
|
134
142
|
|
|
135
143
|
Why this pattern:
|
|
136
144
|
|
|
137
|
-
- `columns` defines types, labels, and formats for raw fields; use `
|
|
138
|
-
- Derived columns use `
|
|
139
|
-
- `xAxis`, `groupBy`, `metric`, `chartType`, `timeBucket` restrict the allowed options
|
|
145
|
+
- `columns` defines types, labels, and formats for raw fields; use `c.exclude(...)` to remove a column from the chart
|
|
146
|
+
- Derived columns use `c.derived.*(...)` helpers for computed values from each row
|
|
147
|
+
- `xAxis`, `groupBy`, `metric`, `chartType`, and `timeBucket` restrict the allowed options
|
|
140
148
|
- invalid column IDs and config keys are rejected at compile time
|
|
141
149
|
- metric restrictions preserve the order you declare, so the first allowed metric becomes the default
|
|
142
150
|
|
|
@@ -153,13 +161,12 @@ type Job = {
|
|
|
153
161
|
salary: number
|
|
154
162
|
}
|
|
155
163
|
|
|
156
|
-
const jobSchema = defineChartSchema<Job>()
|
|
157
|
-
columns
|
|
158
|
-
dateAdded
|
|
159
|
-
ownerName
|
|
160
|
-
salary
|
|
161
|
-
|
|
162
|
-
})
|
|
164
|
+
const jobSchema = defineChartSchema<Job>()
|
|
165
|
+
.columns((c) => [
|
|
166
|
+
c.date('dateAdded', { label: 'Date Added' }),
|
|
167
|
+
c.category('ownerName', { label: 'Consultant' }),
|
|
168
|
+
c.number('salary', { label: 'Salary' })
|
|
169
|
+
])
|
|
163
170
|
|
|
164
171
|
export function JobsChartHeadless({ data }: { data: Job[] }) {
|
|
165
172
|
const chart = useChart({ data, schema: jobSchema })
|
|
@@ -298,12 +305,6 @@ Minimal example:
|
|
|
298
305
|
}
|
|
299
306
|
```
|
|
300
307
|
|
|
301
|
-
## Compatibility
|
|
302
|
-
|
|
303
|
-
- `react`: `>=18.2.0 <20`
|
|
304
|
-
- `recharts`: `>=3.0.0 <4` for the UI layer
|
|
305
|
-
- `lucide-react`: `>=0.577.0 <1` for the UI layer
|
|
306
|
-
|
|
307
308
|
## Common Questions
|
|
308
309
|
|
|
309
310
|
### Which import path should I use?
|
|
@@ -332,9 +333,8 @@ const chart = useChart({
|
|
|
332
333
|
id: 'jobs',
|
|
333
334
|
label: 'Jobs',
|
|
334
335
|
data: jobs,
|
|
335
|
-
schema: defineChartSchema<Job>()
|
|
336
|
-
columns
|
|
337
|
-
})
|
|
336
|
+
schema: defineChartSchema<Job>()
|
|
337
|
+
.columns((c) => [c.date('dateAdded', { label: 'Date Added' })])
|
|
338
338
|
},
|
|
339
339
|
{ id: 'candidates', label: 'Candidates', data: candidates }
|
|
340
340
|
]
|
|
@@ -384,9 +384,9 @@ There is currently no built-in support for drill-down, click-to-filter, brush se
|
|
|
384
384
|
|
|
385
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
386
|
|
|
387
|
-
###
|
|
387
|
+
### Schema Builder Ergonomics
|
|
388
388
|
|
|
389
|
-
`defineChartSchema<Row>()
|
|
389
|
+
`defineChartSchema<Row>()` now returns one fluent builder that you pass directly to `useChart(...)` or `inferColumnsFromData(...)`. That keeps the public API strongly typed while improving IntelliSense for raw field ids, derived columns, and control restrictions.
|
|
390
390
|
|
|
391
391
|
## Release
|
|
392
392
|
|
|
@@ -50,9 +50,10 @@ function restrictConfiguredValues(values, config, fallbackToBaseIfEmpty = false)
|
|
|
50
50
|
/**
|
|
51
51
|
* Resolve one primitive selection against the current option list.
|
|
52
52
|
*/
|
|
53
|
-
function resolveConfiguredValue(currentValue, values, configuredDefault) {
|
|
54
|
-
if (values.includes(currentValue)) return currentValue;
|
|
53
|
+
function resolveConfiguredValue(currentValue, values, configuredDefault, globalDefault) {
|
|
54
|
+
if (currentValue !== null && values.includes(currentValue)) return currentValue;
|
|
55
55
|
if (configuredDefault && values.includes(configuredDefault)) return configuredDefault;
|
|
56
|
+
if (globalDefault && values.includes(globalDefault)) return globalDefault;
|
|
56
57
|
return values[0] ?? currentValue;
|
|
57
58
|
}
|
|
58
59
|
/**
|
|
@@ -1,106 +1,38 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ChartSchemaBuilder } from "./schema-builder.types.mjs";
|
|
2
2
|
|
|
3
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
4
|
/**
|
|
75
|
-
* Define one explicit chart schema
|
|
5
|
+
* Define one explicit chart schema through a fluent builder API.
|
|
76
6
|
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* columns, while the top-level control sections restrict the public chart
|
|
80
|
-
* contract.
|
|
7
|
+
* Put `.columns(...)` early in the chain so later sections can narrow against
|
|
8
|
+
* the declared column ids and roles.
|
|
81
9
|
*
|
|
82
10
|
* Typical shape:
|
|
83
11
|
*
|
|
84
12
|
* ```ts
|
|
85
|
-
* const schema = defineChartSchema<Row>()
|
|
86
|
-
* columns
|
|
87
|
-
* createdAt
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* label: '
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
* metric
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
13
|
+
* const schema = defineChartSchema<Row>()
|
|
14
|
+
* .columns((c) => [
|
|
15
|
+
* c.date('createdAt', {label: 'Created'}),
|
|
16
|
+
* c.category('ownerName', {label: 'Owner'}),
|
|
17
|
+
* c.number('salary', {format: 'currency'}),
|
|
18
|
+
* c.exclude('internalId'),
|
|
19
|
+
* c.derived.category('salaryBand', {
|
|
20
|
+
* label: 'Salary Band',
|
|
21
|
+
* accessor: row => row.salary != null && row.salary > 100_000 ? 'High' : 'Base',
|
|
22
|
+
* }),
|
|
23
|
+
* ])
|
|
24
|
+
* .xAxis((x) => x.allowed('createdAt').default('createdAt'))
|
|
25
|
+
* .groupBy((g) => g.allowed('ownerName', 'salaryBand'))
|
|
26
|
+
* .metric((m) =>
|
|
27
|
+
* m
|
|
28
|
+
* .count()
|
|
29
|
+
* .aggregate('salary', 'sum', 'avg')
|
|
30
|
+
* .defaultAggregate('salary', 'sum')
|
|
31
|
+
* )
|
|
32
|
+
*
|
|
33
|
+
* // Pass the builder directly to useChart(...) or inferColumnsFromData(...).
|
|
102
34
|
* ```
|
|
103
35
|
*/
|
|
104
|
-
declare function defineChartSchema<
|
|
36
|
+
declare function defineChartSchema<TRow>(): ChartSchemaBuilder<TRow, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined>;
|
|
105
37
|
//#endregion
|
|
106
38
|
export { defineChartSchema };
|
|
@@ -1,47 +1,39 @@
|
|
|
1
|
+
import { createChartSchemaBuilder } from "./schema-builder.mjs";
|
|
1
2
|
//#region src/core/define-chart-schema.ts
|
|
2
3
|
/**
|
|
3
|
-
* Define one explicit chart schema
|
|
4
|
+
* Define one explicit chart schema through a fluent builder API.
|
|
4
5
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* columns, while the top-level control sections restrict the public chart
|
|
8
|
-
* contract.
|
|
6
|
+
* Put `.columns(...)` early in the chain so later sections can narrow against
|
|
7
|
+
* the declared column ids and roles.
|
|
9
8
|
*
|
|
10
9
|
* Typical shape:
|
|
11
10
|
*
|
|
12
11
|
* ```ts
|
|
13
|
-
* const schema = defineChartSchema<Row>()
|
|
14
|
-
* columns
|
|
15
|
-
* createdAt
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* label: '
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* metric
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
12
|
+
* const schema = defineChartSchema<Row>()
|
|
13
|
+
* .columns((c) => [
|
|
14
|
+
* c.date('createdAt', {label: 'Created'}),
|
|
15
|
+
* c.category('ownerName', {label: 'Owner'}),
|
|
16
|
+
* c.number('salary', {format: 'currency'}),
|
|
17
|
+
* c.exclude('internalId'),
|
|
18
|
+
* c.derived.category('salaryBand', {
|
|
19
|
+
* label: 'Salary Band',
|
|
20
|
+
* accessor: row => row.salary != null && row.salary > 100_000 ? 'High' : 'Base',
|
|
21
|
+
* }),
|
|
22
|
+
* ])
|
|
23
|
+
* .xAxis((x) => x.allowed('createdAt').default('createdAt'))
|
|
24
|
+
* .groupBy((g) => g.allowed('ownerName', 'salaryBand'))
|
|
25
|
+
* .metric((m) =>
|
|
26
|
+
* m
|
|
27
|
+
* .count()
|
|
28
|
+
* .aggregate('salary', 'sum', 'avg')
|
|
29
|
+
* .defaultAggregate('salary', 'sum')
|
|
30
|
+
* )
|
|
31
|
+
*
|
|
32
|
+
* // Pass the builder directly to useChart(...) or inferColumnsFromData(...).
|
|
30
33
|
* ```
|
|
31
34
|
*/
|
|
32
35
|
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
|
-
};
|
|
36
|
+
return createChartSchemaBuilder();
|
|
45
37
|
}
|
|
46
38
|
//#endregion
|
|
47
39
|
export { defineChartSchema };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { ChartColumn,
|
|
1
|
+
import { ChartColumn, ChartSchemaDefinition, ResolvedChartSchemaFromDefinition, ResolvedColumnIdFromSchema } from "./types.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/core/infer-columns.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Resolve chart columns directly from raw data and an optional explicit schema.
|
|
6
6
|
*/
|
|
7
|
-
declare function inferColumnsFromData<T, const TSchema extends
|
|
7
|
+
declare function inferColumnsFromData<T, const TSchema extends ChartSchemaDefinition<T, any> | undefined = undefined>(data: readonly T[], schema?: TSchema): readonly ChartColumn<T, ResolvedColumnIdFromSchema<T, ResolvedChartSchemaFromDefinition<TSchema>>>[];
|
|
8
8
|
//#endregion
|
|
9
9
|
export { inferColumnsFromData };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveChartSchemaDefinition } from "./schema-builder.mjs";
|
|
1
2
|
//#region src/core/infer-columns.ts
|
|
2
3
|
const MAX_SAMPLE_COUNT = 50;
|
|
3
4
|
const DATE_KEY_PATTERN = /(date|time|timestamp|created|updated|start|end|deadline|due|scheduled|posted|published|at)$/i;
|
|
@@ -463,7 +464,8 @@ function sortResolvedColumns(columns) {
|
|
|
463
464
|
* Resolve chart columns directly from raw data and an optional explicit schema.
|
|
464
465
|
*/
|
|
465
466
|
function inferColumnsFromData(data, schema) {
|
|
466
|
-
const
|
|
467
|
+
const resolvedSchema = resolveChartSchemaDefinition(schema);
|
|
468
|
+
const rawColumnSchema = getRawColumnSchemaMap(resolvedSchema);
|
|
467
469
|
const fields = collectFieldKeys(data, rawColumnSchema);
|
|
468
470
|
const rawFieldIds = new Set(fields);
|
|
469
471
|
const resolvedColumns = [];
|
|
@@ -473,7 +475,7 @@ function inferColumnsFromData(data, schema) {
|
|
|
473
475
|
const column = buildRawColumn(typedField, samples, rawColumnSchema?.[typedField]);
|
|
474
476
|
if (column) resolvedColumns.push(column);
|
|
475
477
|
}
|
|
476
|
-
for (const [key, columnSchema] of getDerivedColumnSchemas(
|
|
478
|
+
for (const [key, columnSchema] of getDerivedColumnSchemas(resolvedSchema, rawFieldIds)) resolvedColumns.push(buildDerivedColumn(key, columnSchema));
|
|
477
479
|
if (resolvedColumns.length === 0) warn("No inferable or explicit chart columns were found. Provide non-empty data or schema.columns.");
|
|
478
480
|
return finalizeResolvedColumns(sortResolvedColumns(resolvedColumns));
|
|
479
481
|
}
|
|
@@ -109,13 +109,21 @@ function restrictAvailableMetrics(metrics, config) {
|
|
|
109
109
|
*/
|
|
110
110
|
function resolveMetric(metric, columns, availableMetrics, configuredDefaultMetric) {
|
|
111
111
|
if (availableMetrics && availableMetrics.length > 0) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
if (metric !== null) {
|
|
113
|
+
const selectedMetric = availableMetrics.find((candidate) => isSameMetric(candidate, metric));
|
|
114
|
+
if (selectedMetric) return selectedMetric;
|
|
115
|
+
}
|
|
116
|
+
const defaultMetric = configuredDefaultMetric ? availableMetrics.find((candidate) => isSameMetric(candidate, configuredDefaultMetric)) : void 0;
|
|
117
|
+
if (defaultMetric) return defaultMetric;
|
|
118
|
+
if (metric === null) {
|
|
119
|
+
const countMetric = availableMetrics.find((candidate) => candidate.kind === "count");
|
|
120
|
+
if (countMetric) return countMetric;
|
|
121
|
+
}
|
|
122
|
+
return availableMetrics[0];
|
|
115
123
|
}
|
|
116
|
-
if (!isAggregateMetric(metric)) return DEFAULT_METRIC;
|
|
124
|
+
if (metric === null || !isAggregateMetric(metric)) return DEFAULT_METRIC;
|
|
117
125
|
if (!columns.find((candidate) => candidate.type === "number" && candidate.id === metric.columnId)) return DEFAULT_METRIC;
|
|
118
126
|
return metric;
|
|
119
127
|
}
|
|
120
128
|
//#endregion
|
|
121
|
-
export { DEFAULT_METRIC, buildAvailableMetrics, getMetricLabel, isAggregateMetric, isSameMetric, resolveMetric, restrictAvailableMetrics };
|
|
129
|
+
export { DEFAULT_METRIC, buildAvailableMetrics, getMetricLabel, isAggregateMetric, isSameMetric, normalizeMetricAllowances, resolveMetric, restrictAvailableMetrics };
|