@hpcc-js/timeline 3.2.4 → 3.3.1

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": "@hpcc-js/timeline",
3
- "version": "3.2.4",
3
+ "version": "3.3.1",
4
4
  "description": "hpcc-js - Viz Timeline",
5
5
  "type": "module",
6
6
  "main": "./dist/index.umd.cjs",
@@ -37,15 +37,15 @@
37
37
  "update-major": "npx --yes npm-check-updates -u"
38
38
  },
39
39
  "dependencies": {
40
- "@hpcc-js/api": "^3.4.4",
41
- "@hpcc-js/chart": "^3.6.0",
42
- "@hpcc-js/common": "^3.6.0",
43
- "@hpcc-js/html": "^3.3.4",
44
- "@hpcc-js/layout": "^3.4.4",
45
- "@hpcc-js/react": "^3.4.4"
40
+ "@hpcc-js/api": "^3.4.5",
41
+ "@hpcc-js/chart": "^3.6.1",
42
+ "@hpcc-js/common": "^3.6.1",
43
+ "@hpcc-js/html": "^3.3.5",
44
+ "@hpcc-js/layout": "^3.5.0",
45
+ "@hpcc-js/react": "^3.4.5"
46
46
  },
47
47
  "devDependencies": {
48
- "@hpcc-js/esbuild-plugins": "^1.7.0",
48
+ "@hpcc-js/esbuild-plugins": "^1.8.0",
49
49
  "d3-array": "^1",
50
50
  "d3-scale": "^1",
51
51
  "d3-selection": "^1",
@@ -63,5 +63,5 @@
63
63
  "url": "https://github.com/hpcc-systems/Visualization/issues"
64
64
  },
65
65
  "homepage": "https://github.com/hpcc-systems/Visualization",
66
- "gitHead": "e26c95239fec90e34e31c16a17bc1522e1086f40"
66
+ "gitHead": "4b76aa36763923096cd3ff7afed75e054ea33197"
67
67
  }
@@ -237,6 +237,8 @@ export interface ReactAxisGanttSeries {
237
237
  bucketColumn(_: string): this;
238
238
  maxZoom(): number;
239
239
  maxZoom(_: number): this;
240
+ preserveZoom(): boolean;
241
+ preserveZoom(_: boolean): this;
240
242
  }
241
243
  ReactAxisGanttSeries.prototype.publish("tickFormat", null, "string", "Format rule applied to axis tick labels", undefined, { optional: true });
242
244
  ReactAxisGanttSeries.prototype.publish("axisHeight", 22, "number", "Height of axes (pixels)");
@@ -260,6 +262,7 @@ ReactAxisGanttSeries.prototype.publishProxy("colorColumn", "_gantt");
260
262
  ReactAxisGanttSeries.prototype.publishProxy("seriesColumn", "_gantt");
261
263
  ReactAxisGanttSeries.prototype.publishProxy("bucketColumn", "_gantt");
262
264
  ReactAxisGanttSeries.prototype.publishProxy("maxZoom", "_gantt");
265
+ ReactAxisGanttSeries.prototype.publishProxy("preserveZoom", "_gantt");
263
266
  ReactAxisGanttSeries.prototype.publishProxy("evenSeriesBackground", "_gantt");
264
267
  ReactAxisGanttSeries.prototype.publishProxy("oddSeriesBackground", "_gantt");
265
268
  ReactAxisGanttSeries.prototype.publishProxy("bucketHeight", "_gantt");
package/src/ReactGantt.ts CHANGED
@@ -36,6 +36,8 @@ export class ReactGantt extends SVGZoomWidget {
36
36
  public _minStart: number;
37
37
  public _maxEnd: number;
38
38
 
39
+ protected _prevZoomState: { visibleStart: number; visibleEnd: number } | null = null;
40
+
39
41
  protected _title_idx = 0;
40
42
  protected _startDate_idx = 1;
41
43
  protected _endDate_idx = 2;
@@ -190,12 +192,14 @@ export class ReactGantt extends SVGZoomWidget {
190
192
  this._buckets = this.calcBuckets(this.data(), this._startDate_idx, this._endDate_idx);
191
193
  }
192
194
  }
193
- const interpedStart = this._interpolateX(this._minStart);
194
195
 
195
- this.zoomTo(
196
- [interpedStart, 0],
197
- 1
198
- );
196
+ if (!this.preserveZoom() || !this._prevZoomState) {
197
+ const interpedStart = this._interpolateX(this._minStart);
198
+ this.zoomTo(
199
+ [interpedStart, 0],
200
+ 1
201
+ );
202
+ }
199
203
 
200
204
  const bucketHeight = this.bucketHeight();
201
205
 
@@ -331,6 +335,51 @@ export class ReactGantt extends SVGZoomWidget {
331
335
  })
332
336
  ;
333
337
  element.on("dblclick.zoom", null);
338
+
339
+ // restore zoom state after all rendering is set up
340
+ if (this.preserveZoom() && this._prevZoomState && this._interpolateX) {
341
+ const width = this.width();
342
+ if (width > 0) {
343
+ const visibleStart = this._minStart;
344
+ const visibleEnd = this._maxEnd;
345
+ const clampedStart = Math.max(visibleStart, Math.min(visibleEnd, this._prevZoomState.visibleStart));
346
+ let clampedEnd = Math.max(visibleStart, Math.min(visibleEnd, this._prevZoomState.visibleEnd));
347
+ if (clampedEnd <= clampedStart) {
348
+ const visibleWidth = visibleEnd - visibleStart;
349
+ const epsilon = visibleWidth * 1e-6 || 1e-6;
350
+ clampedEnd = Math.min(visibleEnd, clampedStart + epsilon);
351
+ }
352
+ const startPixel = this._interpolateX(clampedStart);
353
+ const endPixel = this._interpolateX(clampedEnd);
354
+ const span = endPixel - startPixel;
355
+ if (isFinite(span) && Math.abs(span) > 1e-9) {
356
+ const rawScale = width / span;
357
+ const minScale = 0.05; // must match zoomExtent minimum set at start of update()
358
+ const maxScale = this.maxZoom();
359
+ const targetScale = Math.max(minScale, Math.min(maxScale, rawScale));
360
+
361
+ if (targetScale > 0 && isFinite(targetScale)) {
362
+ const centerPixel = (startPixel + endPixel) / 2;
363
+ const halfViewport = width / (2 * targetScale);
364
+ const x0 = this._interpolateX(visibleStart);
365
+ const x1 = this._interpolateX(visibleEnd);
366
+
367
+ let clampedCenter = centerPixel;
368
+ if (clampedCenter - halfViewport < x0) {
369
+ clampedCenter = x0 + halfViewport;
370
+ }
371
+ if (clampedCenter + halfViewport > x1) {
372
+ clampedCenter = x1 - halfViewport;
373
+ }
374
+
375
+ const translateX = (width / 2) - (targetScale * clampedCenter);
376
+ if (isFinite(translateX)) {
377
+ this.zoomTo([translateX, 0], targetScale);
378
+ }
379
+ }
380
+ }
381
+ }
382
+ }
334
383
  }
335
384
  exit(domNode, element) {
336
385
  this._tooltip.target(null);
@@ -442,6 +491,27 @@ export class ReactGantt extends SVGZoomWidget {
442
491
  public _transform = { k: 1, x: 0, y: 0 };
443
492
  zoomed(transform) {
444
493
  this._transform = transform;
494
+ // store current visible range for zoom preservation
495
+ if (this._interpolateX && typeof this._interpolateX.invert === "function") {
496
+ const width = this.width();
497
+ if (width > 0 && isFinite(transform.k) && transform.k !== 0) {
498
+ const startPixel = (0 - transform.x) / transform.k;
499
+ const endPixel = (width - transform.x) / transform.k;
500
+ let visibleStart = this._interpolateX.invert(startPixel);
501
+ let visibleEnd = this._interpolateX.invert(endPixel);
502
+ if (isFinite(visibleStart) && isFinite(visibleEnd)) {
503
+ if (visibleStart > visibleEnd) {
504
+ const tmp = visibleStart;
505
+ visibleStart = visibleEnd;
506
+ visibleEnd = tmp;
507
+ }
508
+ this._prevZoomState = {
509
+ visibleStart,
510
+ visibleEnd
511
+ };
512
+ }
513
+ }
514
+ }
445
515
  switch (this.renderMode()) {
446
516
  case "scale-all":
447
517
  this._zoomScale = transform.k;
@@ -667,6 +737,8 @@ export interface ReactGantt {
667
737
  fitWidthToContent(_: boolean): this;
668
738
  fitHeightToContent(): boolean;
669
739
  fitHeightToContent(_: boolean): this;
740
+ preserveZoom(): boolean;
741
+ preserveZoom(_: boolean): this;
670
742
  evenSeriesBackground(): string;
671
743
  evenSeriesBackground(_: string): this;
672
744
  oddSeriesBackground(): string;
@@ -675,6 +747,7 @@ export interface ReactGantt {
675
747
 
676
748
  ReactGantt.prototype.publish("fitWidthToContent", false, "boolean", "If true, resize will simply reapply the bounding box width");
677
749
  ReactGantt.prototype.publish("fitHeightToContent", false, "boolean", "If true, resize will simply reapply the bounding box height");
750
+ ReactGantt.prototype.publish("preserveZoom", false, "boolean", "If true, maintain zoom level when data is updated");
678
751
  ReactGantt.prototype.publish("titleColumn", null, "string", "Column name to for the title");
679
752
  ReactGantt.prototype.publish("startDateColumn", null, "string", "Column name to for the start date");
680
753
  ReactGantt.prototype.publish("endDateColumn", null, "string", "Column name to for the end date");
@@ -78,4 +78,6 @@ export interface ReactAxisGanttSeries {
78
78
  bucketColumn(_: string): this;
79
79
  maxZoom(): number;
80
80
  maxZoom(_: number): this;
81
+ preserveZoom(): boolean;
82
+ preserveZoom(_: boolean): this;
81
83
  }
@@ -24,6 +24,10 @@ export declare class ReactGantt extends SVGZoomWidget {
24
24
  _tooltip: any;
25
25
  _minStart: number;
26
26
  _maxEnd: number;
27
+ protected _prevZoomState: {
28
+ visibleStart: number;
29
+ visibleEnd: number;
30
+ } | null;
27
31
  protected _title_idx: number;
28
32
  protected _startDate_idx: number;
29
33
  protected _endDate_idx: number;
@@ -123,6 +127,8 @@ export interface ReactGantt {
123
127
  fitWidthToContent(_: boolean): this;
124
128
  fitHeightToContent(): boolean;
125
129
  fitHeightToContent(_: boolean): this;
130
+ preserveZoom(): boolean;
131
+ preserveZoom(_: boolean): this;
126
132
  evenSeriesBackground(): string;
127
133
  evenSeriesBackground(_: string): this;
128
134
  oddSeriesBackground(): string;