@windborne/grapher 1.0.36 → 1.0.38

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": "@windborne/grapher",
3
- "version": "1.0.36",
3
+ "version": "1.0.38",
4
4
  "description": "Graphing library",
5
5
  "main": "src/index.js",
6
6
  "module": "dist/bundle.esm.js",
@@ -21,7 +21,7 @@ import binarySearch from '../helpers/binary_search';
21
21
 
22
22
  export default React.memo(GraphBody);
23
23
 
24
- function GraphBody({ stateController, webgl, bodyHeight, boundsSelectionEnabled, showTooltips, tooltipOptions, checkIntersection, draggablePoints, onPointDrag, onDraggablePointsDoubleClick, verticalLines, clockStyle, timeZone, onPointClick }) {
24
+ function GraphBody({ stateController, webgl, bodyHeight, boundsSelectionEnabled, showTooltips, showContextMenu=true, tooltipOptions, checkIntersection, draggablePoints, onPointDrag, onDraggablePointsDoubleClick, verticalLines, clockStyle, timeZone, onPointClick }) {
25
25
  const canvasEl = useCallback((el) => {
26
26
  if (stateController.primaryRenderer) {
27
27
  stateController.primaryRenderer.dispose();
@@ -278,11 +278,13 @@ function GraphBody({ stateController, webgl, bodyHeight, boundsSelectionEnabled,
278
278
 
279
279
  const onContextMenu = (event) => {
280
280
  event.preventDefault();
281
- stateController.setContextMenuMousePosition({
282
- clientX: event.clientX,
283
- clientY: event.clientY,
284
- showing: true
285
- });
281
+ if (showContextMenu !== false) {
282
+ stateController.setContextMenuMousePosition({
283
+ clientX: event.clientX,
284
+ clientY: event.clientY,
285
+ showing: true
286
+ });
287
+ }
286
288
  };
287
289
 
288
290
  return (
package/src/grapher.jsx CHANGED
@@ -239,6 +239,7 @@ function Grapher(props) {
239
239
  bodyHeight={props.bodyHeight}
240
240
  boundsSelectionEnabled={props.boundsSelectionEnabled}
241
241
  showTooltips={props.showTooltips}
242
+ showContextMenu={props.showContextMenu}
242
243
  tooltipOptions={props.tooltipOptions}
243
244
  checkIntersection={props.checkIntersection}
244
245
  draggablePoints={props.draggablePoints}
package/src/index.d.ts CHANGED
@@ -131,6 +131,7 @@ export interface GrapherProps {
131
131
  showRangeSelectors?: boolean;
132
132
  showSeriesKey?: boolean;
133
133
  showTooltips?: boolean;
134
+ showContextMenu?: boolean;
134
135
  showGrid?: boolean;
135
136
  showAxisColors?: boolean;
136
137
  bigLabels?: boolean;
@@ -29,6 +29,12 @@ export default function drawLine(dataInRenderSpace, {
29
29
  return;
30
30
  }
31
31
 
32
+ // Check if this is a WebGL context instead of 2D context
33
+ if (!context.setLineDash) {
34
+ console.error('drawLine called with WebGL context instead of 2D context');
35
+ return;
36
+ }
37
+
32
38
  if (highlighted) {
33
39
  width += 2;
34
40
  }
@@ -351,9 +357,27 @@ export default function drawLine(dataInRenderSpace, {
351
357
  if (showIndividualPoints) {
352
358
  const individualPoints = getIndividualPoints();
353
359
 
354
- if (renderCutoffGradient && cutoffIndex !== undefined && originalData) {
360
+ if (renderCutoffGradient && cutoffIndex !== undefined && originalData && selectionBounds) {
361
+ const visibleBounds = selectionBounds;
362
+ let firstTime, lastTime;
363
+
364
+ if (visibleBounds && visibleBounds.minX !== undefined && visibleBounds.maxX !== undefined) {
365
+ firstTime = visibleBounds.minX instanceof Date ? visibleBounds.minX.getTime() : visibleBounds.minX;
366
+ lastTime = visibleBounds.maxX instanceof Date ? visibleBounds.maxX.getTime() : visibleBounds.maxX;
367
+ } else {
368
+ const firstItem = originalData[0];
369
+ const lastItem = originalData[originalData.length - 1];
370
+ const firstX = firstItem[0];
371
+ const lastX = lastItem[0];
372
+
373
+ firstTime = firstX instanceof Date ? firstX.getTime() : firstX;
374
+ lastTime = lastX instanceof Date ? lastX.getTime() : lastX;
375
+ }
376
+
355
377
  let cutoffTime;
356
- if (typeof originalData[0] === 'object' && originalData[0].length === 2) {
378
+ if (typeof cutoffIndex === 'string' && cutoffIndex === 'now') {
379
+ cutoffTime = Date.now();
380
+ } else if (typeof originalData[0] === 'object' && originalData[0].length === 2) {
357
381
  const baseIndex = Math.floor(cutoffIndex);
358
382
  const fraction = cutoffIndex - baseIndex;
359
383
 
@@ -371,6 +395,38 @@ export default function drawLine(dataInRenderSpace, {
371
395
  cutoffTime = cutoffIndex;
372
396
  }
373
397
 
398
+ if (cutoffTime !== null) {
399
+ const timeDiff = cutoffTime - firstTime;
400
+ const totalTime = lastTime - firstTime;
401
+ const timeRatio = timeDiff / totalTime;
402
+
403
+ if (timeRatio > 1) {
404
+ const spacedPoints = applyPointSpacing(individualPoints, minPointSpacing);
405
+ for (let i = 0; i < spacedPoints.length; i++) {
406
+ const [x, y] = spacedPoints[i];
407
+
408
+ let pointColor = color;
409
+ if (negativeColor && hasNegatives) {
410
+ if (y === zero && zeroColor) {
411
+ pointColor = zeroColor;
412
+ } else if (y < zero) {
413
+ pointColor = color;
414
+ } else {
415
+ pointColor = negativeColor;
416
+ }
417
+ }
418
+
419
+ const { applyReducedOpacity } = require("../helpers/colors");
420
+ const reducedOpacityColor = applyReducedOpacity(pointColor, cutoffOpacity);
421
+ context.fillStyle = reducedOpacityColor;
422
+ context.beginPath();
423
+ context.arc(x, y, pointRadius || 8, 0, 2 * Math.PI, false);
424
+ context.fill();
425
+ }
426
+ return;
427
+ }
428
+ }
429
+
374
430
  if (isPreview) {
375
431
  const visibleMinTime = selectionBounds.minX instanceof Date ? selectionBounds.minX.getTime() : selectionBounds.minX;
376
432
  const visibleMaxTime = selectionBounds.maxX instanceof Date ? selectionBounds.maxX.getTime() : selectionBounds.maxX;
@@ -417,7 +473,6 @@ export default function drawLine(dataInRenderSpace, {
417
473
  for (let i = 0; i < spacedPoints.length; i++) {
418
474
  const [x, y] = spacedPoints[i];
419
475
 
420
- // Determine point color based on position relative to zero
421
476
  let pointColor = color;
422
477
  if (negativeColor && hasNegatives) {
423
478
  if (y === zero && zeroColor) {
@@ -96,6 +96,12 @@ export default class GraphBodyRenderer extends Eventable {
96
96
  clear() {
97
97
  if (this._webgl) {
98
98
  this._lineProgram.clear();
99
+ // Also clear the overlay canvas if it exists (for multi shadow charts)
100
+ if (this._overlayContext) {
101
+ this._overlayContext.clearRect(0, 0, this._overlayCanvas.width, this._overlayCanvas.height);
102
+ }
103
+ // Reset the initialization flag so canvas can be resized if needed
104
+ this._overlayCanvasInitialized = false;
99
105
  } else {
100
106
  this._context.clearRect(0, 0, this._context.canvas.width, this._context.canvas.height);
101
107
  }
@@ -564,8 +570,8 @@ export default class GraphBodyRenderer extends Eventable {
564
570
  // since WebGL and 2D contexts can't coexist on the same canvas
565
571
  let drawContext = this._context;
566
572
 
567
- if (this._webgl && singleSeries.rendering === 'shadow' && (width > 0 || shouldShowIndividualPoints)) {
568
- // Only create overlay if we're actually drawing lines or points
573
+ if (this._webgl && singleSeries.rendering === 'shadow') {
574
+ // Always create overlay for shadow charts since they need 2D context for drawLine
569
575
  if (!this._overlayCanvas) {
570
576
  this._overlayCanvas = document.createElement('canvas');
571
577
  this._overlayCanvas.style.position = 'absolute';
@@ -576,19 +582,28 @@ export default class GraphBodyRenderer extends Eventable {
576
582
  this._canvas.parentNode.insertBefore(this._overlayCanvas, this._canvas.nextSibling);
577
583
  }
578
584
 
579
- // Size the overlay canvas to match the main canvas
580
- this._overlayCanvas.width = this._canvas.width;
581
- this._overlayCanvas.height = this._canvas.height;
582
- this._overlayCanvas.style.width = this._canvas.style.width;
583
- this._overlayCanvas.style.height = this._canvas.style.height;
584
-
585
- // Clear the overlay before drawing
586
- this._overlayContext.clearRect(0, 0, this._overlayCanvas.width, this._overlayCanvas.height);
587
-
585
+ // Size the overlay canvas to match the main canvas (only once when creating)
586
+ if (!this._overlayCanvasInitialized) {
587
+ this._overlayCanvas.width = this._canvas.width;
588
+ this._overlayCanvas.height = this._canvas.height;
589
+ this._overlayCanvas.style.width = this._canvas.style.width;
590
+ this._overlayCanvas.style.height = this._canvas.style.height;
591
+ this._overlayCanvasInitialized = true;
592
+ }
588
593
  drawContext = this._overlayContext;
589
594
  } else if (this._context2d) {
590
595
  // For non-WebGL or non-shadow charts with 2D context
591
596
  drawContext = this._context2d;
597
+ } else if (this._webgl) {
598
+ // Fallback for WebGL: create a temporary context for drawing lines
599
+ console.warn('Creating fallback 2D context for WebGL shadow chart');
600
+ if (!this._fallbackContext) {
601
+ const fallbackCanvas = document.createElement('canvas');
602
+ fallbackCanvas.width = this._canvas.width;
603
+ fallbackCanvas.height = this._canvas.height;
604
+ this._fallbackContext = fallbackCanvas.getContext('2d');
605
+ }
606
+ drawContext = this._fallbackContext;
592
607
  }
593
608
 
594
609
  const drawParams = {
@@ -612,6 +627,19 @@ export default class GraphBodyRenderer extends Eventable {
612
627
  zeroColor: singleSeries.zeroLineColor
613
628
  };
614
629
 
630
+ if (this._webgl && singleSeries.rendering === 'shadow' && singleSeries.cutoffTime && (width > 0 || shouldShowIndividualPoints)) {
631
+ drawParams.cutoffIndex = cutoffIndex;
632
+ drawParams.cutoffOpacity = singleSeries.cutoffOpacity !== undefined ? singleSeries.cutoffOpacity : 0.35;
633
+ drawParams.originalData = cutoffData;
634
+ drawParams.renderCutoffGradient = cutoffIndex >= 0;
635
+
636
+ const selection = this === this._stateController.rangeGraphRenderer
637
+ ? this._stateController._bounds
638
+ : (this._stateController._selection || this._stateController._bounds);
639
+ drawParams.selectionBounds = selection || bounds;
640
+ drawParams.currentBounds = bounds;
641
+ }
642
+
615
643
 
616
644
  if (!inRenderSpace) {
617
645
  console.error('inRenderSpace is null for line rendering');