@opendata-ai/openchart-core 6.12.0 → 6.13.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opendata-ai/openchart-core",
3
- "version": "6.12.0",
3
+ "version": "6.13.0",
4
4
  "description": "Types, theme, colors, accessibility, and utilities for openchart",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Riley Hilliard",
@@ -112,7 +112,7 @@ describe('lineChart', () => {
112
112
  const xChannel: EncodingChannel = {
113
113
  field: 'date',
114
114
  type: 'temporal',
115
- axis: { label: 'Year' },
115
+ axis: { title: 'Year' },
116
116
  };
117
117
  const yChannel: EncodingChannel = {
118
118
  field: 'value',
@@ -173,7 +173,7 @@ describe('barChart', () => {
173
173
  const catChannel: EncodingChannel = {
174
174
  field: 'name',
175
175
  type: 'ordinal',
176
- axis: { label: 'Fruit' },
176
+ axis: { title: 'Fruit' },
177
177
  };
178
178
 
179
179
  const spec = barChart(categoricalData, catChannel, 'count');
@@ -320,7 +320,7 @@ describe('mixed FieldRef usage', () => {
320
320
  field: 'value',
321
321
  type: 'quantitative',
322
322
  aggregate: 'mean',
323
- axis: { label: 'Average Value', format: ',.1f' },
323
+ axis: { title: 'Average Value', format: ',.1f' },
324
324
  };
325
325
 
326
326
  const spec = lineChart(timeSeriesData, 'date', yChannel, {
@@ -331,7 +331,7 @@ describe('mixed FieldRef usage', () => {
331
331
  expect(spec.encoding.x?.type).toBe('temporal');
332
332
  // y was a full object, so it's passed through
333
333
  expect(spec.encoding.y?.aggregate).toBe('mean');
334
- expect(spec.encoding.y?.axis?.label).toBe('Average Value');
334
+ expect(spec.encoding.y?.axis?.title).toBe('Average Value');
335
335
  // color was a full object
336
336
  expect(spec.encoding.color?.type).toBe('nominal');
337
337
  });
package/src/index.ts CHANGED
@@ -17,8 +17,7 @@ export * from './types/index';
17
17
  // Colors: palette collections, contrast utilities, color-blindness simulation
18
18
  //
19
19
  // Individual named palettes (SEQUENTIAL_BLUE, DIVERGING_RED_BLUE, etc.) are
20
- // available via SEQUENTIAL_PALETTES and DIVERGING_PALETTES or by direct import
21
- // from '@opendata-ai/openchart-core/src/colors/palettes'.
20
+ // available via the SEQUENTIAL_PALETTES and DIVERGING_PALETTES collections.
22
21
  // ---------------------------------------------------------------------------
23
22
 
24
23
  export type {
@@ -84,6 +84,7 @@ export type {
84
84
  // Spec types (user input)
85
85
  export type {
86
86
  AggregateOp,
87
+ AggregateTransform,
87
88
  AnimationConfig,
88
89
  AnimationEase,
89
90
  AnimationPhaseConfig,
@@ -114,6 +115,7 @@ export type {
114
115
  FieldType,
115
116
  FilterPredicate,
116
117
  FilterTransform,
118
+ FoldTransform,
117
119
  GradientDef,
118
120
  GradientStop,
119
121
  GraphEdge,
package/src/types/spec.ts CHANGED
@@ -195,7 +195,18 @@ export interface MarkDef {
195
195
  export type FieldType = 'quantitative' | 'temporal' | 'nominal' | 'ordinal';
196
196
 
197
197
  /** Aggregate function applied to a field before encoding. */
198
- export type AggregateOp = 'count' | 'sum' | 'mean' | 'median' | 'min' | 'max';
198
+ export type AggregateOp =
199
+ | 'count'
200
+ | 'sum'
201
+ | 'mean'
202
+ | 'median'
203
+ | 'min'
204
+ | 'max'
205
+ | 'variance'
206
+ | 'stdev'
207
+ | 'distinct'
208
+ | 'q1'
209
+ | 'q3';
199
210
 
200
211
  /** Axis configuration for an encoding channel. */
201
212
  export interface AxisConfig {
@@ -227,12 +238,6 @@ export interface AxisConfig {
227
238
  titlePadding?: number;
228
239
  /** Padding between tick labels and axis. */
229
240
  labelPadding?: number;
230
-
231
- // --- Deprecated aliases (will be removed) ---
232
- /** @deprecated Use `title` instead. */
233
- label?: string;
234
- /** @deprecated Use `labelAngle` instead. */
235
- tickAngle?: number;
236
241
  }
237
242
 
238
243
  /** Scale configuration for an encoding channel. */
@@ -309,10 +314,47 @@ export interface EncodingChannel {
309
314
  scale?: ScaleConfig;
310
315
  /**
311
316
  * Stacking behavior for quantitative channels (Vega-Lite aligned).
312
- * - undefined | true | 'zero': stack (default, cumulative segments)
317
+ * - undefined | true | 'zero': stack from zero baseline (default)
318
+ * - 'normalize': stack and normalize to fraction of total (0-1 per category)
319
+ * - 'center': center stacks around zero (streamgraph style)
313
320
  * - null | false: no stacking (grouped/dodged side-by-side)
314
321
  */
315
- stack?: boolean | 'zero' | null;
322
+ stack?: boolean | 'zero' | 'normalize' | 'center' | null;
323
+ /**
324
+ * Encoding-level bin shorthand (Vega-Lite aligned).
325
+ * When set, auto-generates a BinTransform during normalization and updates
326
+ * the field reference to the binned output (convention: `bin_<fieldName>`).
327
+ * - true: bin with default params
328
+ * - BinParams: bin with custom params (maxbins, step, etc.)
329
+ */
330
+ bin?: boolean | BinParams;
331
+ /**
332
+ * Encoding-level timeUnit shorthand (Vega-Lite aligned).
333
+ * When set, auto-generates a TimeUnitTransform during normalization and
334
+ * updates the field reference to the output (convention: `<timeUnit>_<fieldName>`).
335
+ */
336
+ timeUnit?: TimeUnit;
337
+ /**
338
+ * Sort order for categorical (nominal/ordinal) scale domains (Vega-Lite aligned).
339
+ * - 'ascending': sort domain values ascending (default VL behavior)
340
+ * - 'descending': sort domain values descending
341
+ * - null: use data order (no sorting)
342
+ * - undefined: ascending (VL default)
343
+ */
344
+ sort?: 'ascending' | 'descending' | null;
345
+ /**
346
+ * Display title override (Vega-Lite aligned).
347
+ * Used as the label in tooltips instead of the raw field name.
348
+ * Also usable for axis titles, but `axis.title` takes precedence there.
349
+ */
350
+ title?: string;
351
+ /**
352
+ * Format string for values (Vega-Lite aligned).
353
+ * For quantitative fields: d3-format string (e.g. ",.0f", "$,.2f").
354
+ * For temporal fields: d3-time-format string (e.g. "%Y", "%b %d").
355
+ * Used in tooltips; `axis.format` takes precedence for axis tick labels.
356
+ */
357
+ format?: string;
316
358
  }
317
359
 
318
360
  /**
@@ -1198,8 +1240,34 @@ export interface TimeUnitTransform {
1198
1240
  as: string;
1199
1241
  }
1200
1242
 
1243
+ /**
1244
+ * Aggregate transform: group rows and compute summary statistics (VL aligned).
1245
+ * Produces one row per group with the groupby fields and computed aggregates.
1246
+ */
1247
+ export interface AggregateTransform {
1248
+ aggregate: Array<{ op: AggregateOp; field: string; as: string }>;
1249
+ groupby: string[];
1250
+ }
1251
+
1252
+ /**
1253
+ * Fold transform: unpivot wide-format columns into key/value rows (VL aligned).
1254
+ * For each input row, produces N output rows (one per fold field) with all
1255
+ * non-fold fields copied plus a key column (field name) and value column (field value).
1256
+ */
1257
+ export interface FoldTransform {
1258
+ fold: string[];
1259
+ /** Output field names for [key, value]. Defaults to ['key', 'value']. */
1260
+ as?: [string, string];
1261
+ }
1262
+
1201
1263
  /** Discriminated union of all transform types. */
1202
- export type Transform = FilterTransform | BinTransform | CalculateTransform | TimeUnitTransform;
1264
+ export type Transform =
1265
+ | FilterTransform
1266
+ | BinTransform
1267
+ | CalculateTransform
1268
+ | TimeUnitTransform
1269
+ | AggregateTransform
1270
+ | FoldTransform;
1203
1271
 
1204
1272
  // ---------------------------------------------------------------------------
1205
1273
  // Conditional encoding (Vega-Lite aligned)