@perspective-dev/viewer-charts 5.0.0 → 5.2.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.
@@ -44,6 +44,33 @@ export interface ChartTypeConfig {
44
44
  */
45
45
  applicable_plugin_fields: readonly PluginConfigField[];
46
46
 
47
+ /**
48
+ * What `group_by` DRAWS in this chart, e.g. `"X Axis"` for the
49
+ * Y-series charts. Omitted where the field has no visual role of
50
+ * its own and is a plain aggregation key — the X/Y and map charts,
51
+ * whose axes both come from `columns`.
52
+ *
53
+ * The `group_by` counterpart of `initial.names`: one declaration
54
+ * says what every view field draws, so the settings UI's labels and
55
+ * the agent's `list_plugins` contract read the same source instead
56
+ * of restating the mapping.
57
+ */
58
+ group_by_role?: string;
59
+
60
+ /** What `split_by` DRAWS in this chart. See {@link group_by_role}. */
61
+ split_by_role?: string;
62
+
63
+ /**
64
+ * Set on the charts that CONNECT their points in row order, so the
65
+ * view's row order shows up in the drawing: without a `sort` the
66
+ * line follows the table's natural order, which reads as a tangle
67
+ * unless the rows already arrive ordered along the X axis. The point
68
+ * charts (scatter, density) are unaffected, and a pre-ordered table
69
+ * needs no `sort` — which is why this is declared per chart rather
70
+ * than inferred from an empty `sort`.
71
+ */
72
+ connects_row_order?: boolean;
73
+
47
74
  /**
48
75
  * Per-chart-type overrides for `DEFAULT_PLUGIN_CONFIG`. Used when a
49
76
  * field's sensible default differs by chart family — currently
@@ -187,6 +214,9 @@ function make(
187
214
  | "max_columns"
188
215
  | "default_chart_type"
189
216
  | "plugin_field_defaults"
217
+ | "group_by_role"
218
+ | "split_by_role"
219
+ | "connects_row_order"
190
220
  >
191
221
  >,
192
222
  ): ChartTypeConfig {
@@ -205,30 +235,52 @@ function make(
205
235
  ...(overrides?.plugin_field_defaults
206
236
  ? { plugin_field_defaults: overrides.plugin_field_defaults }
207
237
  : {}),
238
+ ...(overrides?.group_by_role
239
+ ? { group_by_role: overrides.group_by_role }
240
+ : {}),
241
+ ...(overrides?.split_by_role
242
+ ? { split_by_role: overrides.split_by_role }
243
+ : {}),
244
+ ...(overrides?.connects_row_order
245
+ ? { connects_row_order: overrides.connects_row_order }
246
+ : {}),
208
247
  };
209
248
  }
210
249
 
211
250
  const FIN_NAMES = ["Open", "Close", "High", "Low", "Tooltip"];
212
251
  const HIER_NAMES = ["Size", "Color", "Tooltip"];
213
252
 
253
+ const SERIES_Y_ROLES = { group_by_role: "X Axis", split_by_role: "Series" };
254
+ const SERIES_X_ROLES = { group_by_role: "Y Axis", split_by_role: "Series" };
255
+ const CART_ROLES = {};
256
+ const HIER_ROLES = { group_by_role: "Hierarchy", split_by_role: "Facets" };
257
+ const HEATMAP_ROLES = { group_by_role: "X Axis", split_by_role: "Y Axis" };
258
+ const FIN_ROLES = { group_by_role: "X Axis", split_by_role: "Series" };
259
+ const MAP_ROLES = {};
260
+
214
261
  const CHARTS: ChartTypeConfig[] = [
215
262
  make("X Bar", "x-bar", SERIES, SELECT, 1, X_AXIS, SERIES_FIELDS, {
263
+ ...SERIES_X_ROLES,
216
264
  default_chart_type: "bar",
217
265
  plugin_field_defaults: ZERO_ANCHORED_DEFAULTS,
218
266
  }),
219
267
  make("Y Bar", "y-bar", SERIES, SELECT, 1, Y_AXIS, SERIES_FIELDS, {
268
+ ...SERIES_Y_ROLES,
220
269
  default_chart_type: "bar",
221
270
  plugin_field_defaults: ZERO_ANCHORED_DEFAULTS,
222
271
  }),
223
272
  make("Y Line", "y-line", SERIES, SELECT, 1, Y_AXIS, SERIES_FIELDS, {
273
+ ...SERIES_Y_ROLES,
224
274
  default_chart_type: "line",
225
275
  plugin_field_defaults: SERIES_DEFAULTS,
226
276
  }),
227
277
  make("Y Scatter", "y-scatter", SERIES, SELECT, 1, Y_AXIS, SERIES_FIELDS, {
278
+ ...SERIES_Y_ROLES,
228
279
  default_chart_type: "scatter",
229
280
  plugin_field_defaults: SERIES_DEFAULTS,
230
281
  }),
231
282
  make("Y Area", "y-area", SERIES, SELECT, 1, Y_AXIS, SERIES_FIELDS, {
283
+ ...SERIES_Y_ROLES,
232
284
  default_chart_type: "area",
233
285
  plugin_field_defaults: ZERO_ANCHORED_DEFAULTS,
234
286
  }),
@@ -240,6 +292,7 @@ const CHARTS: ChartTypeConfig[] = [
240
292
  2,
241
293
  ["X Axis", "Y Axis", "Color", "Size", "Label", "Tooltip"],
242
294
  CARTESIAN_FIELDS,
295
+ { ...CART_ROLES },
243
296
  ),
244
297
  make(
245
298
  "X/Y Line",
@@ -249,6 +302,7 @@ const CHARTS: ChartTypeConfig[] = [
249
302
  2,
250
303
  ["X Axis", "Y Axis", "Tooltip"],
251
304
  CARTESIAN_FIELDS,
305
+ { ...CART_ROLES, connects_row_order: true },
252
306
  ),
253
307
  make(
254
308
  "Density",
@@ -258,15 +312,24 @@ const CHARTS: ChartTypeConfig[] = [
258
312
  2,
259
313
  ["X Axis", "Y Axis", "Color", "Tooltip"],
260
314
  DENSITY_FIELDS,
315
+ { ...CART_ROLES },
261
316
  ),
262
- make("Treemap", "treemap", HIER, TOGGLE, 1, HIER_NAMES, NO_FIELDS),
263
- make("Sunburst", "sunburst", HIER, TOGGLE, 1, HIER_NAMES, NO_FIELDS),
264
- make("Heatmap", "heatmap", HIER, SELECT, 1, ["Color"], HEATMAP_FIELDS),
317
+ make("Treemap", "treemap", HIER, TOGGLE, 1, HIER_NAMES, NO_FIELDS, {
318
+ ...HIER_ROLES,
319
+ }),
320
+ make("Sunburst", "sunburst", HIER, TOGGLE, 1, HIER_NAMES, NO_FIELDS, {
321
+ ...HIER_ROLES,
322
+ }),
323
+ make("Heatmap", "heatmap", HIER, SELECT, 1, ["Color"], HEATMAP_FIELDS, {
324
+ ...HEATMAP_ROLES,
325
+ }),
265
326
  make("Candlestick", "candlestick", FIN, TOGGLE, 1, FIN_NAMES, FIN_FIELDS, {
327
+ ...FIN_ROLES,
266
328
  default_chart_type: "candlestick",
267
329
  plugin_field_defaults: SERIES_DEFAULTS,
268
330
  }),
269
331
  make("OHLC", "ohlc", FIN, TOGGLE, 1, FIN_NAMES, FIN_FIELDS, {
332
+ ...FIN_ROLES,
270
333
  default_chart_type: "ohlc",
271
334
  plugin_field_defaults: SERIES_DEFAULTS,
272
335
  }),
@@ -278,6 +341,7 @@ const CHARTS: ChartTypeConfig[] = [
278
341
  2,
279
342
  ["Longitude", "Latitude", "Color", "Size", "Label", "Tooltip"],
280
343
  MAP_SCATTER_FIELDS,
344
+ { ...MAP_ROLES },
281
345
  ),
282
346
  make(
283
347
  "Map Line",
@@ -287,6 +351,7 @@ const CHARTS: ChartTypeConfig[] = [
287
351
  2,
288
352
  ["Longitude", "Latitude", "Tooltip"],
289
353
  MAP_LINE_FIELDS,
354
+ { ...MAP_ROLES, connects_row_order: true },
290
355
  ),
291
356
  make(
292
357
  "Map Density",
@@ -296,6 +361,7 @@ const CHARTS: ChartTypeConfig[] = [
296
361
  2,
297
362
  ["Longitude", "Latitude", "Color", "Tooltip"],
298
363
  MAP_DENSITY_FIELDS,
364
+ { ...MAP_ROLES },
299
365
  ),
300
366
  ];
301
367
 
@@ -171,6 +171,16 @@ export class HTMLPerspectiveViewerWebGLPluginElement
171
171
  */
172
172
  private _pluginConfigStore: PluginConfig | null = null;
173
173
 
174
+ /**
175
+ * Per-column config (`alt_axis`, `chart_type`, formats, …), held on
176
+ * the element for the same reason as `_pluginConfigStore`: the host
177
+ * calls `restore()` BEFORE the first draw builds the renderer, so
178
+ * forwarding only to a live renderer silently drops the initial
179
+ * `columns_config` — `_buildRenderer` ships it in the `InitMsg`
180
+ * instead.
181
+ */
182
+ private _columnsConfig: Record<string, any> = {};
183
+
174
184
  private get _pluginConfig(): PluginConfig {
175
185
  if (!this._pluginConfigStore) {
176
186
  this._pluginConfigStore = this._effectiveDefaults();
@@ -396,6 +406,7 @@ export class HTMLPerspectiveViewerWebGLPluginElement
396
406
  zoom_mode: this._pluginConfig.facet_zoom_mode,
397
407
  },
398
408
  pluginConfig: this._pluginConfig,
409
+ columnsConfig: this._columnsConfig,
399
410
  defaultChartType: this._chartType.default_chart_type,
400
411
  renderBlitMode: BLIT_MODE,
401
412
  });
@@ -431,6 +442,10 @@ export class HTMLPerspectiveViewerWebGLPluginElement
431
442
  max_cells: this._chartType.max_cells,
432
443
  max_columns: this._chartType.max_columns,
433
444
  group_rollup_modes: ["flat"],
445
+ split_rollup_modes: ["flat"],
446
+ group_by_role: this._chartType.group_by_role,
447
+ split_by_role: this._chartType.split_by_role,
448
+ connects_row_order: !!this._chartType.connects_row_order,
434
449
  priority: 0,
435
450
  can_render_column_styles:
436
451
  !!this._chartType.default_chart_type ||
@@ -693,8 +708,9 @@ export class HTMLPerspectiveViewerWebGLPluginElement
693
708
  ...config,
694
709
  };
695
710
 
711
+ this._columnsConfig = columns_config ?? {};
696
712
  this._renderer?.setPluginConfig(this._pluginConfig);
697
- this._renderer?.setColumnsConfig(columns_config ?? {});
713
+ this._renderer?.setColumnsConfig(this._columnsConfig);
698
714
  }
699
715
 
700
716
  delete() {
@@ -162,6 +162,15 @@ export interface InitMsg {
162
162
  * a `setPluginConfig` control msg.
163
163
  */
164
164
  pluginConfig: PluginConfig;
165
+
166
+ /**
167
+ * Initial per-column config (`alt_axis`, `chart_type`, formats, …).
168
+ * Seeds the chart impl before the first `loadAndRender` — the host
169
+ * calls `plugin.restore` before the renderer exists, so without
170
+ * this the initial `columns_config` never reaches the worker.
171
+ * Later changes arrive as `setColumnsConfig` control msgs.
172
+ */
173
+ columnsConfig?: Record<string, any>;
165
174
  defaultChartType?: string;
166
175
 
167
176
  /**
@@ -258,6 +258,7 @@ export class RendererTransport {
258
258
  chrome: HTMLCanvasElement;
259
259
  facetConfig: FacetConfig;
260
260
  pluginConfig: PluginConfig;
261
+ columnsConfig?: Record<string, any>;
261
262
  defaultChartType?: string;
262
263
  renderBlitMode: "blit" | "direct";
263
264
  }): Promise<void> {
@@ -339,6 +340,7 @@ export class RendererTransport {
339
340
  tableName: this._tableName,
340
341
  facetConfig: opts.facetConfig,
341
342
  pluginConfig: opts.pluginConfig,
343
+ columnsConfig: opts.columnsConfig,
342
344
  defaultChartType: opts.defaultChartType,
343
345
  themeVars,
344
346
  fontFaces,
@@ -577,6 +579,17 @@ export class RendererTransport {
577
579
  return Promise.resolve();
578
580
  }
579
581
 
582
+ const dpr = window.devicePixelRatio || 1;
583
+ const last = this._lastPostedSize;
584
+ if (
585
+ last &&
586
+ Math.abs(last.cssWidth - cssWidth) <= 0.5 &&
587
+ Math.abs(last.cssHeight - cssHeight) <= 0.5 &&
588
+ last.dpr === dpr
589
+ ) {
590
+ return Promise.resolve();
591
+ }
592
+
580
593
  this._holdPresent = true;
581
594
  return this._postResize(cssWidth, cssHeight).then(
582
595
  () => () => this._presentStaged(),
@@ -204,6 +204,9 @@ export class WorkerRenderer {
204
204
 
205
205
  this.chartImpl.setFacetConfig?.(msg.facetConfig);
206
206
  this.chartImpl.setPluginConfig?.(msg.pluginConfig);
207
+ if (msg.columnsConfig) {
208
+ this.chartImpl.setColumnsConfig?.(msg.columnsConfig);
209
+ }
207
210
 
208
211
  if (this.chartImpl.setZoomController) {
209
212
  this.zoomController = new ZoomController();