aliencharts 0.3.2 → 0.3.3

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/react.js CHANGED
@@ -1750,19 +1750,24 @@ var getScaledYRangeForLayout = ({
1750
1750
  descriptor,
1751
1751
  state,
1752
1752
  plot,
1753
+ yBaseRangeRef,
1753
1754
  yScaleRef,
1754
1755
  yCenterOffsetRef
1755
- }) => applyYScale(
1756
- getYRange(
1756
+ }) => {
1757
+ const automaticRange = getYRange(
1757
1758
  chart,
1758
1759
  state.xMin,
1759
1760
  state.xMax,
1760
1761
  getCategoryPixelLength(chart, plot, descriptor),
1761
1762
  descriptor
1762
- ),
1763
- yScaleRef.current.get(chart.id) ?? 1,
1764
- yCenterOffsetRef.current.get(chart.id) ?? 0
1765
- );
1763
+ );
1764
+ const lockedRange = yBaseRangeRef?.current.get(chart.id);
1765
+ return applyYScale(
1766
+ lockedRange ? { ...automaticRange, ...lockedRange } : automaticRange,
1767
+ yScaleRef.current.get(chart.id) ?? 1,
1768
+ yCenterOffsetRef.current.get(chart.id) ?? 0
1769
+ );
1770
+ };
1766
1771
  var applyRectangleZoom = ({
1767
1772
  chart,
1768
1773
  descriptor: suppliedDescriptor,
@@ -1771,6 +1776,7 @@ var applyRectangleZoom = ({
1771
1776
  end,
1772
1777
  initialVisiblePoints,
1773
1778
  viewStateRef,
1779
+ yBaseRangeRef,
1774
1780
  yScaleRef,
1775
1781
  yCenterOffsetRef,
1776
1782
  yManualScaleRef
@@ -1787,6 +1793,7 @@ var applyRectangleZoom = ({
1787
1793
  descriptor,
1788
1794
  state,
1789
1795
  plot,
1796
+ yBaseRangeRef,
1790
1797
  yScaleRef,
1791
1798
  yCenterOffsetRef
1792
1799
  });
@@ -1839,6 +1846,7 @@ var screenPointToDataPoint = ({
1839
1846
  plot,
1840
1847
  initialVisiblePoints,
1841
1848
  viewStateRef,
1849
+ yBaseRangeRef,
1842
1850
  yScaleRef,
1843
1851
  yCenterOffsetRef
1844
1852
  }) => {
@@ -1849,6 +1857,7 @@ var screenPointToDataPoint = ({
1849
1857
  descriptor,
1850
1858
  state,
1851
1859
  plot,
1860
+ yBaseRangeRef,
1852
1861
  yScaleRef,
1853
1862
  yCenterOffsetRef
1854
1863
  });
@@ -1965,6 +1974,7 @@ var drawChartLayouts = ({
1965
1974
  antialiasLines = false,
1966
1975
  layouts,
1967
1976
  viewStateRef,
1977
+ yBaseRangeRef,
1968
1978
  yScaleRef,
1969
1979
  yCenterOffsetRef,
1970
1980
  initialVisiblePoints,
@@ -2002,17 +2012,15 @@ var drawChartLayouts = ({
2002
2012
  plot,
2003
2013
  descriptor
2004
2014
  );
2005
- const yRange = applyYScale(
2006
- getYRange(
2007
- chart,
2008
- state.xMin,
2009
- state.xMax,
2010
- categoryPixelLength,
2011
- descriptor
2012
- ),
2013
- yScaleRef.current.get(chart.id) ?? 1,
2014
- yCenterOffsetRef.current.get(chart.id) ?? 0
2015
- );
2015
+ const yRange = getScaledYRangeForLayout({
2016
+ chart,
2017
+ descriptor,
2018
+ state,
2019
+ plot,
2020
+ yBaseRangeRef,
2021
+ yScaleRef,
2022
+ yCenterOffsetRef
2023
+ });
2016
2024
  gl.enable(gl.SCISSOR_TEST);
2017
2025
  gl.scissor(
2018
2026
  Math.floor(scaledPlot.x),
@@ -2252,6 +2260,7 @@ var ChartGridController = class {
2252
2260
  this.viewStates = /* @__PURE__ */ new Map();
2253
2261
  this.yScales = /* @__PURE__ */ new Map();
2254
2262
  this.yOffsets = /* @__PURE__ */ new Map();
2263
+ this.yBaseRanges = /* @__PURE__ */ new Map();
2255
2264
  this.latestX = /* @__PURE__ */ new Map();
2256
2265
  this.axisOverlays = {};
2257
2266
  this.layouts = [];
@@ -2302,7 +2311,7 @@ var ChartGridController = class {
2302
2311
  }
2303
2312
  initializeChartState() {
2304
2313
  const ids = new Set(this.options.charts.map((chart) => chart.id));
2305
- [this.viewStates, this.yScales, this.yOffsets, this.latestX].forEach((map) => {
2314
+ [this.viewStates, this.yScales, this.yOffsets, this.yBaseRanges, this.latestX].forEach((map) => {
2306
2315
  [...map.keys()].forEach((id) => {
2307
2316
  if (!ids.has(id)) map.delete(id);
2308
2317
  });
@@ -2489,6 +2498,7 @@ var ChartGridController = class {
2489
2498
  antialiasLines: this.options.antialiasLines,
2490
2499
  layouts,
2491
2500
  viewStateRef: { current: this.viewStates },
2501
+ yBaseRangeRef: { current: this.yBaseRanges },
2492
2502
  yScaleRef: { current: this.yScales },
2493
2503
  yCenterOffsetRef: { current: this.yOffsets },
2494
2504
  initialVisiblePoints: this.options.initialVisiblePoints,
@@ -2634,8 +2644,11 @@ var ChartGridController = class {
2634
2644
  (item) => item.id === crosshair.chartId
2635
2645
  );
2636
2646
  const xAxisLabel = chart?.xAxisLabel ?? this.options.xAxisLabel;
2647
+ const pointLayer = `<div data-crosshair-points class="pointer-events-none absolute z-30 overflow-hidden" style="left:${crosshair.plot.x}px;top:${crosshair.plot.y}px;width:${crosshair.plot.width}px;height:${crosshair.plot.height}px">
2648
+ ${crosshair.points.map((point) => `<div data-crosshair-point class="absolute size-2.5 -translate-x-1/2 -translate-y-1/2 rounded-full border border-background" style="left:${point.x - crosshair.plot.x}px;top:${point.y - crosshair.plot.y}px;background:${point.color}"></div>`).join("")}
2649
+ </div>`;
2637
2650
  return `<div class="pointer-events-none absolute z-20 border-l border-foreground/40" style="left:${crosshair.x}px;top:0;height:100%"></div><div class="pointer-events-none absolute z-20 border-t border-foreground/40" style="top:${crosshair.y}px;left:0;width:100%"></div>
2638
- ${crosshair.points.map((point) => `<div class="pointer-events-none absolute z-30 size-2.5 -translate-x-1/2 -translate-y-1/2 rounded-full border border-background" style="left:${point.x}px;top:${point.y}px;background:${point.color}"></div>`).join("")}
2651
+ ${pointLayer}
2639
2652
  <div data-crosshair-tooltip class="pointer-events-none absolute z-30 w-[220px] rounded-sm border border-border/70 bg-popover/80 px-2 py-1.5 text-xs text-popover-foreground shadow-sm backdrop-blur-sm" style="left:${crosshair.tooltipX}px;top:${crosshair.tooltipY}px"><div class="mb-1 font-medium">${escapeHtml(xAxisLabel)}: ${escapeHtml(crosshair.categoryLabel ?? this.options.formatXValue(crosshair.xValue))}</div>${crosshair.points.map((point) => `<div class="grid grid-cols-[auto_1fr] gap-x-2"><span class="mt-1 size-2 rounded-full" style="background:${point.color}"></span><div class="min-w-0"><div class="truncate text-muted-foreground">${escapeHtml(point.name)}</div><div class="tabular-nums">${escapeHtml(this.options.formatYValue(point.yValue))}</div></div></div>`).join("")}</div>`;
2640
2653
  }
2641
2654
  rectangleOverlayHtml() {
@@ -2666,22 +2679,42 @@ var ChartGridController = class {
2666
2679
  return this.getTransform(layout, state, range).dataToScreen(point);
2667
2680
  }
2668
2681
  getRange(layout, state = this.viewStates.get(layout.chart.id)) {
2669
- return applyYScale(
2670
- getYRange(
2682
+ const automaticRange = getYRange(
2683
+ layout.chart,
2684
+ state.xMin,
2685
+ state.xMax,
2686
+ getCategoryPixelLength(
2671
2687
  layout.chart,
2672
- state.xMin,
2673
- state.xMax,
2674
- getCategoryPixelLength(
2675
- layout.chart,
2676
- layout.plot,
2677
- layout.descriptor
2678
- ),
2688
+ layout.plot,
2679
2689
  layout.descriptor
2680
2690
  ),
2691
+ layout.descriptor
2692
+ );
2693
+ const lockedRange = this.yBaseRanges.get(layout.chart.id);
2694
+ return applyYScale(
2695
+ lockedRange ? { ...automaticRange, ...lockedRange } : automaticRange,
2681
2696
  this.yScales.get(layout.chart.id),
2682
2697
  this.yOffsets.get(layout.chart.id)
2683
2698
  );
2684
2699
  }
2700
+ lockYRange(layout, state = this.viewStates.get(layout.chart.id)) {
2701
+ if (this.yBaseRanges.has(layout.chart.id)) return;
2702
+ const range = getYRange(
2703
+ layout.chart,
2704
+ state.xMin,
2705
+ state.xMax,
2706
+ getCategoryPixelLength(
2707
+ layout.chart,
2708
+ layout.plot,
2709
+ layout.descriptor
2710
+ ),
2711
+ layout.descriptor
2712
+ );
2713
+ this.yBaseRanges.set(layout.chart.id, {
2714
+ minY: range.minY,
2715
+ maxY: range.maxY
2716
+ });
2717
+ }
2685
2718
  getTransform(layout, state, range) {
2686
2719
  return createCoordinateTransform({
2687
2720
  orientation: layout.descriptor.orientation,
@@ -2784,7 +2817,7 @@ var ChartGridController = class {
2784
2817
  this.focusedChartId = layout.chart.id;
2785
2818
  const inPlot = point.x >= layout.plot.x && point.x <= layout.plot.x + layout.plot.width && point.y >= layout.plot.y && point.y <= layout.plot.y + layout.plot.height;
2786
2819
  if (inPlot && this.activeDrawingTool && !this.options.disableDrawings && layout.descriptor.capabilities.drawings) {
2787
- const dataPoint = screenPointToDataPoint({ point, chart: layout.chart, descriptor: layout.descriptor, plot: layout.plot, initialVisiblePoints: this.options.initialVisiblePoints, viewStateRef: { current: this.viewStates }, yScaleRef: { current: this.yScales }, yCenterOffsetRef: { current: this.yOffsets } });
2820
+ const dataPoint = screenPointToDataPoint({ point, chart: layout.chart, descriptor: layout.descriptor, plot: layout.plot, initialVisiblePoints: this.options.initialVisiblePoints, viewStateRef: { current: this.viewStates }, yBaseRangeRef: { current: this.yBaseRanges }, yScaleRef: { current: this.yScales }, yCenterOffsetRef: { current: this.yOffsets } });
2788
2821
  if (["hline", "vline", "pin"].includes(this.activeDrawingTool)) {
2789
2822
  this.commitDrawing(layout.chart.id, this.activeDrawingTool, dataPoint, dataPoint);
2790
2823
  } else if (this.drawingSession?.chartId === layout.chart.id) {
@@ -2807,7 +2840,17 @@ var ChartGridController = class {
2807
2840
  } else {
2808
2841
  this.selectedDrawingId = null;
2809
2842
  this.emit("onSelectedDrawingIdChange", null);
2810
- this.drag = { type: "pan", layout, start: point, state: { ...this.viewStates.get(layout.chart.id) }, yOffset: this.yOffsets.get(layout.chart.id) || 0 };
2843
+ const state = { ...this.viewStates.get(layout.chart.id) };
2844
+ const range = this.getRange(layout, state);
2845
+ this.drag = {
2846
+ type: "pan",
2847
+ layout,
2848
+ start: point,
2849
+ state,
2850
+ yOffset: this.yOffsets.get(layout.chart.id) || 0,
2851
+ canPanY: this.yBaseRanges.has(layout.chart.id),
2852
+ valueSpan: range.maxY - range.minY
2853
+ };
2811
2854
  }
2812
2855
  } else if (layout.descriptor.orientation === "vertical" && point.x >= layout.plot.x + layout.plot.width) {
2813
2856
  this.drag = { type: "y-scale", layout, start: point, scale: this.yScales.get(layout.chart.id) || 1 };
@@ -2829,7 +2872,7 @@ var ChartGridController = class {
2829
2872
  const { layout, point } = context;
2830
2873
  if (this.drawingSession) {
2831
2874
  if (layout.chart.id !== this.drawingSession.chartId) return;
2832
- const end = screenPointToDataPoint({ point, chart: layout.chart, descriptor: layout.descriptor, plot: layout.plot, initialVisiblePoints: this.options.initialVisiblePoints, viewStateRef: { current: this.viewStates }, yScaleRef: { current: this.yScales }, yCenterOffsetRef: { current: this.yOffsets } });
2875
+ const end = screenPointToDataPoint({ point, chart: layout.chart, descriptor: layout.descriptor, plot: layout.plot, initialVisiblePoints: this.options.initialVisiblePoints, viewStateRef: { current: this.viewStates }, yBaseRangeRef: { current: this.yBaseRanges }, yScaleRef: { current: this.yScales }, yCenterOffsetRef: { current: this.yOffsets } });
2833
2876
  this.draftDrawing = createDraftDrawing({ ...this.drawingSession, end });
2834
2877
  this.requestRender();
2835
2878
  return;
@@ -2837,7 +2880,7 @@ var ChartGridController = class {
2837
2880
  if (this.drag) {
2838
2881
  const { chart, descriptor, plot } = this.drag.layout;
2839
2882
  if (this.drag.type === "drawing-edit") {
2840
- const dataPoint = screenPointToDataPoint({ point, chart, descriptor, plot, initialVisiblePoints: this.options.initialVisiblePoints, viewStateRef: { current: this.viewStates }, yScaleRef: { current: this.yScales }, yCenterOffsetRef: { current: this.yOffsets } });
2883
+ const dataPoint = screenPointToDataPoint({ point, chart, descriptor, plot, initialVisiblePoints: this.options.initialVisiblePoints, viewStateRef: { current: this.viewStates }, yBaseRangeRef: { current: this.yBaseRanges }, yScaleRef: { current: this.yScales }, yCenterOffsetRef: { current: this.yOffsets } });
2841
2884
  const next = updateDrawingById(this.drawings, this.drag.drawingId, (drawing) => {
2842
2885
  if (drawing.type === "hline") {
2843
2886
  const deltaX = (drawing.end?.x ?? drawing.start.x) - drawing.start.x;
@@ -2855,22 +2898,13 @@ var ChartGridController = class {
2855
2898
  }
2856
2899
  if (this.drag.type === "rectangle") this.rectangleZoomRect = { start: this.drag.start, end: point };
2857
2900
  if (this.drag.type === "pan") {
2858
- const span = this.drag.state.xMax - this.drag.state.xMin;
2859
- const baseRange = getYRange(
2860
- chart,
2861
- this.drag.state.xMin,
2862
- this.drag.state.xMax,
2863
- getCategoryPixelLength(chart, plot, descriptor),
2864
- descriptor
2865
- );
2866
- const rangeSpan = (baseRange.maxY - baseRange.minY) * (this.yScales.get(chart.id) || 1);
2867
2901
  const transform = createCoordinateTransform({
2868
2902
  orientation: descriptor.orientation,
2869
2903
  categoryRange: {
2870
2904
  min: this.drag.state.xMin,
2871
2905
  max: this.drag.state.xMax
2872
2906
  },
2873
- valueRange: { min: 0, max: rangeSpan },
2907
+ valueRange: { min: 0, max: this.drag.valueSpan },
2874
2908
  plot
2875
2909
  });
2876
2910
  const categoryDelta = transform.categoryDragDelta(
@@ -2882,7 +2916,9 @@ var ChartGridController = class {
2882
2916
  point
2883
2917
  );
2884
2918
  this.viewStates.set(chart.id, { xMin: this.drag.state.xMin - categoryDelta, xMax: this.drag.state.xMax - categoryDelta });
2885
- this.yOffsets.set(chart.id, this.drag.yOffset + valueDelta);
2919
+ if (this.drag.canPanY) {
2920
+ this.yOffsets.set(chart.id, this.drag.yOffset + valueDelta);
2921
+ }
2886
2922
  }
2887
2923
  if (this.drag.type === "y-scale") {
2888
2924
  const transform = createCoordinateTransform({
@@ -2892,6 +2928,7 @@ var ChartGridController = class {
2892
2928
  plot
2893
2929
  });
2894
2930
  const delta = transform.valueScaleDelta(this.drag.start, point);
2931
+ if (delta !== 0) this.lockYRange(this.drag.layout);
2895
2932
  this.yScales.set(
2896
2933
  chart.id,
2897
2934
  scaleValueRange(
@@ -2934,7 +2971,7 @@ var ChartGridController = class {
2934
2971
  const normalized = normalizeRect(this.rectangleZoomRect.start, this.rectangleZoomRect.end);
2935
2972
  if (normalized?.width >= 4 && normalized?.height >= 4) {
2936
2973
  const layout = this.drag.layout;
2937
- applyRectangleZoom({
2974
+ const zoomed = applyRectangleZoom({
2938
2975
  chart: layout.chart,
2939
2976
  descriptor: layout.descriptor,
2940
2977
  plot: layout.plot,
@@ -2942,10 +2979,12 @@ var ChartGridController = class {
2942
2979
  end: this.rectangleZoomRect.end,
2943
2980
  initialVisiblePoints: this.options.initialVisiblePoints,
2944
2981
  viewStateRef: { current: this.viewStates },
2982
+ yBaseRangeRef: { current: this.yBaseRanges },
2945
2983
  yScaleRef: { current: this.yScales },
2946
2984
  yCenterOffsetRef: { current: this.yOffsets },
2947
2985
  yManualScaleRef: { current: /* @__PURE__ */ new Set() }
2948
2986
  });
2987
+ if (zoomed) this.lockYRange(layout);
2949
2988
  }
2950
2989
  this.rectangleZoomChartId = null;
2951
2990
  this.rectangleZoomRect = null;
@@ -2966,6 +3005,7 @@ var ChartGridController = class {
2966
3005
  if (event.shiftKey) {
2967
3006
  const wheelDelta = event.deltaY || event.deltaX;
2968
3007
  const currentScale = this.yScales.get(layout.chart.id) || 1;
3008
+ if (wheelDelta !== 0) this.lockYRange(layout);
2969
3009
  this.yScales.set(
2970
3010
  layout.chart.id,
2971
3011
  scaleValueRange(
@@ -3000,7 +3040,7 @@ var ChartGridController = class {
3000
3040
  event.preventDefault();
3001
3041
  const { layout, point } = context;
3002
3042
  const inPlot = point.x >= layout.plot.x && point.x <= layout.plot.x + layout.plot.width && point.y >= layout.plot.y && point.y <= layout.plot.y + layout.plot.height;
3003
- const data = inPlot ? screenPointToDataPoint({ point, chart: layout.chart, descriptor: layout.descriptor, plot: layout.plot, initialVisiblePoints: this.options.initialVisiblePoints, viewStateRef: { current: this.viewStates }, yScaleRef: { current: this.yScales }, yCenterOffsetRef: { current: this.yOffsets } }) : null;
3043
+ const data = inPlot ? screenPointToDataPoint({ point, chart: layout.chart, descriptor: layout.descriptor, plot: layout.plot, initialVisiblePoints: this.options.initialVisiblePoints, viewStateRef: { current: this.viewStates }, yBaseRangeRef: { current: this.yBaseRanges }, yScaleRef: { current: this.yScales }, yCenterOffsetRef: { current: this.yOffsets } }) : null;
3004
3044
  this.options.onChartContextMenu({ chart: layout.chart, event, point: data });
3005
3045
  }
3006
3046
  updateCrosshair(point, layout) {
@@ -3047,6 +3087,7 @@ var ChartGridController = class {
3047
3087
  xValue: nearest.xValue,
3048
3088
  categoryLabel: getCategoryLabel(layout.chart, nearest.xValue),
3049
3089
  points,
3090
+ plot: { ...layout.plot },
3050
3091
  tooltipX: point.x + 232 > layout.rect.x + layout.rect.width ? point.x - 232 : point.x + 12,
3051
3092
  tooltipY: clamp(point.y + 12, layout.plot.y, layout.rect.y + layout.rect.height - 96)
3052
3093
  };
@@ -3114,6 +3155,7 @@ var ChartGridController = class {
3114
3155
  this.viewStates.set(chartId, getInitialView(chart, this.options.initialVisiblePoints));
3115
3156
  this.yScales.set(chartId, 1);
3116
3157
  this.yOffsets.set(chartId, 0);
3158
+ this.yBaseRanges.delete(chartId);
3117
3159
  this.requestRender();
3118
3160
  }
3119
3161
  openFullscreen(chartId) {