@matthieumordrel/chart-studio 0.2.4 → 0.3.0

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.
@@ -501,7 +501,18 @@ type ChartSchemaValidationTarget<T, TSchema extends ChartSchema<T, any>> = {
501
501
  type ValidatedChartSchema<T, TSchema> = TSchema extends ChartSchema<T, any> ? TSchema & ExactShape<ChartSchemaValidationTarget<T, TSchema>, TSchema> & ValidateChartSchemaLiterals<T, TSchema> : TSchema;
502
502
  /** Strict schema object returned by `defineChartSchema(...)`. */
503
503
  type DefinedChartSchema<T, TSchema extends ChartSchema<T, any> = ChartSchema<T, any>> = TSchema & ChartSchemaDefinitionBrand;
504
- type ResolvedChartSchemaFromDefinition<TSchema> = TSchema extends DefinedChartSchema<any, infer TResolvedSchema> ? TResolvedSchema : undefined;
504
+ /**
505
+ * Public schema definition input accepted by chart-studio APIs.
506
+ *
507
+ * Most callers use the fluent builder returned by `defineChartSchema<Row>()`.
508
+ * Plain schema objects are also accepted at the runtime boundary.
509
+ */
510
+ type ChartSchemaDefinition<T, TSchema extends ChartSchema<T, any> = ChartSchema<T, any>> = TSchema | {
511
+ build: () => DefinedChartSchema<T, TSchema>;
512
+ };
513
+ type ResolvedChartSchemaFromDefinition<TSchema> = TSchema extends DefinedChartSchema<any, infer TResolvedSchema> ? TResolvedSchema : TSchema extends {
514
+ build: () => DefinedChartSchema<any, infer TResolvedSchema>;
515
+ } ? TResolvedSchema : TSchema extends ChartSchema<any, any> ? TSchema : undefined;
505
516
  /** GroupBy IDs narrowed by explicit schema restrictions when present. */
506
517
  type RestrictedGroupByColumnIdFromSchema<T, TSchema extends ChartSchema<T, any> | undefined = undefined> = RestrictOptionsFromControlConfig<ResolvedGroupByColumnIdFromSchema<T, TSchema>, ConfigSection<TSchema, 'groupBy'>>;
507
518
  /** X-axis IDs narrowed by explicit schema restrictions when present. */
@@ -657,7 +668,7 @@ type ChartTypeConfig = SelectableControlConfig<ChartType>;
657
668
  type TimeBucketConfig = SelectableControlConfig<TimeBucket>;
658
669
  type ExtractSchemaColumns<TSchema> = TSchema extends {
659
670
  columns?: infer TColumns;
660
- } ? Extract<TColumns, Record<string, unknown>> : undefined;
671
+ } ? [Extract<TColumns, Record<string, unknown>>] extends [never] ? undefined : Extract<TColumns, Record<string, unknown>> : undefined;
661
672
  type RawSchemaColumnsFromColumns<T, TColumns extends Record<string, unknown> | undefined> = TColumns extends Record<string, unknown> ? { [TKey in keyof TColumns as TKey extends InferableFieldKey<T> ? TKey : never]: TColumns[TKey] } : undefined;
662
673
  type RawSchemaColumns<T, TSchema> = RawSchemaColumnsFromColumns<T, ExtractSchemaColumns<TSchema>>;
663
674
  type DerivedColumnIdsFromColumns<TColumns extends Record<string, unknown> | undefined> = Extract<TColumns extends Record<string, unknown> ? { [TKey in keyof TColumns]-?: TColumns[TKey] extends DerivedColumnSchema<any> ? TKey : never }[keyof TColumns] : never, string>;
@@ -750,11 +761,11 @@ type SortConfig = {
750
761
  * @property data - Array of raw data items
751
762
  * @property schema - Optional explicit schema layered on top of inference
752
763
  */
753
- type ChartSourceOptions<TId extends string = string, T = unknown, TSchema extends ChartSchema<T, any> | undefined = undefined> = {
764
+ type ChartSourceOptions<TId extends string = string, T = unknown, TSchema extends ChartSchemaDefinition<T, any> | undefined = undefined> = {
754
765
  id: TId;
755
766
  label: string;
756
767
  data: readonly T[];
757
- schema?: DefinedChartSchema<T, Exclude<TSchema, undefined>>;
768
+ schema?: TSchema;
758
769
  };
759
770
  /** Convenience alias for any multi-source input definition. */
760
771
  type AnyChartSourceOptions = {
@@ -933,14 +944,16 @@ type ChartInstance<T, TColumnId extends string = string, TChartType extends Char
933
944
  };
934
945
  /** Single-source chart instance narrowed by one explicit schema. */
935
946
  type ChartInstanceFromSchema<T, TSchema extends ChartSchema<T, any> | undefined = undefined> = ChartInstance<T, ResolvedColumnIdFromSchema<T, TSchema>, RestrictedChartTypeFromSchema<TSchema>, RestrictedXAxisColumnIdFromSchema<T, TSchema>, RestrictedGroupByColumnIdFromSchema<T, TSchema>, Extract<MetricColumnIdFromMetric<RestrictedMetricFromSchema<T, TSchema>>, ResolvedMetricColumnIdFromSchema<T, TSchema>>, RestrictedMetricFromSchema<T, TSchema>, RestrictedFilterColumnIdFromSchema<T, TSchema>, ResolvedDateColumnIdFromSchema<T, TSchema>, RestrictedTimeBucketFromSchema<TSchema>>;
947
+ /** Single-source chart instance narrowed by any supported schema input. */
948
+ type ChartInstanceFromSchemaDefinition<T, TSchema extends ChartSchemaDefinition<T, any> | undefined = undefined> = ChartInstanceFromSchema<T, ResolvedChartSchemaFromDefinition<TSchema>>;
936
949
  type SourceIdFromSource<TSource extends AnyChartSourceOptions> = TSource['id'];
937
950
  type SourceRowFromSource<TSource extends AnyChartSourceOptions> = TSource extends ChartSourceOptions<string, infer TRow, any> ? TRow : never;
938
- type SourceColumnIdFromSource<TSource extends AnyChartSourceOptions> = TSource extends ChartSourceOptions<string, infer TRow, infer TSchema> ? ResolvedColumnIdFromSchema<TRow, Extract<TSchema, ChartSchema<TRow> | undefined>> : never;
939
- type SourceXAxisColumnIdFromSource<TSource extends AnyChartSourceOptions> = TSource extends ChartSourceOptions<string, infer TRow, infer TSchema> ? RestrictedXAxisColumnIdFromSchema<TRow, Extract<TSchema, ChartSchema<TRow> | undefined>> : never;
940
- type SourceGroupByColumnIdFromSource<TSource extends AnyChartSourceOptions> = TSource extends ChartSourceOptions<string, infer TRow, infer TSchema> ? RestrictedGroupByColumnIdFromSchema<TRow, Extract<TSchema, ChartSchema<TRow> | undefined>> : never;
941
- type SourceMetricColumnIdFromSource<TSource extends AnyChartSourceOptions> = TSource extends ChartSourceOptions<string, infer TRow, infer TSchema> ? Extract<MetricColumnIdFromMetric<RestrictedMetricFromSchema<TRow, Extract<TSchema, ChartSchema<TRow> | undefined>>>, ResolvedMetricColumnIdFromSchema<TRow, Extract<TSchema, ChartSchema<TRow> | undefined>>> : never;
942
- type SourceFilterColumnIdFromSource<TSource extends AnyChartSourceOptions> = TSource extends ChartSourceOptions<string, infer TRow, infer TSchema> ? RestrictedFilterColumnIdFromSchema<TRow, Extract<TSchema, ChartSchema<TRow> | undefined>> : never;
943
- type SourceDateColumnIdFromSource<TSource extends AnyChartSourceOptions> = TSource extends ChartSourceOptions<string, infer TRow, infer TSchema> ? ResolvedDateColumnIdFromSchema<TRow, Extract<TSchema, ChartSchema<TRow> | undefined>> : never;
951
+ type SourceColumnIdFromSource<TSource extends AnyChartSourceOptions> = TSource extends ChartSourceOptions<string, infer TRow, infer TSchema> ? ResolvedColumnIdFromSchema<TRow, ResolvedChartSchemaFromDefinition<TSchema>> : never;
952
+ type SourceXAxisColumnIdFromSource<TSource extends AnyChartSourceOptions> = TSource extends ChartSourceOptions<string, infer TRow, infer TSchema> ? RestrictedXAxisColumnIdFromSchema<TRow, ResolvedChartSchemaFromDefinition<TSchema>> : never;
953
+ type SourceGroupByColumnIdFromSource<TSource extends AnyChartSourceOptions> = TSource extends ChartSourceOptions<string, infer TRow, infer TSchema> ? RestrictedGroupByColumnIdFromSchema<TRow, ResolvedChartSchemaFromDefinition<TSchema>> : never;
954
+ type SourceMetricColumnIdFromSource<TSource extends AnyChartSourceOptions> = TSource extends ChartSourceOptions<string, infer TRow, infer TSchema> ? Extract<MetricColumnIdFromMetric<RestrictedMetricFromSchema<TRow, ResolvedChartSchemaFromDefinition<TSchema>>>, ResolvedMetricColumnIdFromSchema<TRow, ResolvedChartSchemaFromDefinition<TSchema>>> : never;
955
+ type SourceFilterColumnIdFromSource<TSource extends AnyChartSourceOptions> = TSource extends ChartSourceOptions<string, infer TRow, infer TSchema> ? RestrictedFilterColumnIdFromSchema<TRow, ResolvedChartSchemaFromDefinition<TSchema>> : never;
956
+ type SourceDateColumnIdFromSource<TSource extends AnyChartSourceOptions> = TSource extends ChartSourceOptions<string, infer TRow, infer TSchema> ? ResolvedDateColumnIdFromSchema<TRow, ResolvedChartSchemaFromDefinition<TSchema>> : never;
944
957
  type SourceIdFromSources<TSources extends NonEmptyChartSourceOptions> = Extract<TSources[number]['id'], string>;
945
958
  type SourceColumnIdFromSources<TSources extends NonEmptyChartSourceOptions> = TSources[number] extends infer TSource ? TSource extends AnyChartSourceOptions ? SourceColumnIdFromSource<TSource> : never : never;
946
959
  type MultiSourceChartBranch<TSources extends NonEmptyChartSourceOptions, TSource extends AnyChartSourceOptions> = Omit<ChartInstance<SourceRowFromSource<TSource>, SourceColumnIdFromSources<TSources>>, 'activeSourceId' | 'setActiveSource' | 'sources' | 'xAxisId' | 'setXAxis' | 'availableXAxes' | 'groupById' | 'setGroupBy' | 'availableGroupBys' | 'metric' | 'setMetric' | 'availableMetrics' | 'filters' | 'toggleFilter' | 'clearFilter' | 'availableFilters' | 'dateRange' | 'referenceDateId' | 'setReferenceDateId' | 'availableDateColumns' | 'columns'> & {
@@ -985,4 +998,4 @@ type MultiSourceChartBranch<TSources extends NonEmptyChartSourceOptions, TSource
985
998
  */
986
999
  type MultiSourceChartInstance<TSources extends NonEmptyChartSourceOptions> = TSources[number] extends infer TSource ? TSource extends AnyChartSourceOptions ? MultiSourceChartBranch<TSources, TSource> : never : never;
987
1000
  //#endregion
988
- export { AggregateFunction, AggregateMetric, AvailableFilter, BooleanColumn, CategoricalChartType, CategoryColumn, ChartColumn, ChartColumnType, ChartInstance, ChartInstanceFromSchema, ChartSchema, ChartSeries, ChartSourceOptions, ChartType, ChartTypeConfig, ColumnFormat, ColumnFormatPreset, CountMetric, DateColumn, DateColumnFormat, DefinedChartSchema, DerivedBooleanColumnSchema, DerivedCategoryColumnSchema, DerivedColumnSchema, DerivedDateColumnSchema, DerivedNumberColumnSchema, ExactShape, FilterState, FiltersConfig, GroupByConfig, InferableFieldKey, Metric, MetricConfig, MultiSourceChartInstance, NonEmptyChartSourceOptions, NumberColumn, NumberColumnFormat, NumericAggregateFunction, RawColumnSchemaFor, RawColumnSchemaMap, ResolvedChartSchemaFromDefinition, ResolvedColumnIdFromSchema, ResolvedFilterColumnIdFromSchema, ResolvedGroupByColumnIdFromSchema, ResolvedMetricColumnIdFromSchema, ResolvedXAxisColumnIdFromSchema, RestrictedChartTypeFromSchema, RestrictedFilterColumnIdFromSchema, RestrictedGroupByColumnIdFromSchema, RestrictedMetricFromSchema, RestrictedTimeBucketFromSchema, RestrictedXAxisColumnIdFromSchema, SchemaColumnsValidationShape, SelectableControlConfig, SortConfig, SortDirection, TimeBucket, TimeBucketConfig, TimeSeriesChartType, TransformedDataPoint, ValidatedChartSchema, XAxisConfig };
1001
+ export { AggregateFunction, AggregateMetric, AvailableFilter, BaseColumnHint, BooleanColumn, CategoricalChartType, CategoryColumn, ChartColumn, ChartColumnType, ChartInstance, ChartInstanceFromSchema, ChartInstanceFromSchemaDefinition, ChartSchema, ChartSchemaDefinition, ChartSeries, ChartSourceOptions, ChartType, ChartTypeConfig, ColumnFormat, ColumnFormatPreset, CountMetric, DateColumn, DateColumnFormat, DefinedChartSchema, DerivedBooleanColumnSchema, DerivedCategoryColumnSchema, DerivedColumnSchema, DerivedDateColumnSchema, DerivedNumberColumnSchema, FilterState, FiltersConfig, GroupByConfig, InferableFieldKey, Metric, MetricConfig, MultiSourceChartInstance, NonEmptyChartSourceOptions, NumberColumn, NumberColumnFormat, NumericAggregateFunction, RawColumnSchemaFor, RawColumnSchemaMap, ResolvedChartSchemaFromDefinition, ResolvedColumnIdFromSchema, ResolvedFilterColumnIdFromSchema, ResolvedGroupByColumnIdFromSchema, ResolvedMetricColumnIdFromSchema, ResolvedXAxisColumnIdFromSchema, RestrictedChartTypeFromSchema, RestrictedFilterColumnIdFromSchema, RestrictedGroupByColumnIdFromSchema, RestrictedMetricFromSchema, RestrictedTimeBucketFromSchema, RestrictedXAxisColumnIdFromSchema, SelectableControlConfig, SortConfig, SortDirection, TimeBucket, TimeBucketConfig, TimeSeriesChartType, TransformedDataPoint, ValidatedChartSchema, XAxisConfig };
@@ -1,4 +1,4 @@
1
- import { ChartSchema, ChartSourceOptions, DefinedChartSchema, NonEmptyChartSourceOptions } from "./types.mjs";
1
+ import { ChartSchemaDefinition, ChartSourceOptions, NonEmptyChartSourceOptions } from "./types.mjs";
2
2
 
3
3
  //#region src/core/use-chart-options.d.ts
4
4
  /**
@@ -7,7 +7,7 @@ import { ChartSchema, ChartSourceOptions, DefinedChartSchema, NonEmptyChartSourc
7
7
  * This is the common case: one dataset, one optional schema, one optional
8
8
  * human-readable source label.
9
9
  */
10
- interface SingleSourceOptions<T, TSchema extends ChartSchema<T, any> | undefined = undefined> {
10
+ interface SingleSourceOptions<T, TSchema extends ChartSchemaDefinition<T, any> | undefined = undefined> {
11
11
  /**
12
12
  * Raw rows that chart-studio should inspect and transform.
13
13
  *
@@ -18,6 +18,9 @@ interface SingleSourceOptions<T, TSchema extends ChartSchema<T, any> | undefined
18
18
  /**
19
19
  * Optional explicit schema layered on top of inference.
20
20
  *
21
+ * Usually this is the fluent builder returned by `defineChartSchema<Row>()`.
22
+ * Plain schema objects are also accepted.
23
+ *
21
24
  * Use this when you want to:
22
25
  * - rename fields with `label`
23
26
  * - force or refine column `type`
@@ -26,7 +29,7 @@ interface SingleSourceOptions<T, TSchema extends ChartSchema<T, any> | undefined
26
29
  * - create derived columns
27
30
  * - restrict what users can select in the chart UI
28
31
  */
29
- schema?: DefinedChartSchema<T, Exclude<TSchema, undefined>>;
32
+ schema?: TSchema;
30
33
  /**
31
34
  * Human-readable source name shown by the built-in UI when relevant.
32
35
  *
@@ -59,6 +62,6 @@ interface MultiSourceOptions<TSources extends NonEmptyChartSourceOptions = NonEm
59
62
  /**
60
63
  * Options for the useChart hook.
61
64
  */
62
- type UseChartOptions<T, TSchema extends ChartSchema<T, any> | undefined = undefined> = SingleSourceOptions<T, TSchema> | MultiSourceOptions;
65
+ type UseChartOptions<T, TSchema extends ChartSchemaDefinition<T, any> | undefined = undefined> = SingleSourceOptions<T, TSchema> | MultiSourceOptions;
63
66
  //#endregion
64
67
  export { SingleSourceOptions, UseChartOptions };
@@ -1,4 +1,4 @@
1
- import { ChartInstanceFromSchema, ChartSchema, MultiSourceChartInstance, NonEmptyChartSourceOptions } from "./types.mjs";
1
+ import { ChartInstanceFromSchemaDefinition, ChartSchemaDefinition, MultiSourceChartInstance, NonEmptyChartSourceOptions } from "./types.mjs";
2
2
  import { SingleSourceOptions } from "./use-chart-options.mjs";
3
3
 
4
4
  //#region src/core/use-chart.d.ts
@@ -15,8 +15,8 @@ import { SingleSourceOptions } from "./use-chart-options.mjs";
15
15
  * Chart configuration options. Should provide either:
16
16
  * - `data`, optional `schema`, and (optionally) `sourceLabel` for a single source
17
17
  * - or `sources` array for multiple sources
18
- * Any explicit single-source or per-source schema should be created with
19
- * `defineChartSchema<Row>()(...)` so the schema shape stays exact and strongly typed.
18
+ * Any explicit single-source or per-source schema is usually authored with
19
+ * `defineChartSchema<Row>()...`. Plain schema objects are also accepted.
20
20
  *
21
21
  * @returns {ChartInstance}
22
22
  * An object representing chart configuration, state, and all derived data/operations:
@@ -45,6 +45,6 @@ declare function useChart<const TSources extends NonEmptyChartSourceOptions>(opt
45
45
  sourceLabel?: never;
46
46
  sources: TSources;
47
47
  }): MultiSourceChartInstance<TSources>;
48
- declare function useChart<T, const TSchema extends ChartSchema<T, any> | undefined = undefined>(options: SingleSourceOptions<T, TSchema>): ChartInstanceFromSchema<T, TSchema>;
48
+ declare function useChart<T, const TSchema extends ChartSchemaDefinition<T, any> | undefined = undefined>(options: SingleSourceOptions<T, TSchema>): ChartInstanceFromSchemaDefinition<T, TSchema>;
49
49
  //#endregion
50
50
  export { useChart };
@@ -1,8 +1,9 @@
1
1
  import { TIME_BUCKET_ORDER, resolveConfiguredIdSelection, resolveConfiguredValue, restrictConfiguredIdOptions, restrictConfiguredValues } from "./config-utils.mjs";
2
2
  import { CHART_TYPE_CONFIG, getAvailableChartTypes } from "./chart-capabilities.mjs";
3
3
  import { resolvePresetFilter } from "./date-range-presets.mjs";
4
+ import { buildAvailableMetrics, isSameMetric, resolveMetric, restrictAvailableMetrics } from "./metric-utils.mjs";
5
+ import { resolveChartSchemaDefinition } from "./schema-builder.mjs";
4
6
  import { inferColumnsFromData } from "./infer-columns.mjs";
5
- import { DEFAULT_METRIC, buildAvailableMetrics, isSameMetric, resolveMetric, restrictAvailableMetrics } from "./metric-utils.mjs";
6
7
  import { applyFilters, extractAvailableFilters, runPipeline } from "./pipeline.mjs";
7
8
  import { computeDateRange, filterByDateRange } from "./date-utils.mjs";
8
9
  import { DEFAULT_TIME_BUCKET } from "./use-chart-options.mjs";
@@ -21,28 +22,32 @@ function createAvailableFilterValueMap(availableFilters) {
21
22
  function useChart(options) {
22
23
  if ("sources" in options && options.sources?.length === 0) throw new Error("useChart requires at least one source");
23
24
  const sources = useMemo(() => {
24
- if ("sources" in options && options.sources) return options.sources.map((source) => ({
25
- id: source.id,
26
- label: source.label,
27
- data: source.data,
28
- columns: inferColumnsFromData(source.data, source.schema),
29
- schema: source.schema
30
- }));
25
+ if ("sources" in options && options.sources) return options.sources.map((source) => {
26
+ const schema = resolveChartSchemaDefinition(source.schema);
27
+ return {
28
+ id: source.id,
29
+ label: source.label,
30
+ data: source.data,
31
+ columns: inferColumnsFromData(source.data, schema),
32
+ schema
33
+ };
34
+ });
35
+ const schema = resolveChartSchemaDefinition(options.schema);
31
36
  return [{
32
37
  id: "default",
33
38
  label: options.sourceLabel ?? "Unnamed Source",
34
39
  data: options.data,
35
- columns: inferColumnsFromData(options.data, options.schema),
36
- schema: options.schema
40
+ columns: inferColumnsFromData(options.data, schema),
41
+ schema
37
42
  }];
38
43
  }, [options]);
39
44
  const hasMultipleSources = sources.length > 1;
40
45
  const [activeSourceIdRaw, setActiveSourceRaw] = useState(sources[0]?.id ?? "default");
41
- const [chartType, setChartTypeRaw] = useState("bar");
46
+ const [chartType, setChartTypeRaw] = useState(null);
42
47
  const [xAxisId, setXAxisRaw] = useState(null);
43
48
  const [groupById, setGroupByRaw] = useState(null);
44
- const [metric, setMetricRaw] = useState(DEFAULT_METRIC);
45
- const [timeBucket, setTimeBucketRaw] = useState(DEFAULT_TIME_BUCKET);
49
+ const [metric, setMetricRaw] = useState(null);
50
+ const [timeBucket, setTimeBucketRaw] = useState(null);
46
51
  const [filters, setFilters] = useState(() => /* @__PURE__ */ new Map());
47
52
  const [sorting, setSorting] = useState(null);
48
53
  const [referenceDateIdRaw, setReferenceDateIdRaw] = useState(null);
@@ -114,7 +119,7 @@ function useChart(options) {
114
119
  resolvedXAxisType,
115
120
  activeSource.schema
116
121
  ]);
117
- const resolvedChartType = useMemo(() => resolveConfiguredValue(chartType, availableChartTypes, activeSource.schema?.chartType?.default), [
122
+ const resolvedChartType = useMemo(() => resolveConfiguredValue(chartType, availableChartTypes, activeSource.schema?.chartType?.default, "bar"), [
118
123
  chartType,
119
124
  availableChartTypes,
120
125
  activeSource.schema
@@ -127,7 +132,7 @@ function useChart(options) {
127
132
  resolvedChartType,
128
133
  activeSource.schema
129
134
  ]);
130
- const resolvedTimeBucket = useMemo(() => resolveConfiguredValue(timeBucket, availableTimeBuckets, activeSource.schema?.timeBucket?.default), [
135
+ const resolvedTimeBucket = useMemo(() => resolveConfiguredValue(timeBucket, availableTimeBuckets, activeSource.schema?.timeBucket?.default, DEFAULT_TIME_BUCKET), [
131
136
  timeBucket,
132
137
  availableTimeBuckets,
133
138
  activeSource.schema
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { AggregateFunction, AggregateMetric, AvailableFilter, BooleanColumn, CategoricalChartType, CategoryColumn, ChartColumn, ChartColumnType, ChartInstance, ChartInstanceFromSchema, ChartSchema, ChartSeries, ChartSourceOptions, ChartType, ChartTypeConfig, ColumnFormat, ColumnFormatPreset, CountMetric, DateColumn, DateColumnFormat, DefinedChartSchema, DerivedBooleanColumnSchema, DerivedCategoryColumnSchema, DerivedColumnSchema, DerivedDateColumnSchema, DerivedNumberColumnSchema, FilterState, FiltersConfig, GroupByConfig, InferableFieldKey, Metric, MetricConfig, MultiSourceChartInstance, NumberColumn, NumberColumnFormat, NumericAggregateFunction, RawColumnSchemaFor, RawColumnSchemaMap, ResolvedColumnIdFromSchema, RestrictedChartTypeFromSchema, RestrictedFilterColumnIdFromSchema, RestrictedGroupByColumnIdFromSchema, RestrictedMetricFromSchema, RestrictedTimeBucketFromSchema, RestrictedXAxisColumnIdFromSchema, SelectableControlConfig, SortConfig, SortDirection, TimeBucket, TimeBucketConfig, TimeSeriesChartType, TransformedDataPoint, ValidatedChartSchema, XAxisConfig } from "./core/types.mjs";
1
+ import { AggregateFunction, AggregateMetric, AvailableFilter, BooleanColumn, CategoricalChartType, CategoryColumn, ChartColumn, ChartColumnType, ChartInstance, ChartInstanceFromSchema, ChartInstanceFromSchemaDefinition, ChartSchema, ChartSchemaDefinition, ChartSeries, ChartSourceOptions, ChartType, ChartTypeConfig, ColumnFormat, ColumnFormatPreset, CountMetric, DateColumn, DateColumnFormat, DefinedChartSchema, DerivedBooleanColumnSchema, DerivedCategoryColumnSchema, DerivedColumnSchema, DerivedDateColumnSchema, DerivedNumberColumnSchema, FilterState, FiltersConfig, GroupByConfig, InferableFieldKey, Metric, MetricConfig, MultiSourceChartInstance, NumberColumn, NumberColumnFormat, NumericAggregateFunction, RawColumnSchemaFor, RawColumnSchemaMap, ResolvedColumnIdFromSchema, RestrictedChartTypeFromSchema, RestrictedFilterColumnIdFromSchema, RestrictedGroupByColumnIdFromSchema, RestrictedMetricFromSchema, RestrictedTimeBucketFromSchema, RestrictedXAxisColumnIdFromSchema, SelectableControlConfig, SortConfig, SortDirection, TimeBucket, TimeBucketConfig, TimeSeriesChartType, TransformedDataPoint, ValidatedChartSchema, XAxisConfig } from "./core/types.mjs";
2
2
  import { CHART_TYPE_CONFIG, ChartAxisType, ChartTypeCapabilities } from "./core/chart-capabilities.mjs";
3
3
  import { buildColorMap, getSeriesColor } from "./core/colors.mjs";
4
4
  import { defineChartSchema } from "./core/define-chart-schema.mjs";
@@ -7,4 +7,4 @@ import { buildAvailableMetrics, getMetricLabel } from "./core/metric-utils.mjs";
7
7
  import { PipelineInput, PipelineOutput, applyFilters, extractAvailableFilters, runPipeline } from "./core/pipeline.mjs";
8
8
  import { UseChartOptions } from "./core/use-chart-options.mjs";
9
9
  import { useChart } from "./core/use-chart.mjs";
10
- export { type AggregateFunction, type AggregateMetric, type AvailableFilter, type BooleanColumn, CHART_TYPE_CONFIG, type CategoricalChartType, type CategoryColumn, type ChartAxisType, type ChartColumn, type ChartColumnType, type ChartInstance, type ChartInstanceFromSchema, type ChartSchema, type ChartSeries, type ChartSourceOptions, type ChartType, type ChartTypeCapabilities, type ChartTypeConfig, type ColumnFormat, type ColumnFormatPreset, type CountMetric, type DateColumn, type DateColumnFormat, type DefinedChartSchema, type DerivedBooleanColumnSchema, type DerivedCategoryColumnSchema, type DerivedColumnSchema, type DerivedDateColumnSchema, type DerivedNumberColumnSchema, type FilterState, type FiltersConfig, type GroupByConfig, type InferableFieldKey, type Metric, type MetricConfig, type MultiSourceChartInstance, type NumberColumn, type NumberColumnFormat, type NumericAggregateFunction, type PipelineInput, type PipelineOutput, type RawColumnSchemaFor, type RawColumnSchemaMap, type ResolvedColumnIdFromSchema, type RestrictedChartTypeFromSchema, type RestrictedFilterColumnIdFromSchema, type RestrictedGroupByColumnIdFromSchema, type RestrictedMetricFromSchema, type RestrictedTimeBucketFromSchema, type RestrictedXAxisColumnIdFromSchema, type SelectableControlConfig, type SortConfig, type SortDirection, type TimeBucket, type TimeBucketConfig, type TimeSeriesChartType, type TransformedDataPoint, type UseChartOptions, type ValidatedChartSchema, type XAxisConfig, applyFilters, buildAvailableMetrics, buildColorMap, defineChartSchema, extractAvailableFilters, getMetricLabel, getSeriesColor, inferColumnsFromData, runPipeline, useChart };
10
+ export { type AggregateFunction, type AggregateMetric, type AvailableFilter, type BooleanColumn, CHART_TYPE_CONFIG, type CategoricalChartType, type CategoryColumn, type ChartAxisType, type ChartColumn, type ChartColumnType, type ChartInstance, type ChartInstanceFromSchema, type ChartInstanceFromSchemaDefinition, type ChartSchema, type ChartSchemaDefinition, type ChartSeries, type ChartSourceOptions, type ChartType, type ChartTypeCapabilities, type ChartTypeConfig, type ColumnFormat, type ColumnFormatPreset, type CountMetric, type DateColumn, type DateColumnFormat, type DefinedChartSchema, type DerivedBooleanColumnSchema, type DerivedCategoryColumnSchema, type DerivedColumnSchema, type DerivedDateColumnSchema, type DerivedNumberColumnSchema, type FilterState, type FiltersConfig, type GroupByConfig, type InferableFieldKey, type Metric, type MetricConfig, type MultiSourceChartInstance, type NumberColumn, type NumberColumnFormat, type NumericAggregateFunction, type PipelineInput, type PipelineOutput, type RawColumnSchemaFor, type RawColumnSchemaMap, type ResolvedColumnIdFromSchema, type RestrictedChartTypeFromSchema, type RestrictedFilterColumnIdFromSchema, type RestrictedGroupByColumnIdFromSchema, type RestrictedMetricFromSchema, type RestrictedTimeBucketFromSchema, type RestrictedXAxisColumnIdFromSchema, type SelectableControlConfig, type SortConfig, type SortDirection, type TimeBucket, type TimeBucketConfig, type TimeSeriesChartType, type TransformedDataPoint, type UseChartOptions, type ValidatedChartSchema, type XAxisConfig, applyFilters, buildAvailableMetrics, buildColorMap, defineChartSchema, extractAvailableFilters, getMetricLabel, getSeriesColor, inferColumnsFromData, runPipeline, useChart };
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import { CHART_TYPE_CONFIG } from "./core/chart-capabilities.mjs";
2
2
  import { buildColorMap, getSeriesColor } from "./core/colors.mjs";
3
+ import { buildAvailableMetrics, getMetricLabel } from "./core/metric-utils.mjs";
3
4
  import { defineChartSchema } from "./core/define-chart-schema.mjs";
4
5
  import { inferColumnsFromData } from "./core/infer-columns.mjs";
5
- import { buildAvailableMetrics, getMetricLabel } from "./core/metric-utils.mjs";
6
6
  import { applyFilters, extractAvailableFilters, runPipeline } from "./core/pipeline.mjs";
7
7
  import { useChart } from "./core/use-chart.mjs";
8
8
  export { CHART_TYPE_CONFIG, applyFilters, buildAvailableMetrics, buildColorMap, defineChartSchema, extractAvailableFilters, getMetricLabel, getSeriesColor, inferColumnsFromData, runPipeline, useChart };
@@ -15,11 +15,15 @@ import * as react_jsx_runtime0 from "react/jsx-runtime";
15
15
  * @property showDataLabels - Opt into cartesian/pie value labels using the shared formatting rules.
16
16
  */
17
17
  type ChartCanvasProps = {
18
- height?: number;
19
- className?: string;
18
+ /** Chart height in pixels (default: 300) */height?: number; /** Additional CSS classes */
19
+ className?: string; /** Will show labels using the shared formatting rules. (default: false) */
20
20
  showDataLabels?: boolean;
21
21
  };
22
- /** Renders the appropriate recharts chart based on the chart instance state. */
22
+ /** Renders the appropriate recharts chart based on the chart instance state.
23
+ * @param height - Chart height in pixels (default: 300)
24
+ * @param className - Additional CSS classes
25
+ * @param showDataLabels - Opt into cartesian/pie value labels using the shared formatting rules.
26
+ */
23
27
  declare function ChartCanvas({
24
28
  height,
25
29
  className,
@@ -2,6 +2,7 @@ import { getSeriesColor } from "../core/colors.mjs";
2
2
  import { createNumericRange, formatChartValue, formatTimeBucketLabel, shouldAllowDecimalTicks } from "../core/formatting.mjs";
3
3
  import { useChartContext } from "./chart-context.mjs";
4
4
  import { selectVisibleXAxisTicks } from "./chart-axis-ticks.mjs";
5
+ import { getPercentStackedDisplayValue } from "./percent-stacked.mjs";
5
6
  import { useEffect, useRef, useState } from "react";
6
7
  import { jsx, jsxs } from "react/jsx-runtime";
7
8
  import { Area, AreaChart, Bar, BarChart, CartesianGrid, LabelList, Legend, Line, LineChart, Pie, PieChart, Tooltip, XAxis, YAxis } from "recharts";
@@ -213,7 +214,11 @@ function useCssBarRadius() {
213
214
  }, []);
214
215
  return radiusPx;
215
216
  }
216
- /** Renders the appropriate recharts chart based on the chart instance state. */
217
+ /** Renders the appropriate recharts chart based on the chart instance state.
218
+ * @param height - Chart height in pixels (default: 300)
219
+ * @param className - Additional CSS classes
220
+ * @param showDataLabels - Opt into cartesian/pie value labels using the shared formatting rules.
221
+ */
217
222
  function ChartCanvas({ height = 300, className, showDataLabels = false }) {
218
223
  const chart = useChartContext();
219
224
  const { chartType, transformedData, series, connectNulls } = chart;
@@ -335,13 +340,19 @@ function ChartCanvas({ height = 300, className, showDataLabels = false }) {
335
340
  */
336
341
  const PERCENT_STACKED_COLUMN = {
337
342
  type: "number",
338
- format: "percent",
339
- formatter: void 0
343
+ format: "percent"
340
344
  };
341
345
  const PERCENT_STACKED_RANGE = {
342
346
  min: 0,
343
347
  max: 1
344
348
  };
349
+ function createStackedTooltipItemSorter(series) {
350
+ const order = new Map(series.map((s, index) => [s.dataKey, index]));
351
+ return (item) => {
352
+ const dataKey = typeof item.dataKey === "string" ? item.dataKey : typeof item.name === "string" ? item.name : "";
353
+ return -(order.get(dataKey) ?? -1);
354
+ };
355
+ }
345
356
  /**
346
357
  * Remove data points where every series value is null.
347
358
  *
@@ -360,7 +371,7 @@ function filterAllNullPoints(data, series) {
360
371
  * Owns the grid, axes, tooltip, and legend — the only things that change
361
372
  * per chart type are the root component and the series element.
362
373
  */
363
- function CartesianChartShell({ data, series, width, height, valueColumn, valueRange, allowDecimalTicks, xColumn, timeBucket, showDataLabels, Chart, renderSeries }) {
374
+ function CartesianChartShell({ data, series, width, height, valueColumn, valueRange, allowDecimalTicks, xColumn, timeBucket, showDataLabels, Chart, renderSeries, tooltipItemSorter }) {
364
375
  const yAxisWidth = estimateYAxisWidth(valueRange, valueColumn);
365
376
  const xAxisTickValues = selectVisibleXAxisTicks({
366
377
  values: data.map(getXAxisTickValue),
@@ -402,6 +413,7 @@ function CartesianChartShell({ data, series, width, height, valueColumn, valueRa
402
413
  }) : String(value)
403
414
  }),
404
415
  /* @__PURE__ */ jsx(Tooltip, {
416
+ itemSorter: tooltipItemSorter,
405
417
  formatter: (value) => typeof value === "number" ? formatChartValue(value, {
406
418
  column: valueColumn,
407
419
  surface: "tooltip",
@@ -443,6 +455,7 @@ function BarChartRenderer(props) {
443
455
  return /* @__PURE__ */ jsx(CartesianChartShell, {
444
456
  ...props,
445
457
  Chart: BarChart,
458
+ tooltipItemSorter: isStacked ? createStackedTooltipItemSorter(series) : void 0,
446
459
  renderSeries: (s) => {
447
460
  const isTop = !isStacked || s.dataKey === topSeriesKey;
448
461
  return /* @__PURE__ */ jsx(Bar, {
@@ -511,6 +524,8 @@ function AreaChartRenderer(props) {
511
524
  }
512
525
  function PercentAreaChartRenderer(props) {
513
526
  const { series, data, xColumn, timeBucket, showDataLabels, connectNulls, width, height } = props;
527
+ const seriesKeys = series.map((s) => s.dataKey);
528
+ const tooltipItemSorter = createStackedTooltipItemSorter(series);
514
529
  const stackableData = filterAllNullPoints(data, series);
515
530
  const yAxisWidth = estimateYAxisWidth(PERCENT_STACKED_RANGE, PERCENT_STACKED_COLUMN);
516
531
  const xAxisTickValues = selectVisibleXAxisTicks({
@@ -553,11 +568,20 @@ function PercentAreaChartRenderer(props) {
553
568
  width: yAxisWidth
554
569
  }),
555
570
  /* @__PURE__ */ jsx(Tooltip, {
556
- formatter: (value) => typeof value === "number" ? formatChartValue(value, {
557
- column: PERCENT_STACKED_COLUMN,
558
- surface: "tooltip",
559
- numericRange: PERCENT_STACKED_RANGE
560
- }) : value,
571
+ itemSorter: tooltipItemSorter,
572
+ formatter: (_value, _name, entry) => {
573
+ const proportion = getPercentStackedDisplayValue(entry, String(entry.dataKey ?? ""), seriesKeys);
574
+ if (proportion != null) return formatChartValue(proportion, {
575
+ column: PERCENT_STACKED_COLUMN,
576
+ surface: "tooltip",
577
+ numericRange: PERCENT_STACKED_RANGE
578
+ });
579
+ return typeof _value === "number" ? formatChartValue(_value, {
580
+ column: PERCENT_STACKED_COLUMN,
581
+ surface: "tooltip",
582
+ numericRange: PERCENT_STACKED_RANGE
583
+ }) : _value;
584
+ },
561
585
  labelFormatter: (label, payload) => formatTooltipLabel(label, payload, xColumn, timeBucket)
562
586
  }),
563
587
  series.length > 1 && /* @__PURE__ */ jsx(Legend, {}),
@@ -573,6 +597,7 @@ function PercentAreaChartRenderer(props) {
573
597
  children: showDataLabels && /* @__PURE__ */ jsx(LabelList, {
574
598
  position: "top",
575
599
  offset: 8,
600
+ valueAccessor: (entry) => getPercentStackedDisplayValue(entry, s.dataKey, seriesKeys) ?? 0,
576
601
  formatter: (value) => formatDataLabel(value, PERCENT_STACKED_COLUMN, PERCENT_STACKED_RANGE)
577
602
  })
578
603
  }, s.dataKey))
@@ -608,14 +633,9 @@ function PercentBarChartRenderer(props) {
608
633
  const { series, data, xColumn, timeBucket, showDataLabels, width, height } = props;
609
634
  const barRadius = useCssBarRadius();
610
635
  const topSeriesKey = series[series.length - 1]?.dataKey;
611
- const yAxisWidth = estimateYAxisWidth({
612
- min: 0,
613
- max: 100
614
- }, {
615
- type: "number",
616
- format: void 0,
617
- formatter: void 0
618
- });
636
+ const seriesKeys = series.map((s) => s.dataKey);
637
+ const tooltipItemSorter = createStackedTooltipItemSorter(series);
638
+ const yAxisWidth = estimateYAxisWidth(PERCENT_STACKED_RANGE, PERCENT_STACKED_COLUMN);
619
639
  const xAxisTickValues = selectVisibleXAxisTicks({
620
640
  values: data.map(getXAxisTickValue),
621
641
  labels: data.map((point) => formatXAxisValue(getXAxisTickValue(point), xColumn, timeBucket, "axis")),
@@ -648,13 +668,27 @@ function PercentBarChartRenderer(props) {
648
668
  tickLine: false,
649
669
  axisLine: false,
650
670
  tickMargin: 4,
651
- tickFormatter: (value) => typeof value === "number" ? `${Math.round(value * 100)}%` : String(value),
671
+ tickFormatter: (value) => typeof value === "number" ? formatChartValue(value, {
672
+ column: PERCENT_STACKED_COLUMN,
673
+ surface: "axis",
674
+ numericRange: PERCENT_STACKED_RANGE
675
+ }) : String(value),
652
676
  width: yAxisWidth
653
677
  }),
654
678
  /* @__PURE__ */ jsx(Tooltip, {
655
- formatter: (value, name) => {
656
- if (typeof value === "number") return [`${(value * 100).toFixed(1)}%`, name];
657
- return [value, name];
679
+ itemSorter: tooltipItemSorter,
680
+ formatter: (_value, _name, entry) => {
681
+ const proportion = getPercentStackedDisplayValue(entry, String(entry.dataKey ?? ""), seriesKeys);
682
+ if (proportion != null) return formatChartValue(proportion, {
683
+ column: PERCENT_STACKED_COLUMN,
684
+ surface: "tooltip",
685
+ numericRange: PERCENT_STACKED_RANGE
686
+ });
687
+ return typeof _value === "number" ? formatChartValue(_value, {
688
+ column: PERCENT_STACKED_COLUMN,
689
+ surface: "tooltip",
690
+ numericRange: PERCENT_STACKED_RANGE
691
+ }) : _value;
658
692
  },
659
693
  labelFormatter: (label, payload) => formatTooltipLabel(label, payload, xColumn, timeBucket)
660
694
  }),
@@ -672,7 +706,13 @@ function PercentBarChartRenderer(props) {
672
706
  0,
673
707
  0
674
708
  ] : 0,
675
- stackId: "percent"
709
+ stackId: "percent",
710
+ children: showDataLabels && /* @__PURE__ */ jsx(LabelList, {
711
+ position: "top",
712
+ offset: 8,
713
+ valueAccessor: (entry) => getPercentStackedDisplayValue(entry, s.dataKey, seriesKeys) ?? 0,
714
+ formatter: (value) => formatDataLabel(value, PERCENT_STACKED_COLUMN, PERCENT_STACKED_RANGE)
715
+ })
676
716
  }, s.dataKey);
677
717
  })
678
718
  ]
@@ -1,4 +1,4 @@
1
- import { ChartColumn, ChartInstance, ChartInstanceFromSchema, ChartSchema, Metric, ResolvedChartSchemaFromDefinition } from "../core/types.mjs";
1
+ import { ChartColumn, ChartInstance, ChartInstanceFromSchemaDefinition, ChartSchemaDefinition, Metric } from "../core/types.mjs";
2
2
  import { ReactElement, ReactNode } from "react";
3
3
 
4
4
  //#region src/ui/chart-context.d.ts
@@ -70,10 +70,14 @@ declare function useChartContext(): ChartContextChart;
70
70
  * React cannot infer provider generics through arbitrary subtrees, so callers
71
71
  * provide the row type (and optional schema type) explicitly.
72
72
  */
73
- declare function useTypedChartContext<T, const TSchema extends ChartSchema<T, any> | undefined = undefined>(): ChartInstanceFromSchema<T, ResolvedChartSchemaFromDefinition<TSchema>>;
73
+ declare function useTypedChartContext<T, const TSchema extends ChartSchemaDefinition<T, any> | undefined = undefined>(): ChartInstanceFromSchemaDefinition<T, TSchema>;
74
74
  /**
75
75
  * Root provider component. Wraps children with the chart instance context.
76
76
  *
77
+ * @param chart - The chart instance to share
78
+ * @param children - The children to render
79
+ * @param className - Additional CSS classes for the chart container
80
+ *
77
81
  * @example
78
82
  * ```tsx
79
83
  * <Chart chart={chart}>
@@ -87,8 +91,8 @@ declare function Chart({
87
91
  children,
88
92
  className
89
93
  }: {
90
- chart: AnyChartInstance;
91
- children: ReactNode;
94
+ /** The chart instance to share. Create it with `useChart()` first.*/chart: AnyChartInstance; /** The children to render. Can be any UI primitives from the UI package. */
95
+ children: ReactNode; /** Additional CSS classes for the chart container. */
92
96
  className?: string;
93
97
  }): ReactElement;
94
98
  //#endregion
@@ -5,12 +5,16 @@ import * as react_jsx_runtime0 from "react/jsx-runtime";
5
5
  * Debug panel — shows raw data, transformed data, series, and current state.
6
6
  * Drop `<ChartDebug />` inside a `<Chart>` to inspect what's happening.
7
7
  */
8
- /** Debug panel that renders chart internals as formatted JSON. */
8
+ /** Debug panel that renders chart internals as formatted JSON.
9
+ *
10
+ * @param className - Additional CSS classes for the debug panel.
11
+ * @param defaultOpen - Whether the debug panel should be open by default. (default: false)
12
+ */
9
13
  declare function ChartDebug({
10
14
  className,
11
15
  defaultOpen
12
16
  }: {
13
- className?: string;
17
+ /** Additional CSS classes for the debug panel. */className?: string; /** Whether the debug panel should be open by default. (default: false) */
14
18
  defaultOpen?: boolean;
15
19
  }): react_jsx_runtime0.JSX.Element;
16
20
  //#endregion
@@ -112,7 +112,11 @@ function TabButton({ tab, isActive, onClick }) {
112
112
  children: tab.label
113
113
  });
114
114
  }
115
- /** Debug panel that renders chart internals as formatted JSON. */
115
+ /** Debug panel that renders chart internals as formatted JSON.
116
+ *
117
+ * @param className - Additional CSS classes for the debug panel.
118
+ * @param defaultOpen - Whether the debug panel should be open by default. (default: false)
119
+ */
116
120
  function ChartDebug({ className, defaultOpen = false }) {
117
121
  const chart = useChartContext();
118
122
  const [isOpen, setIsOpen] = useState(defaultOpen);
@@ -10,8 +10,8 @@ import * as react_jsx_runtime0 from "react/jsx-runtime";
10
10
  * @property hidden - Control IDs to completely hide (not in toolbar, not in overflow)
11
11
  */
12
12
  type ChartToolbarProps = {
13
- className?: string;
14
- pinned?: readonly ControlId[];
13
+ /** Additional CSS classes for the toolbar container. */className?: string; /** Control IDs to always show in the toolbar row (outside the overflow menu) */
14
+ pinned?: readonly ControlId[]; /** Control IDs to completely hide (not in toolbar, not in overflow) */
15
15
  hidden?: readonly ControlId[];
16
16
  };
17
17
  /**
@@ -19,6 +19,10 @@ type ChartToolbarProps = {
19
19
  *
20
20
  * Controls are rendered in registry order. Each sub-component still
21
21
  * auto-hides when not relevant (e.g. time bucket only shows for date X-axis).
22
+ *
23
+ * @param className - Additional CSS classes for the toolbar container
24
+ * @param pinned - Control IDs to always show in the toolbar row (outside the overflow menu)
25
+ * @param hidden - Control IDs to completely hide (not in toolbar, not in overflow)
22
26
  */
23
27
  declare function ChartToolbar({
24
28
  className,
@@ -32,6 +32,10 @@ const DEFAULT_PINNED_CONTROLS = ["dateRange"];
32
32
  *
33
33
  * Controls are rendered in registry order. Each sub-component still
34
34
  * auto-hides when not relevant (e.g. time bucket only shows for date X-axis).
35
+ *
36
+ * @param className - Additional CSS classes for the toolbar container
37
+ * @param pinned - Control IDs to always show in the toolbar row (outside the overflow menu)
38
+ * @param hidden - Control IDs to completely hide (not in toolbar, not in overflow)
35
39
  */
36
40
  function ChartToolbar({ className, pinned = DEFAULT_PINNED_CONTROLS, hidden = [] }) {
37
41
  const pinnedSet = useMemo(() => new Set(pinned), [pinned]);
@@ -0,0 +1,36 @@
1
+ //#region src/ui/percent-stacked.ts
2
+ /**
3
+ * Recharts stacked graphical entries expose [lower, upper] bounds after
4
+ * stackOffset="expand". The visible segment size is upper - lower.
5
+ */
6
+ function getPercentStackedProportion(entry) {
7
+ const value = entry.value;
8
+ if (Array.isArray(value) && typeof value[0] === "number" && typeof value[1] === "number") return value[1] - value[0];
9
+ return null;
10
+ }
11
+ /**
12
+ * Recharts tooltip payload entries are rebuilt from the raw transformed row,
13
+ * so value stays as the raw metric and payload contains the full bucket.
14
+ */
15
+ function getPercentStackedProportionFromPayload(payload, dataKey, seriesKeys) {
16
+ if (!payload || typeof payload !== "object") return null;
17
+ const point = payload;
18
+ const rawValue = point[dataKey];
19
+ if (typeof rawValue !== "number") return null;
20
+ let total = 0;
21
+ for (const seriesKey of seriesKeys) {
22
+ const seriesValue = point[seriesKey];
23
+ if (typeof seriesValue === "number") total += seriesValue;
24
+ }
25
+ if (total <= 0) return null;
26
+ return rawValue / total;
27
+ }
28
+ /**
29
+ * Prefer the raw-row calculation because it works for both tooltip payloads
30
+ * and label entries, then fall back to stacked bounds when present.
31
+ */
32
+ function getPercentStackedDisplayValue(entry, dataKey, seriesKeys) {
33
+ return getPercentStackedProportionFromPayload(entry.payload, dataKey, seriesKeys) ?? getPercentStackedProportion(entry);
34
+ }
35
+ //#endregion
36
+ export { getPercentStackedDisplayValue };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matthieumordrel/chart-studio",
3
- "version": "0.2.4",
3
+ "version": "0.3.0",
4
4
  "description": "Headless, composable charting for React with an optional batteries-included UI layer.",
5
5
  "type": "module",
6
6
  "sideEffects": false,