hyperprop-charting-library 0.1.112 → 0.1.114

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.
@@ -1035,6 +1035,7 @@ function createChart(element, options = {}) {
1035
1035
  let magnetModifierActive = false;
1036
1036
  let shiftKeyActive = false;
1037
1037
  const MAGNET_WEAK_THRESHOLD_PX = 16;
1038
+ const MAGNET_STRONG_THRESHOLD_PX = 48;
1038
1039
  let drawingHoverHandler = null;
1039
1040
  let lastHoveredDrawingId = null;
1040
1041
  let selectedDrawingId = null;
@@ -3942,7 +3943,8 @@ function createChart(element, options = {}) {
3942
3943
  nearest = candidate;
3943
3944
  }
3944
3945
  }
3945
- if (effective === "weak" && Math.abs(canvasYFromDrawingPrice(nearest) - y) > MAGNET_WEAK_THRESHOLD_PX) {
3946
+ const snapThresholdPx = effective === "weak" ? MAGNET_WEAK_THRESHOLD_PX : MAGNET_STRONG_THRESHOLD_PX;
3947
+ if (Math.abs(canvasYFromDrawingPrice(nearest) - y) > snapThresholdPx) {
3946
3948
  return { index, price };
3947
3949
  }
3948
3950
  return { index: barIndex, price: nearest };
@@ -5196,6 +5198,50 @@ function createChart(element, options = {}) {
5196
5198
  }
5197
5199
  scheduleDraw();
5198
5200
  };
5201
+ const upsertBar = (point) => {
5202
+ const time = new Date(point.t);
5203
+ const timeMs = time.getTime();
5204
+ const open = Number(point.o);
5205
+ const close = Number(point.c);
5206
+ const highInput = Number(point.h);
5207
+ const lowInput = Number(point.l);
5208
+ if (!Number.isFinite(timeMs) || !Number.isFinite(open) || !Number.isFinite(close) || !Number.isFinite(highInput) || !Number.isFinite(lowInput)) {
5209
+ return;
5210
+ }
5211
+ const volumeValue = point.v === void 0 ? void 0 : Number(point.v);
5212
+ const parsed = {
5213
+ time,
5214
+ o: open,
5215
+ h: Math.max(highInput, open, close),
5216
+ l: Math.min(lowInput, open, close),
5217
+ c: close,
5218
+ ...volumeValue === void 0 || !Number.isFinite(volumeValue) ? {} : { v: volumeValue }
5219
+ };
5220
+ const last = data[data.length - 1];
5221
+ if (!last) {
5222
+ setData([point]);
5223
+ return;
5224
+ }
5225
+ const lastMs = last.time.getTime();
5226
+ if (timeMs === lastMs) {
5227
+ data[data.length - 1] = parsed;
5228
+ } else if (timeMs > lastMs) {
5229
+ data.push(parsed);
5230
+ if (mergedOptions.preserveViewportOnDataUpdate && followLatest) {
5231
+ xCenter = data.length - xSpan / 2 + rightEdgePaddingBars;
5232
+ clampXViewport();
5233
+ }
5234
+ } else {
5235
+ const merged = /* @__PURE__ */ new Map();
5236
+ for (const existing of data) {
5237
+ merged.set(existing.time.getTime(), existing);
5238
+ }
5239
+ merged.set(timeMs, parsed);
5240
+ data = Array.from(merged.values()).sort((a, b) => a.time.getTime() - b.time.getTime());
5241
+ }
5242
+ pushSmoothedTicker(parsed.c, parsed.v);
5243
+ scheduleDraw();
5244
+ };
5199
5245
  const setPriceLines = (lines) => {
5200
5246
  priceLines = lines.map((line, index) => ({
5201
5247
  ...line,
@@ -5451,6 +5497,7 @@ function createChart(element, options = {}) {
5451
5497
  return {
5452
5498
  updateOptions,
5453
5499
  setData,
5500
+ upsertBar,
5454
5501
  setPriceLines,
5455
5502
  addPriceLine,
5456
5503
  removePriceLine,
@@ -412,6 +412,13 @@ interface LabelsOptions {
412
412
  interface ChartInstance {
413
413
  updateOptions: (options: ChartOptions) => void;
414
414
  setData: (data: OhlcDataPoint[]) => void;
415
+ /**
416
+ * Incrementally upsert a single bar: O(1) update-in-place for the live
417
+ * (forming) bar and O(1) append for a new bar, instead of re-ingesting the
418
+ * full series through `setData`. Out-of-order timestamps fall back to a
419
+ * full merge, so correctness never depends on call ordering.
420
+ */
421
+ upsertBar: (point: OhlcDataPoint) => void;
415
422
  setPriceLines: (lines: PriceLineOptions[]) => void;
416
423
  addPriceLine: (line: PriceLineOptions) => string;
417
424
  removePriceLine: (id: string) => void;
@@ -1009,6 +1009,7 @@ function createChart(element, options = {}) {
1009
1009
  let magnetModifierActive = false;
1010
1010
  let shiftKeyActive = false;
1011
1011
  const MAGNET_WEAK_THRESHOLD_PX = 16;
1012
+ const MAGNET_STRONG_THRESHOLD_PX = 48;
1012
1013
  let drawingHoverHandler = null;
1013
1014
  let lastHoveredDrawingId = null;
1014
1015
  let selectedDrawingId = null;
@@ -3916,7 +3917,8 @@ function createChart(element, options = {}) {
3916
3917
  nearest = candidate;
3917
3918
  }
3918
3919
  }
3919
- if (effective === "weak" && Math.abs(canvasYFromDrawingPrice(nearest) - y) > MAGNET_WEAK_THRESHOLD_PX) {
3920
+ const snapThresholdPx = effective === "weak" ? MAGNET_WEAK_THRESHOLD_PX : MAGNET_STRONG_THRESHOLD_PX;
3921
+ if (Math.abs(canvasYFromDrawingPrice(nearest) - y) > snapThresholdPx) {
3920
3922
  return { index, price };
3921
3923
  }
3922
3924
  return { index: barIndex, price: nearest };
@@ -5170,6 +5172,50 @@ function createChart(element, options = {}) {
5170
5172
  }
5171
5173
  scheduleDraw();
5172
5174
  };
5175
+ const upsertBar = (point) => {
5176
+ const time = new Date(point.t);
5177
+ const timeMs = time.getTime();
5178
+ const open = Number(point.o);
5179
+ const close = Number(point.c);
5180
+ const highInput = Number(point.h);
5181
+ const lowInput = Number(point.l);
5182
+ if (!Number.isFinite(timeMs) || !Number.isFinite(open) || !Number.isFinite(close) || !Number.isFinite(highInput) || !Number.isFinite(lowInput)) {
5183
+ return;
5184
+ }
5185
+ const volumeValue = point.v === void 0 ? void 0 : Number(point.v);
5186
+ const parsed = {
5187
+ time,
5188
+ o: open,
5189
+ h: Math.max(highInput, open, close),
5190
+ l: Math.min(lowInput, open, close),
5191
+ c: close,
5192
+ ...volumeValue === void 0 || !Number.isFinite(volumeValue) ? {} : { v: volumeValue }
5193
+ };
5194
+ const last = data[data.length - 1];
5195
+ if (!last) {
5196
+ setData([point]);
5197
+ return;
5198
+ }
5199
+ const lastMs = last.time.getTime();
5200
+ if (timeMs === lastMs) {
5201
+ data[data.length - 1] = parsed;
5202
+ } else if (timeMs > lastMs) {
5203
+ data.push(parsed);
5204
+ if (mergedOptions.preserveViewportOnDataUpdate && followLatest) {
5205
+ xCenter = data.length - xSpan / 2 + rightEdgePaddingBars;
5206
+ clampXViewport();
5207
+ }
5208
+ } else {
5209
+ const merged = /* @__PURE__ */ new Map();
5210
+ for (const existing of data) {
5211
+ merged.set(existing.time.getTime(), existing);
5212
+ }
5213
+ merged.set(timeMs, parsed);
5214
+ data = Array.from(merged.values()).sort((a, b) => a.time.getTime() - b.time.getTime());
5215
+ }
5216
+ pushSmoothedTicker(parsed.c, parsed.v);
5217
+ scheduleDraw();
5218
+ };
5173
5219
  const setPriceLines = (lines) => {
5174
5220
  priceLines = lines.map((line, index) => ({
5175
5221
  ...line,
@@ -5425,6 +5471,7 @@ function createChart(element, options = {}) {
5425
5471
  return {
5426
5472
  updateOptions,
5427
5473
  setData,
5474
+ upsertBar,
5428
5475
  setPriceLines,
5429
5476
  addPriceLine,
5430
5477
  removePriceLine,
package/dist/index.cjs CHANGED
@@ -1035,6 +1035,7 @@ function createChart(element, options = {}) {
1035
1035
  let magnetModifierActive = false;
1036
1036
  let shiftKeyActive = false;
1037
1037
  const MAGNET_WEAK_THRESHOLD_PX = 16;
1038
+ const MAGNET_STRONG_THRESHOLD_PX = 48;
1038
1039
  let drawingHoverHandler = null;
1039
1040
  let lastHoveredDrawingId = null;
1040
1041
  let selectedDrawingId = null;
@@ -3942,7 +3943,8 @@ function createChart(element, options = {}) {
3942
3943
  nearest = candidate;
3943
3944
  }
3944
3945
  }
3945
- if (effective === "weak" && Math.abs(canvasYFromDrawingPrice(nearest) - y) > MAGNET_WEAK_THRESHOLD_PX) {
3946
+ const snapThresholdPx = effective === "weak" ? MAGNET_WEAK_THRESHOLD_PX : MAGNET_STRONG_THRESHOLD_PX;
3947
+ if (Math.abs(canvasYFromDrawingPrice(nearest) - y) > snapThresholdPx) {
3946
3948
  return { index, price };
3947
3949
  }
3948
3950
  return { index: barIndex, price: nearest };
@@ -5196,6 +5198,50 @@ function createChart(element, options = {}) {
5196
5198
  }
5197
5199
  scheduleDraw();
5198
5200
  };
5201
+ const upsertBar = (point) => {
5202
+ const time = new Date(point.t);
5203
+ const timeMs = time.getTime();
5204
+ const open = Number(point.o);
5205
+ const close = Number(point.c);
5206
+ const highInput = Number(point.h);
5207
+ const lowInput = Number(point.l);
5208
+ if (!Number.isFinite(timeMs) || !Number.isFinite(open) || !Number.isFinite(close) || !Number.isFinite(highInput) || !Number.isFinite(lowInput)) {
5209
+ return;
5210
+ }
5211
+ const volumeValue = point.v === void 0 ? void 0 : Number(point.v);
5212
+ const parsed = {
5213
+ time,
5214
+ o: open,
5215
+ h: Math.max(highInput, open, close),
5216
+ l: Math.min(lowInput, open, close),
5217
+ c: close,
5218
+ ...volumeValue === void 0 || !Number.isFinite(volumeValue) ? {} : { v: volumeValue }
5219
+ };
5220
+ const last = data[data.length - 1];
5221
+ if (!last) {
5222
+ setData([point]);
5223
+ return;
5224
+ }
5225
+ const lastMs = last.time.getTime();
5226
+ if (timeMs === lastMs) {
5227
+ data[data.length - 1] = parsed;
5228
+ } else if (timeMs > lastMs) {
5229
+ data.push(parsed);
5230
+ if (mergedOptions.preserveViewportOnDataUpdate && followLatest) {
5231
+ xCenter = data.length - xSpan / 2 + rightEdgePaddingBars;
5232
+ clampXViewport();
5233
+ }
5234
+ } else {
5235
+ const merged = /* @__PURE__ */ new Map();
5236
+ for (const existing of data) {
5237
+ merged.set(existing.time.getTime(), existing);
5238
+ }
5239
+ merged.set(timeMs, parsed);
5240
+ data = Array.from(merged.values()).sort((a, b) => a.time.getTime() - b.time.getTime());
5241
+ }
5242
+ pushSmoothedTicker(parsed.c, parsed.v);
5243
+ scheduleDraw();
5244
+ };
5199
5245
  const setPriceLines = (lines) => {
5200
5246
  priceLines = lines.map((line, index) => ({
5201
5247
  ...line,
@@ -5451,6 +5497,7 @@ function createChart(element, options = {}) {
5451
5497
  return {
5452
5498
  updateOptions,
5453
5499
  setData,
5500
+ upsertBar,
5454
5501
  setPriceLines,
5455
5502
  addPriceLine,
5456
5503
  removePriceLine,
package/dist/index.d.cts CHANGED
@@ -412,6 +412,13 @@ interface LabelsOptions {
412
412
  interface ChartInstance {
413
413
  updateOptions: (options: ChartOptions) => void;
414
414
  setData: (data: OhlcDataPoint[]) => void;
415
+ /**
416
+ * Incrementally upsert a single bar: O(1) update-in-place for the live
417
+ * (forming) bar and O(1) append for a new bar, instead of re-ingesting the
418
+ * full series through `setData`. Out-of-order timestamps fall back to a
419
+ * full merge, so correctness never depends on call ordering.
420
+ */
421
+ upsertBar: (point: OhlcDataPoint) => void;
415
422
  setPriceLines: (lines: PriceLineOptions[]) => void;
416
423
  addPriceLine: (line: PriceLineOptions) => string;
417
424
  removePriceLine: (id: string) => void;
package/dist/index.d.ts CHANGED
@@ -412,6 +412,13 @@ interface LabelsOptions {
412
412
  interface ChartInstance {
413
413
  updateOptions: (options: ChartOptions) => void;
414
414
  setData: (data: OhlcDataPoint[]) => void;
415
+ /**
416
+ * Incrementally upsert a single bar: O(1) update-in-place for the live
417
+ * (forming) bar and O(1) append for a new bar, instead of re-ingesting the
418
+ * full series through `setData`. Out-of-order timestamps fall back to a
419
+ * full merge, so correctness never depends on call ordering.
420
+ */
421
+ upsertBar: (point: OhlcDataPoint) => void;
415
422
  setPriceLines: (lines: PriceLineOptions[]) => void;
416
423
  addPriceLine: (line: PriceLineOptions) => string;
417
424
  removePriceLine: (id: string) => void;
package/dist/index.js CHANGED
@@ -1009,6 +1009,7 @@ function createChart(element, options = {}) {
1009
1009
  let magnetModifierActive = false;
1010
1010
  let shiftKeyActive = false;
1011
1011
  const MAGNET_WEAK_THRESHOLD_PX = 16;
1012
+ const MAGNET_STRONG_THRESHOLD_PX = 48;
1012
1013
  let drawingHoverHandler = null;
1013
1014
  let lastHoveredDrawingId = null;
1014
1015
  let selectedDrawingId = null;
@@ -3916,7 +3917,8 @@ function createChart(element, options = {}) {
3916
3917
  nearest = candidate;
3917
3918
  }
3918
3919
  }
3919
- if (effective === "weak" && Math.abs(canvasYFromDrawingPrice(nearest) - y) > MAGNET_WEAK_THRESHOLD_PX) {
3920
+ const snapThresholdPx = effective === "weak" ? MAGNET_WEAK_THRESHOLD_PX : MAGNET_STRONG_THRESHOLD_PX;
3921
+ if (Math.abs(canvasYFromDrawingPrice(nearest) - y) > snapThresholdPx) {
3920
3922
  return { index, price };
3921
3923
  }
3922
3924
  return { index: barIndex, price: nearest };
@@ -5170,6 +5172,50 @@ function createChart(element, options = {}) {
5170
5172
  }
5171
5173
  scheduleDraw();
5172
5174
  };
5175
+ const upsertBar = (point) => {
5176
+ const time = new Date(point.t);
5177
+ const timeMs = time.getTime();
5178
+ const open = Number(point.o);
5179
+ const close = Number(point.c);
5180
+ const highInput = Number(point.h);
5181
+ const lowInput = Number(point.l);
5182
+ if (!Number.isFinite(timeMs) || !Number.isFinite(open) || !Number.isFinite(close) || !Number.isFinite(highInput) || !Number.isFinite(lowInput)) {
5183
+ return;
5184
+ }
5185
+ const volumeValue = point.v === void 0 ? void 0 : Number(point.v);
5186
+ const parsed = {
5187
+ time,
5188
+ o: open,
5189
+ h: Math.max(highInput, open, close),
5190
+ l: Math.min(lowInput, open, close),
5191
+ c: close,
5192
+ ...volumeValue === void 0 || !Number.isFinite(volumeValue) ? {} : { v: volumeValue }
5193
+ };
5194
+ const last = data[data.length - 1];
5195
+ if (!last) {
5196
+ setData([point]);
5197
+ return;
5198
+ }
5199
+ const lastMs = last.time.getTime();
5200
+ if (timeMs === lastMs) {
5201
+ data[data.length - 1] = parsed;
5202
+ } else if (timeMs > lastMs) {
5203
+ data.push(parsed);
5204
+ if (mergedOptions.preserveViewportOnDataUpdate && followLatest) {
5205
+ xCenter = data.length - xSpan / 2 + rightEdgePaddingBars;
5206
+ clampXViewport();
5207
+ }
5208
+ } else {
5209
+ const merged = /* @__PURE__ */ new Map();
5210
+ for (const existing of data) {
5211
+ merged.set(existing.time.getTime(), existing);
5212
+ }
5213
+ merged.set(timeMs, parsed);
5214
+ data = Array.from(merged.values()).sort((a, b) => a.time.getTime() - b.time.getTime());
5215
+ }
5216
+ pushSmoothedTicker(parsed.c, parsed.v);
5217
+ scheduleDraw();
5218
+ };
5173
5219
  const setPriceLines = (lines) => {
5174
5220
  priceLines = lines.map((line, index) => ({
5175
5221
  ...line,
@@ -5425,6 +5471,7 @@ function createChart(element, options = {}) {
5425
5471
  return {
5426
5472
  updateOptions,
5427
5473
  setData,
5474
+ upsertBar,
5428
5475
  setPriceLines,
5429
5476
  addPriceLine,
5430
5477
  removePriceLine,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperprop-charting-library",
3
- "version": "0.1.112",
3
+ "version": "0.1.114",
4
4
  "description": "Lightweight TypeScript charting core",
5
5
  "type": "module",
6
6
  "main": "./dist/hyperprop-charting-library.cjs",