@opendata-ai/openchart-engine 6.3.0 → 6.5.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.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _opendata_ai_openchart_core from '@opendata-ai/openchart-core';
2
- import { LegendLayout, ResolvedChrome, TooltipContent, A11yMetadata, ResolvedTheme, CompileOptions, ChartLayout, LayerSpec, CompileTableOptions, TableLayout, MarkType, MarkDef, DataRow, Encoding, ChromeText, Annotation, LabelConfig, LegendConfig, ThemeConfig, DarkMode, ColumnConfig, GraphSpec, GraphEncoding, GraphLayoutConfig, NodeOverride, VizSpec, ScaleType, EncodingChannel, Rect, LayoutStrategy, Mark, BinTransform, CalculateTransform, ConditionalValueDef, FilterPredicate, TimeUnitTransform, Transform } from '@opendata-ai/openchart-core';
2
+ import { LegendLayout, ResolvedChrome, TooltipContent, A11yMetadata, ResolvedTheme, CompileOptions, ChartLayout, LayerSpec, CompileTableOptions, TableLayout, AnimationSpec, ResolvedAnimation, MarkType, MarkDef, DataRow, Encoding, ChromeText, Annotation, LabelConfig, LegendConfig, ThemeConfig, DarkMode, ColumnConfig, GraphSpec, GraphEncoding, GraphLayoutConfig, NodeOverride, VizSpec, ScaleType, EncodingChannel, Rect, LayoutStrategy, Mark, BinTransform, CalculateTransform, ConditionalValueDef, FilterPredicate, TimeUnitTransform, Transform } from '@opendata-ai/openchart-core';
3
3
  export { ChartLayout, ChartSpec, CompileOptions, CompileTableOptions, GraphLayout, GraphSpec, LayerSpec, TableLayout, TableSpec, VizSpec } from '@opendata-ai/openchart-core';
4
4
  import { ScaleLinear, ScaleTime, ScaleLogarithmic, ScalePower, ScaleSymLog, ScaleBand, ScalePoint, ScaleOrdinal, ScaleQuantile, ScaleQuantize, ScaleThreshold } from 'd3-scale';
5
5
 
@@ -168,6 +168,25 @@ declare function compileTable(spec: unknown, options: CompileTableOptions): Tabl
168
168
  */
169
169
  declare function compileGraph(spec: unknown, options: CompileOptions): GraphCompilation;
170
170
 
171
+ /**
172
+ * Animation resolver: normalizes AnimationSpec into fully resolved config.
173
+ *
174
+ * Handles the shorthand forms:
175
+ * - true -> { enter: true } -> full defaults
176
+ * - { enter: { duration: 800 } } -> merge with defaults
177
+ * - false/undefined -> undefined (no animation)
178
+ */
179
+
180
+ /**
181
+ * Resolve an AnimationSpec into a fully resolved config with all defaults filled.
182
+ * Returns undefined if animation is disabled (false or omitted).
183
+ */
184
+ declare function resolveAnimation(spec: AnimationSpec | undefined): ResolvedAnimation | undefined;
185
+ /**
186
+ * Clamp stagger delay so total stagger time doesn't exceed MAX_TOTAL_STAGGER_MS.
187
+ */
188
+ declare function clampStaggerDelay(delay: number, elementCount: number): number;
189
+
171
190
  /** Chrome with all string values normalized to ChromeText objects. */
172
191
  interface NormalizedChrome {
173
192
  title?: ChromeText;
@@ -214,6 +233,7 @@ interface NormalizedTableSpec {
214
233
  stickyFirstColumn: boolean;
215
234
  compact: boolean;
216
235
  responsive: boolean;
236
+ animation?: AnimationSpec;
217
237
  }
218
238
  /** A GraphSpec with all optional fields filled with sensible defaults. */
219
239
  interface NormalizedGraphSpec {
@@ -510,4 +530,4 @@ declare function runTimeUnit(data: DataRow[], transform: TimeUnitTransform): Dat
510
530
  */
511
531
  declare function runTransforms(data: DataRow[], transforms: Transform[]): DataRow[];
512
532
 
513
- export { type ChartRenderer, type CompileResult, type CompiledGraphEdge, type CompiledGraphNode, type GraphCompilation, type NormalizedChartSpec, type NormalizedChrome, type NormalizedGraphSpec, type NormalizedSpec, type NormalizedTableSpec, type SimulationConfig, type ValidationError, type ValidationErrorCode, type ValidationResult, clearRenderers, compile, compileChart, compileGraph, compileLayer, compileTable, evaluatePredicate, getChartRenderer, isConditionalValueDef, normalizeSpec, registerChartRenderer, resolveConditionalValue, runBin, runCalculate, runFilter, runTimeUnit, runTransforms, validateSpec };
533
+ export { type ChartRenderer, type CompileResult, type CompiledGraphEdge, type CompiledGraphNode, type GraphCompilation, type NormalizedChartSpec, type NormalizedChrome, type NormalizedGraphSpec, type NormalizedSpec, type NormalizedTableSpec, type SimulationConfig, type ValidationError, type ValidationErrorCode, type ValidationResult, clampStaggerDelay, clearRenderers, compile, compileChart, compileGraph, compileLayer, compileTable, evaluatePredicate, getChartRenderer, isConditionalValueDef, normalizeSpec, registerChartRenderer, resolveAnimation, resolveConditionalValue, runBin, runCalculate, runFilter, runTimeUnit, runTransforms, validateSpec };
package/dist/index.js CHANGED
@@ -209,6 +209,7 @@ function resolveTextAnnotation(annotation, scales, chartArea, isDark) {
209
209
  };
210
210
  return {
211
211
  type: "text",
212
+ id: annotation.id,
212
213
  label,
213
214
  stroke: annotation.stroke,
214
215
  fill: annotation.fill,
@@ -261,6 +262,7 @@ function resolveRangeAnnotation(annotation, scales, chartArea, isDark) {
261
262
  const defaultOpacity = isDark ? 0.2 : DEFAULT_RANGE_OPACITY;
262
263
  return {
263
264
  type: "range",
265
+ id: annotation.id,
264
266
  rect,
265
267
  label,
266
268
  fill: annotation.fill ?? DEFAULT_RANGE_FILL,
@@ -314,6 +316,7 @@ function resolveRefLineAnnotation(annotation, scales, chartArea, isDark) {
314
316
  const defaultStroke = isDark ? DARK_REFLINE_STROKE : LIGHT_REFLINE_STROKE;
315
317
  return {
316
318
  type: "refline",
319
+ id: annotation.id,
317
320
  line: { start, end },
318
321
  label,
319
322
  stroke: annotation.stroke ?? defaultStroke,
@@ -771,7 +774,9 @@ function computeStackedBars(data, valueField, categoryField, colorField, xScale,
771
774
  fill: color2,
772
775
  cornerRadius: 0,
773
776
  data: row,
774
- aria
777
+ aria,
778
+ orient: "horizontal",
779
+ stackGroup: category
775
780
  });
776
781
  cumulativeValue += value;
777
782
  }
@@ -810,7 +815,8 @@ function computeSimpleBars(data, valueField, categoryField, xScale, yScale, band
810
815
  fill: color2,
811
816
  cornerRadius: 2,
812
817
  data: row,
813
- aria
818
+ aria,
819
+ orient: "horizontal"
814
820
  });
815
821
  }
816
822
  return marks;
@@ -1048,7 +1054,8 @@ function computeSimpleColumns(data, categoryField, valueField, xScale, yScale, b
1048
1054
  fill: color2,
1049
1055
  cornerRadius: 2,
1050
1056
  data: row,
1051
- aria
1057
+ aria,
1058
+ orient: "vertical"
1052
1059
  });
1053
1060
  }
1054
1061
  return marks;
@@ -1078,7 +1085,8 @@ function computeColoredColumns(data, categoryField, valueField, colorField, xSca
1078
1085
  fill: color2,
1079
1086
  cornerRadius: 2,
1080
1087
  data: row,
1081
- aria
1088
+ aria,
1089
+ orient: "vertical"
1082
1090
  });
1083
1091
  }
1084
1092
  return marks;
@@ -1110,7 +1118,9 @@ function computeStackedColumns(data, categoryField, valueField, colorField, xSca
1110
1118
  fill: color2,
1111
1119
  cornerRadius: 0,
1112
1120
  data: row,
1113
- aria
1121
+ aria,
1122
+ orient: "vertical",
1123
+ stackGroup: category
1114
1124
  });
1115
1125
  cumulativeValue += value;
1116
1126
  }
@@ -5989,7 +5999,8 @@ function normalizeTableSpec(spec, _warnings) {
5989
5999
  pagination: spec.pagination ?? false,
5990
6000
  stickyFirstColumn: spec.stickyFirstColumn ?? false,
5991
6001
  compact: spec.compact ?? false,
5992
- responsive: spec.responsive ?? true
6002
+ responsive: spec.responsive ?? true,
6003
+ animation: spec.animation
5993
6004
  };
5994
6005
  }
5995
6006
  function normalizeGraphSpec(spec, _warnings) {
@@ -6656,6 +6667,77 @@ ${errorMessages}`);
6656
6667
  return { spec: normalized, warnings };
6657
6668
  }
6658
6669
 
6670
+ // src/compiler/animation.ts
6671
+ var ENTER_DEFAULTS = {
6672
+ duration: 500,
6673
+ ease: "smooth",
6674
+ staggerDelay: 80,
6675
+ staggerOrder: "index",
6676
+ annotationDelay: 200
6677
+ };
6678
+ var MAX_TOTAL_STAGGER_MS = 2e3;
6679
+ function resolveAnimation(spec) {
6680
+ if (spec === void 0 || spec === false) return void 0;
6681
+ if (spec === true) {
6682
+ return {
6683
+ enabled: true,
6684
+ duration: ENTER_DEFAULTS.duration,
6685
+ ease: ENTER_DEFAULTS.ease,
6686
+ staggerDelay: ENTER_DEFAULTS.staggerDelay,
6687
+ staggerOrder: ENTER_DEFAULTS.staggerOrder,
6688
+ annotationDelay: ENTER_DEFAULTS.annotationDelay
6689
+ };
6690
+ }
6691
+ const config = spec;
6692
+ if (config.enter === false || config.enter === void 0 && !hasAnyPhase(config)) {
6693
+ return void 0;
6694
+ }
6695
+ const enterConfig = resolvePhaseConfig(config.enter);
6696
+ return {
6697
+ enabled: true,
6698
+ duration: enterConfig.duration,
6699
+ ease: enterConfig.ease,
6700
+ staggerDelay: enterConfig.staggerDelay,
6701
+ staggerOrder: enterConfig.staggerOrder,
6702
+ annotationDelay: config.annotationDelay ?? ENTER_DEFAULTS.annotationDelay
6703
+ };
6704
+ }
6705
+ function clampStaggerDelay(delay, elementCount) {
6706
+ if (elementCount <= 1) return 0;
6707
+ return Math.min(delay, MAX_TOTAL_STAGGER_MS / elementCount);
6708
+ }
6709
+ function hasAnyPhase(config) {
6710
+ return config.enter !== void 0 || config.update !== void 0 || config.exit !== void 0;
6711
+ }
6712
+ function resolvePhaseConfig(phase) {
6713
+ if (phase === void 0 || phase === true) {
6714
+ return {
6715
+ duration: ENTER_DEFAULTS.duration,
6716
+ ease: ENTER_DEFAULTS.ease,
6717
+ staggerDelay: ENTER_DEFAULTS.staggerDelay,
6718
+ staggerOrder: ENTER_DEFAULTS.staggerOrder
6719
+ };
6720
+ }
6721
+ const cfg = phase;
6722
+ const stagger = resolveStagger(cfg.stagger);
6723
+ return {
6724
+ duration: cfg.duration ?? ENTER_DEFAULTS.duration,
6725
+ ease: cfg.ease ?? ENTER_DEFAULTS.ease,
6726
+ staggerDelay: stagger.delay,
6727
+ staggerOrder: stagger.order
6728
+ };
6729
+ }
6730
+ function resolveStagger(stagger) {
6731
+ if (stagger === false) return { delay: 0, order: "index" };
6732
+ if (stagger === void 0 || stagger === true) {
6733
+ return { delay: ENTER_DEFAULTS.staggerDelay, order: ENTER_DEFAULTS.staggerOrder };
6734
+ }
6735
+ return {
6736
+ delay: stagger.delay ?? ENTER_DEFAULTS.staggerDelay,
6737
+ order: stagger.order ?? ENTER_DEFAULTS.staggerOrder
6738
+ };
6739
+ }
6740
+
6659
6741
  // src/graphs/compile-graph.ts
6660
6742
  import { adaptTheme, computeChrome, resolveTheme } from "@opendata-ai/openchart-core";
6661
6743
 
@@ -8744,7 +8826,8 @@ function compileTableLayout(spec, options, theme) {
8744
8826
  caption,
8745
8827
  summary: `${resolvedColumns.length} columns, ${totalFiltered} rows`
8746
8828
  },
8747
- theme
8829
+ theme,
8830
+ animation: resolveAnimation(spec.animation)
8748
8831
  };
8749
8832
  }
8750
8833
 
@@ -9227,6 +9310,8 @@ function compileChart(spec, options) {
9227
9310
  };
9228
9311
  }
9229
9312
  }
9313
+ const rawAnimationSpec = overrides?.[breakpoint]?.animation ?? rawSpec.animation;
9314
+ const resolvedAnimation = resolveAnimation(rawAnimationSpec);
9230
9315
  const mergedThemeConfig = options.theme ? { ...chartSpec.theme, ...options.theme } : chartSpec.theme;
9231
9316
  let theme = resolveTheme2(mergedThemeConfig);
9232
9317
  if (options.darkMode) {
@@ -9357,6 +9442,37 @@ function compileChart(spec, options) {
9357
9442
  },
9358
9443
  chartSpec.data
9359
9444
  );
9445
+ if (resolvedAnimation?.enabled && resolvedAnimation.staggerOrder === "value") {
9446
+ const indexed = marks.map((m, i) => ({ mark: m, idx: i }));
9447
+ indexed.sort((a, b) => {
9448
+ const av = getMarkPrimaryValue(a.mark);
9449
+ const bv = getMarkPrimaryValue(b.mark);
9450
+ return av - bv;
9451
+ });
9452
+ for (let i = 0; i < indexed.length; i++) {
9453
+ const m = indexed[i].mark;
9454
+ if (m.type === "rect" && m.stackGroup) continue;
9455
+ m.animationIndex = i;
9456
+ }
9457
+ }
9458
+ if (resolvedAnimation?.enabled) {
9459
+ const groupIndexMap = /* @__PURE__ */ new Map();
9460
+ const groupStackPos = /* @__PURE__ */ new Map();
9461
+ let nextGroupIndex = 0;
9462
+ for (const mark of marks) {
9463
+ if (mark.type === "rect" && mark.stackGroup) {
9464
+ const rect = mark;
9465
+ const group = rect.stackGroup;
9466
+ if (!groupIndexMap.has(group)) {
9467
+ groupIndexMap.set(group, nextGroupIndex++);
9468
+ }
9469
+ rect.animationIndex = groupIndexMap.get(group);
9470
+ const pos = groupStackPos.get(group) ?? 0;
9471
+ rect.stackPos = pos;
9472
+ groupStackPos.set(group, pos + 1);
9473
+ }
9474
+ }
9475
+ }
9360
9476
  return {
9361
9477
  area: chartArea,
9362
9478
  chrome: dims.chrome,
@@ -9378,9 +9494,29 @@ function compileChart(spec, options) {
9378
9494
  dimensions: {
9379
9495
  width: options.width,
9380
9496
  height: options.height
9381
- }
9497
+ },
9498
+ animation: resolvedAnimation
9382
9499
  };
9383
9500
  }
9501
+ function getMarkPrimaryValue(mark) {
9502
+ switch (mark.type) {
9503
+ case "rect":
9504
+ return mark.height;
9505
+ // bar height is the primary value encoding
9506
+ case "point":
9507
+ return mark.cy;
9508
+ // y position for scatter
9509
+ case "arc":
9510
+ return mark.endAngle - mark.startAngle;
9511
+ // arc angle extent
9512
+ case "line":
9513
+ case "area":
9514
+ return 0;
9515
+ // series marks don't have individual values
9516
+ default:
9517
+ return 0;
9518
+ }
9519
+ }
9384
9520
  function compileLayer(spec, options) {
9385
9521
  const leaves = flattenLayers(spec);
9386
9522
  if (leaves.length === 0) {
@@ -9451,6 +9587,7 @@ function compileGraph2(spec, options) {
9451
9587
  return compileGraph(spec, options);
9452
9588
  }
9453
9589
  export {
9590
+ clampStaggerDelay,
9454
9591
  clearRenderers,
9455
9592
  compile,
9456
9593
  compileChart,
@@ -9462,6 +9599,7 @@ export {
9462
9599
  isConditionalValueDef,
9463
9600
  normalizeSpec,
9464
9601
  registerChartRenderer,
9602
+ resolveAnimation,
9465
9603
  resolveConditionalValue,
9466
9604
  runBin,
9467
9605
  runCalculate,