@matthieumordrel/chart-studio 0.3.0 → 0.5.2

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.
Files changed (94) hide show
  1. package/README.md +10 -378
  2. package/dist/_internal.d.mts +9 -0
  3. package/dist/_internal.mjs +9 -0
  4. package/dist/core/chart-builder-controls.mjs +141 -0
  5. package/dist/core/chart-capabilities.d.mts +5 -0
  6. package/dist/core/chart-capabilities.mjs +9 -0
  7. package/dist/core/config-utils.mjs +2 -1
  8. package/dist/core/dashboard.types.d.mts +220 -0
  9. package/dist/core/data-label-defaults.d.mts +92 -0
  10. package/dist/core/data-label-defaults.mjs +78 -0
  11. package/dist/core/data-model.types.d.mts +196 -0
  12. package/dist/core/dataset-builder.types.d.mts +51 -0
  13. package/dist/core/dataset-chart-metadata.d.mts +8 -0
  14. package/dist/core/dataset-chart-metadata.mjs +4 -0
  15. package/dist/core/date-range-presets.d.mts +43 -1
  16. package/dist/core/date-range-presets.mjs +2 -2
  17. package/dist/core/date-utils.d.mts +26 -0
  18. package/dist/core/define-dashboard.d.mts +8 -0
  19. package/dist/core/define-dashboard.mjs +156 -0
  20. package/dist/core/define-data-model.d.mts +11 -0
  21. package/dist/core/define-data-model.mjs +327 -0
  22. package/dist/core/define-dataset.d.mts +13 -0
  23. package/dist/core/define-dataset.mjs +111 -0
  24. package/dist/core/formatting.d.mts +49 -0
  25. package/dist/core/formatting.mjs +32 -10
  26. package/dist/core/index.d.mts +19 -0
  27. package/dist/core/infer-columns.mjs +28 -2
  28. package/dist/core/materialized-view.mjs +580 -0
  29. package/dist/core/materialized-view.types.d.mts +223 -0
  30. package/dist/core/metric-utils.d.mts +18 -2
  31. package/dist/core/metric-utils.mjs +1 -1
  32. package/dist/core/model-chart.mjs +242 -0
  33. package/dist/core/model-chart.types.d.mts +199 -0
  34. package/dist/core/model-inference.mjs +169 -0
  35. package/dist/core/model-inference.types.d.mts +71 -0
  36. package/dist/core/pipeline.mjs +32 -1
  37. package/dist/core/schema-builder.mjs +28 -158
  38. package/dist/core/schema-builder.types.d.mts +2 -49
  39. package/dist/core/types.d.mts +61 -10
  40. package/dist/core/use-chart-options.d.mts +35 -8
  41. package/dist/core/use-chart-resolvers.mjs +13 -3
  42. package/dist/core/use-chart.d.mts +16 -12
  43. package/dist/core/use-chart.mjs +137 -35
  44. package/dist/core/use-dashboard.d.mts +190 -0
  45. package/dist/core/use-dashboard.mjs +551 -0
  46. package/dist/index.d.mts +14 -4
  47. package/dist/index.mjs +8 -2
  48. package/package.json +10 -41
  49. package/LICENSE +0 -21
  50. package/dist/core/define-chart-schema.d.mts +0 -38
  51. package/dist/core/define-chart-schema.mjs +0 -39
  52. package/dist/ui/chart-axis-ticks.mjs +0 -65
  53. package/dist/ui/chart-canvas.d.mts +0 -33
  54. package/dist/ui/chart-canvas.mjs +0 -779
  55. package/dist/ui/chart-context.d.mts +0 -99
  56. package/dist/ui/chart-context.mjs +0 -115
  57. package/dist/ui/chart-date-range-badge.d.mts +0 -20
  58. package/dist/ui/chart-date-range-badge.mjs +0 -49
  59. package/dist/ui/chart-date-range-panel.d.mts +0 -18
  60. package/dist/ui/chart-date-range-panel.mjs +0 -126
  61. package/dist/ui/chart-date-range.d.mts +0 -20
  62. package/dist/ui/chart-date-range.mjs +0 -67
  63. package/dist/ui/chart-debug.d.mts +0 -21
  64. package/dist/ui/chart-debug.mjs +0 -173
  65. package/dist/ui/chart-dropdown.mjs +0 -92
  66. package/dist/ui/chart-filters-panel.d.mts +0 -26
  67. package/dist/ui/chart-filters-panel.mjs +0 -132
  68. package/dist/ui/chart-filters.d.mts +0 -18
  69. package/dist/ui/chart-filters.mjs +0 -48
  70. package/dist/ui/chart-group-by-selector.d.mts +0 -16
  71. package/dist/ui/chart-group-by-selector.mjs +0 -32
  72. package/dist/ui/chart-metric-panel.d.mts +0 -25
  73. package/dist/ui/chart-metric-panel.mjs +0 -172
  74. package/dist/ui/chart-metric-selector.d.mts +0 -16
  75. package/dist/ui/chart-metric-selector.mjs +0 -50
  76. package/dist/ui/chart-select.mjs +0 -61
  77. package/dist/ui/chart-source-switcher.d.mts +0 -24
  78. package/dist/ui/chart-source-switcher.mjs +0 -56
  79. package/dist/ui/chart-time-bucket-selector.d.mts +0 -17
  80. package/dist/ui/chart-time-bucket-selector.mjs +0 -37
  81. package/dist/ui/chart-toolbar-overflow.d.mts +0 -28
  82. package/dist/ui/chart-toolbar-overflow.mjs +0 -231
  83. package/dist/ui/chart-toolbar.d.mts +0 -33
  84. package/dist/ui/chart-toolbar.mjs +0 -60
  85. package/dist/ui/chart-type-selector.d.mts +0 -19
  86. package/dist/ui/chart-type-selector.mjs +0 -168
  87. package/dist/ui/chart-x-axis-selector.d.mts +0 -16
  88. package/dist/ui/chart-x-axis-selector.mjs +0 -28
  89. package/dist/ui/index.d.mts +0 -19
  90. package/dist/ui/index.mjs +0 -18
  91. package/dist/ui/percent-stacked.mjs +0 -36
  92. package/dist/ui/theme.css +0 -67
  93. package/dist/ui/toolbar-types.d.mts +0 -7
  94. package/dist/ui/toolbar-types.mjs +0 -83
@@ -0,0 +1,13 @@
1
+ import { DatasetBuilder, DatasetDefinition, ResolvedDatasetFromDefinition } from "./dataset-builder.types.mjs";
2
+
3
+ //#region src/core/define-dataset.d.ts
4
+ declare function validateDatasetData<TDataset extends DatasetDefinition<any, any, any>>(dataset: TDataset, rows: Parameters<ResolvedDatasetFromDefinition<TDataset>['validateData']>[0], datasetLabel?: string): void;
5
+ /**
6
+ * Define one reusable dataset contract for columns, derived fields, and row identity.
7
+ *
8
+ * Dataset-owned `.columns(...)` is the canonical reusable meaning.
9
+ * Use `.chart(...)` to derive chart definitions from that shared contract.
10
+ */
11
+ declare function defineDataset<TRow>(): DatasetBuilder<TRow, undefined, undefined>;
12
+ //#endregion
13
+ export { defineDataset, validateDatasetData };
@@ -0,0 +1,111 @@
1
+ import { COLUMN_HELPER, assertColumnEntries, buildColumnsMap, createDatasetChartBuilder } from "./schema-builder.mjs";
2
+ //#region src/core/define-dataset.ts
3
+ function normalizeDatasetKey(key) {
4
+ return Array.isArray(key) ? key : [key];
5
+ }
6
+ function formatKeyValue(value) {
7
+ return value.length === 1 ? String(value[0]) : `[${value.map((part) => String(part)).join(", ")}]`;
8
+ }
9
+ function buildKeyFingerprint(parts) {
10
+ return JSON.stringify(parts.map((part) => {
11
+ if (part instanceof Date) return {
12
+ type: "date",
13
+ value: part.toISOString()
14
+ };
15
+ return {
16
+ type: typeof part,
17
+ value: part
18
+ };
19
+ }));
20
+ }
21
+ function validateDatasetRows(key, rows, datasetLabel) {
22
+ if (!key || key.length === 0) return;
23
+ const seen = /* @__PURE__ */ new Map();
24
+ rows.forEach((row, index) => {
25
+ const parts = key.map((keyId) => row[keyId]);
26
+ const missingKeyId = parts.findIndex((part) => part == null);
27
+ if (missingKeyId >= 0) throw new Error(`Dataset "${datasetLabel}" key "${key.join(", ")}" is missing a value at row ${index} for "${key[missingKeyId]}".`);
28
+ const fingerprint = buildKeyFingerprint(parts);
29
+ if (seen.get(fingerprint)) throw new Error(`Dataset "${datasetLabel}" key "${key.join(", ")}" must be unique. Duplicate value: ${formatKeyValue(parts)}.`);
30
+ seen.set(fingerprint, parts);
31
+ });
32
+ }
33
+ function resolveDatasetDefinition(dataset) {
34
+ return dataset.build();
35
+ }
36
+ function validateDatasetData(dataset, rows, datasetLabel = "dataset") {
37
+ validateDatasetRows(resolveDatasetDefinition(dataset).key, rows, datasetLabel);
38
+ }
39
+ function createDefinedDataset(state) {
40
+ let cachedDataset;
41
+ const build = () => {
42
+ if (cachedDataset) return cachedDataset;
43
+ const definedDataset = {
44
+ ...state.key !== void 0 ? { key: state.key } : {},
45
+ ...state.columns !== void 0 ? { columns: state.columns } : {},
46
+ chart(id) {
47
+ return createDatasetChartBuilder({ ...state.columns !== void 0 ? { columns: state.columns } : {} }, {
48
+ dataset: definedDataset,
49
+ chartId: id
50
+ });
51
+ },
52
+ validateData(data) {
53
+ validateDatasetRows(state.key, data, "dataset");
54
+ },
55
+ build() {
56
+ return definedDataset;
57
+ },
58
+ __datasetBrand: "dataset-definition"
59
+ };
60
+ cachedDataset = definedDataset;
61
+ return definedDataset;
62
+ };
63
+ return build();
64
+ }
65
+ function createDatasetBuilder(state = {}) {
66
+ let cachedDataset;
67
+ const getOrBuildDataset = () => {
68
+ if (cachedDataset) return cachedDataset;
69
+ cachedDataset = createDefinedDataset(state);
70
+ return cachedDataset;
71
+ };
72
+ return {
73
+ key(keyOrKeys) {
74
+ return createDatasetBuilder({
75
+ ...state,
76
+ key: normalizeDatasetKey(keyOrKeys)
77
+ });
78
+ },
79
+ columns(defineColumns) {
80
+ const entries = defineColumns(COLUMN_HELPER);
81
+ assertColumnEntries(entries, "defineDataset");
82
+ return createDatasetBuilder({
83
+ ...state,
84
+ columns: buildColumnsMap(entries)
85
+ });
86
+ },
87
+ chart(id) {
88
+ return createDatasetChartBuilder({ ...state.columns !== void 0 ? { columns: state.columns } : {} }, {
89
+ dataset: getOrBuildDataset(),
90
+ chartId: id
91
+ });
92
+ },
93
+ validateData(data) {
94
+ validateDatasetRows(state.key, data, "dataset");
95
+ },
96
+ build() {
97
+ return getOrBuildDataset();
98
+ }
99
+ };
100
+ }
101
+ /**
102
+ * Define one reusable dataset contract for columns, derived fields, and row identity.
103
+ *
104
+ * Dataset-owned `.columns(...)` is the canonical reusable meaning.
105
+ * Use `.chart(...)` to derive chart definitions from that shared contract.
106
+ */
107
+ function defineDataset() {
108
+ return createDatasetBuilder();
109
+ }
110
+ //#endregion
111
+ export { defineDataset, resolveDatasetDefinition, validateDatasetData };
@@ -0,0 +1,49 @@
1
+ import { ChartColumn, ColumnFormat, TimeBucket } from "./types.mjs";
2
+
3
+ //#region src/core/formatting.d.ts
4
+ /** Formatting surfaces exposed by the chart UI. */
5
+ type ChartValueSurface = 'axis' | 'tooltip' | 'data-label' | 'table-cell' | 'raw';
6
+ /** Numeric extent used to choose sensible default precision. */
7
+ type NumericRange = {
8
+ min: number;
9
+ max: number;
10
+ };
11
+ type FormatColumnLike<T> = {
12
+ type: ChartColumn<T>['type'];
13
+ format?: ColumnFormat;
14
+ formatter?: ChartColumn<T>['formatter'];
15
+ trueLabel?: string;
16
+ falseLabel?: string;
17
+ };
18
+ type FormatValueOptions<T> = {
19
+ column: FormatColumnLike<T>;
20
+ surface: ChartValueSurface;
21
+ timeBucket?: TimeBucket;
22
+ numericRange?: NumericRange | null;
23
+ locale?: string;
24
+ item?: T;
25
+ };
26
+ /**
27
+ * Build a numeric range from chart values.
28
+ */
29
+ declare function createNumericRange(values: readonly number[]): NumericRange | null;
30
+ /**
31
+ * Decide whether an axis should keep decimal ticks for the current visible
32
+ * numeric values.
33
+ */
34
+ declare function shouldAllowDecimalTicks(values: readonly number[]): boolean;
35
+ /**
36
+ * Format one chart value for a specific UI surface.
37
+ */
38
+ declare function formatChartValue<T>(value: string | number | boolean | Date | null | undefined, options: FormatValueOptions<T>): string;
39
+ /**
40
+ * Format a date bucket label from the machine-friendly pipeline key.
41
+ */
42
+ declare function formatTimeBucketLabel(key: string, bucket: TimeBucket, surface: ChartValueSurface, locale?: string): string;
43
+ /**
44
+ * Format the Y-axis or label width estimate with the same surface rules used in
45
+ * the visible chart.
46
+ */
47
+ declare function formatNumericSurfaceValue(value: number, surface: ChartValueSurface, numericRange?: NumericRange | null, format?: ColumnFormat, formatter?: ChartColumn<unknown>['formatter'], locale?: string): string;
48
+ //#endregion
49
+ export { ChartValueSurface, NumericRange, createNumericRange, formatChartValue, formatNumericSurfaceValue, formatTimeBucketLabel, shouldAllowDecimalTicks };
@@ -54,7 +54,7 @@ function formatTimeBucketLabel(key, bucket, surface, locale = "en-US") {
54
54
  case "day": return formatDateWithOptions(parseBucketDate(key), locale, getBucketDayOptions(surface));
55
55
  case "week": {
56
56
  const date = parseBucketDate(key);
57
- return `${surface === "tooltip" ? "Week of " : "Wk of "}${formatDateWithOptions(date, locale, getBucketWeekOptions(surface))}`;
57
+ return `${isFullPrecisionSurface(surface) ? "Week of " : "Wk of "}${formatDateWithOptions(date, locale, getBucketWeekOptions(surface))}`;
58
58
  }
59
59
  case "month": return formatDateWithOptions(parseBucketMonth(key), locale, {
60
60
  month: "short",
@@ -68,6 +68,22 @@ function formatTimeBucketLabel(key, bucket, surface, locale = "en-US") {
68
68
  }
69
69
  }
70
70
  /**
71
+ * Format the Y-axis or label width estimate with the same surface rules used in
72
+ * the visible chart.
73
+ */
74
+ function formatNumericSurfaceValue(value, surface, numericRange, format, formatter, locale = "en-US") {
75
+ return formatChartValue(value, {
76
+ column: {
77
+ type: "number",
78
+ format,
79
+ formatter
80
+ },
81
+ surface,
82
+ numericRange,
83
+ locale
84
+ });
85
+ }
86
+ /**
71
87
  * Resolve the boolean labels while keeping null handling in the shared entry
72
88
  * point above.
73
89
  */
@@ -176,17 +192,23 @@ function formatDateWithOptions(value, locale, options) {
176
192
  /**
177
193
  * Decide which number family should power the current surface.
178
194
  */
195
+ /**
196
+ * Surfaces that show full-precision values (not compacted).
197
+ */
198
+ function isFullPrecisionSurface(surface) {
199
+ return surface === "tooltip" || surface === "table-cell";
200
+ }
179
201
  function resolveNumberFormatMode(format, surface, numericRange, value) {
180
202
  if (typeof format === "string") switch (format) {
181
- case "currency": return surface === "tooltip" ? "currency" : "compact-currency";
203
+ case "currency": return isFullPrecisionSurface(surface) ? "currency" : "compact-currency";
182
204
  case "compact-number": return "compact-number";
183
205
  case "percent": return "percent";
184
206
  case "number":
185
207
  case "date":
186
- case "datetime": return surface === "tooltip" ? "number" : shouldUseCompactNumber(numericRange, value) ? "compact-number" : "number";
208
+ case "datetime": return isFullPrecisionSurface(surface) ? "number" : shouldUseCompactNumber(numericRange, value) ? "compact-number" : "number";
187
209
  }
188
210
  if (shouldUsePercentByDefault(numericRange)) return "percent";
189
- if (surface !== "tooltip" && shouldUseCompactNumber(numericRange, value)) return "compact-number";
211
+ if (!isFullPrecisionSurface(surface) && shouldUseCompactNumber(numericRange, value)) return "compact-number";
190
212
  return "number";
191
213
  }
192
214
  /**
@@ -239,7 +261,7 @@ function getNumberFormatOptions(mode, surface, numericRange, value) {
239
261
  };
240
262
  case "percent": return {
241
263
  style: "percent",
242
- maximumFractionDigits: surface === "tooltip" ? 2 : 1
264
+ maximumFractionDigits: isFullPrecisionSurface(surface) ? 2 : 1
243
265
  };
244
266
  case "number": return { maximumFractionDigits: getStandardFractionDigits(surface, numericRange, value) };
245
267
  }
@@ -249,7 +271,7 @@ function getNumberFormatOptions(mode, surface, numericRange, value) {
249
271
  */
250
272
  function getStandardFractionDigits(surface, numericRange, value) {
251
273
  const span = numericRange ? Math.abs(numericRange.max - numericRange.min) : Math.abs(value);
252
- if (surface === "tooltip") {
274
+ if (isFullPrecisionSurface(surface)) {
253
275
  if (span < 1) return 3;
254
276
  if (span < 10) return 2;
255
277
  if (span < 100) return 1;
@@ -263,7 +285,7 @@ function getStandardFractionDigits(surface, numericRange, value) {
263
285
  * Match raw date values to a clear default display.
264
286
  */
265
287
  function getDateValueOptions(mode, surface) {
266
- if (mode === "datetime") return surface === "tooltip" ? {
288
+ if (mode === "datetime") return isFullPrecisionSurface(surface) ? {
267
289
  dateStyle: "medium",
268
290
  timeStyle: "short"
269
291
  } : {
@@ -300,7 +322,7 @@ function getBucketDateOptions(bucket, surface) {
300
322
  * Keep day buckets short on axes and clearer in tooltips.
301
323
  */
302
324
  function getBucketDayOptions(surface) {
303
- return surface === "tooltip" ? {
325
+ return isFullPrecisionSurface(surface) ? {
304
326
  month: "short",
305
327
  day: "numeric",
306
328
  year: "2-digit"
@@ -313,7 +335,7 @@ function getBucketDayOptions(surface) {
313
335
  * Keep week buckets short on axes and clearer in tooltips.
314
336
  */
315
337
  function getBucketWeekOptions(surface) {
316
- return surface === "tooltip" ? {
338
+ return isFullPrecisionSurface(surface) ? {
317
339
  month: "short",
318
340
  day: "numeric",
319
341
  year: "2-digit"
@@ -346,4 +368,4 @@ function parseQuarterKey(key) {
346
368
  };
347
369
  }
348
370
  //#endregion
349
- export { createNumericRange, formatChartValue, formatTimeBucketLabel, shouldAllowDecimalTicks };
371
+ export { createNumericRange, formatChartValue, formatNumericSurfaceValue, formatTimeBucketLabel, shouldAllowDecimalTicks };
@@ -0,0 +1,19 @@
1
+ import { DATE_RANGE_PRESETS, DateRangePreset, DateRangePresetId, autoFilterForBucket, getPresetLabel, resolvePresetFilter } from "./date-range-presets.mjs";
2
+ import { AggregateFunction, AggregateMetric, AggregateMetricAllowance, AvailableFilter, BooleanColumn, CategoricalChartType, CategoryColumn, ChartColumn, ChartColumnType, ChartControlMode, ChartDataScopeControlState, ChartDataScopeInputs, ChartDataScopeInputsFromSchemaDefinition, ChartDateRangeSelection, ChartInstance, ChartInstanceFromSchema, ChartInstanceFromSchemaDefinition, ChartSchema, ChartSchemaDefinition, ChartSeries, ChartSourceOptions, ChartType, ChartTypeConfig, ColumnFormat, ColumnFormatPreset, CountMetric, DateColumn, DateColumnFormat, DateRange, DateRangeFilter, DefinedChartSchema, DerivedBooleanColumnSchema, DerivedCategoryColumnSchema, DerivedColumnSchema, DerivedDateColumnSchema, DerivedNumberColumnSchema, DurationColumnFormat, DurationInputUnit, FilterState, FiltersConfig, GroupByConfig, InferableFieldKey, Metric, MetricAllowance, MetricConfig, MultiSourceChartDataScopeInputs, MultiSourceChartInstance, NumberColumn, NumberColumnFormat, NumericAggregateFunction, NumericAggregateSelection, RawColumnSchemaFor, RawColumnSchemaMap, ResolvedColumnIdFromSchema, ResolvedDateColumnIdFromSchema, ResolvedFilterColumnIdFromSchema, ResolvedGroupByColumnIdFromSchema, ResolvedMetricColumnIdFromSchema, ResolvedXAxisColumnIdFromSchema, RestrictedChartTypeFromSchema, RestrictedFilterColumnIdFromSchema, RestrictedGroupByColumnIdFromSchema, RestrictedMetricFromSchema, RestrictedTimeBucketFromSchema, RestrictedXAxisColumnIdFromSchema, SelectableControlConfig, SortConfig, SortDirection, TimeBucket, TimeBucketConfig, TimeSeriesChartType, TransformedDataPoint, ValidatedChartSchema, XAxisConfig } from "./types.mjs";
3
+ import { CHART_TYPE_CONFIG, ChartAxisType, ChartTypeCapabilities } from "./chart-capabilities.mjs";
4
+ import { buildColorMap, getSeriesColor } from "./colors.mjs";
5
+ import { DATA_LABEL_DEFAULTS, DataLabelDefaults, DataLabelPosition, DataLabelStyle, resolveShowDataLabels } from "./data-label-defaults.mjs";
6
+ import { computeDateRange, filterByDateRange } from "./date-utils.mjs";
7
+ import { buildAvailableMetrics, getMetricLabel } from "./metric-utils.mjs";
8
+ import { PipelineInput, PipelineOutput, applyFilters, extractAvailableFilters, runPipeline } from "./pipeline.mjs";
9
+ import { DatasetBuilder, DatasetChartBuilder, DatasetChartDefinition, DatasetDefinition, DatasetKey, DatasetRow, DefinedDataset, DefinedDatasetChartSchema, SingleDatasetKeyId } from "./dataset-builder.types.mjs";
10
+ import { DefinedMaterializedView, MaterializedProjectableColumnId, MaterializedViewDefinition, MaterializedViewMetadata, MaterializedViewStepMetadata, ModelMaterializationBuilder, ModelMaterializationStartBuilder } from "./materialized-view.types.mjs";
11
+ import { DataModelBuilder, DataModelDefinition, DefinedDataModel, KeyedDatasetDefinition, ModelAssociationDefinition, ModelAttributeDefinition, ModelDataInput, ModelDatasetId, ModelRelationshipDefinition, ResolvedDataModelFromDefinition, SelectAttributeConfig } from "./data-model.types.mjs";
12
+ import { defineDataModel } from "./define-data-model.mjs";
13
+ import { DashboardBuilder, DashboardChartIdFromDefinition, DashboardChartInstanceFromDefinition, DashboardDataInputFromDefinition, DashboardDatasetIdFromDefinition, DashboardDatasetRowsFromDefinition, DashboardDateRangeSelection, DashboardDefinition, DashboardLocalSharedSelectFilterConfig, DashboardResolvedChart, DashboardResolvedChartOwnership, DashboardRuntime, DashboardSharedDateRangeFilterConfig, DashboardSharedDateRangeFilterDefinition, DashboardSharedDateRangeFilterRuntime, DashboardSharedDateRangePresetId, DashboardSharedDateRangeTarget, DashboardSharedFilterDefinition, DashboardSharedFilterIdFromDefinition, DashboardSharedFilterRuntime, DashboardSharedFilterRuntimeFromDefinition, DashboardSharedFilters, DashboardSharedSelectFilterRuntime, DashboardSharedSelectTarget, DefinedDashboard, ResolvedDashboardFromDefinition } from "./dashboard.types.mjs";
14
+ import { defineDashboard, resolveDashboardDefinition } from "./define-dashboard.mjs";
15
+ import { defineDataset, validateDatasetData } from "./define-dataset.mjs";
16
+ import { inferColumnsFromData } from "./infer-columns.mjs";
17
+ import { DashboardProvider, useDashboard, useDashboardChart, useDashboardContext, useDashboardDataset, useDashboardSharedFilter } from "./use-dashboard.mjs";
18
+ import { UseChartOptions } from "./use-chart-options.mjs";
19
+ import { useChart } from "./use-chart.mjs";
@@ -215,7 +215,7 @@ function detectStringDateSignal(key, samples) {
215
215
  return {
216
216
  type: "category",
217
217
  confidence: parseRatio >= .8 ? "medium" : "low",
218
- looksDateLike: parseRatio >= .8
218
+ looksDateLike: strongPatternRatio >= .8 || parseRatio >= .8 && hasDateKeySignal
219
219
  };
220
220
  }
221
221
  /**
@@ -461,6 +461,32 @@ function sortResolvedColumns(columns) {
461
461
  });
462
462
  }
463
463
  /**
464
+ * Reorder resolved columns so that schema-declared columns maintain their
465
+ * declaration order while inferred-only columns keep their default sorted
466
+ * positions.
467
+ *
468
+ * The algorithm first sorts all columns with the default heuristic, then
469
+ * replaces the positions occupied by declared columns with those same
470
+ * columns in schema declaration order. This preserves type-rank placement
471
+ * for inferred columns while honouring the author's intent for explicitly
472
+ * declared ones.
473
+ *
474
+ * When no schema columns are declared, all columns are sorted using the
475
+ * default heuristic.
476
+ */
477
+ function orderResolvedColumns(columns, schemaColumnOrder) {
478
+ const sorted = sortResolvedColumns(columns);
479
+ if (!schemaColumnOrder || schemaColumnOrder.length === 0) return sorted;
480
+ const declaredIds = new Set(schemaColumnOrder);
481
+ const declaredPositions = [];
482
+ for (let i = 0; i < sorted.length; i++) if (declaredIds.has(sorted[i].id)) declaredPositions.push(i);
483
+ const schemaOrderIndex = new Map(schemaColumnOrder.map((id, index) => [id, index]));
484
+ const declaredInSchemaOrder = sorted.filter((column) => declaredIds.has(column.id)).sort((a, b) => schemaOrderIndex.get(a.id) - schemaOrderIndex.get(b.id));
485
+ const result = [...sorted];
486
+ for (let i = 0; i < declaredPositions.length; i++) result[declaredPositions[i]] = declaredInSchemaOrder[i];
487
+ return result;
488
+ }
489
+ /**
464
490
  * Resolve chart columns directly from raw data and an optional explicit schema.
465
491
  */
466
492
  function inferColumnsFromData(data, schema) {
@@ -477,7 +503,7 @@ function inferColumnsFromData(data, schema) {
477
503
  }
478
504
  for (const [key, columnSchema] of getDerivedColumnSchemas(resolvedSchema, rawFieldIds)) resolvedColumns.push(buildDerivedColumn(key, columnSchema));
479
505
  if (resolvedColumns.length === 0) warn("No inferable or explicit chart columns were found. Provide non-empty data or schema.columns.");
480
- return finalizeResolvedColumns(sortResolvedColumns(resolvedColumns));
506
+ return finalizeResolvedColumns(orderResolvedColumns(resolvedColumns, resolvedSchema?.columns ? Object.keys(resolvedSchema.columns) : null));
481
507
  }
482
508
  //#endregion
483
509
  export { inferColumnsFromData };