hyperprop-charting-library 0.1.11 → 0.1.13

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,14 @@ var DEFAULT_WATERMARK_OPTIONS = {
44
44
  imageTintColor: "",
45
45
  imageTintOpacity: 1
46
46
  };
47
+ var DEFAULT_DASH_PATTERNS = {
48
+ dotted: [2, 2],
49
+ dashed: [8, 6],
50
+ connectorDotted: [2, 3],
51
+ connectorDashed: [6, 5],
52
+ borderDotted: [2, 2],
53
+ borderDashed: [6, 4]
54
+ };
47
55
  var DEFAULT_PRICE_LINE_OPTIONS = {
48
56
  visible: true,
49
57
  style: "solid",
@@ -94,6 +102,9 @@ var DEFAULT_OPTIONS = {
94
102
  priceDecimals: 2,
95
103
  initialViewport: "latest",
96
104
  initialVisibleBars: 60,
105
+ minVisibleBars: 5,
106
+ maxVisibleBars: 2e4,
107
+ maxPanBars: 1e6,
97
108
  rightEdgePaddingBars: 2,
98
109
  preserveViewportOnDataUpdate: true,
99
110
  upColor: "#2fb171",
@@ -120,8 +131,13 @@ var DEFAULT_OPTIONS = {
120
131
  labelBackgroundColor: "#38bdf8",
121
132
  labelTextColor: "#0b1220",
122
133
  labelBorderRadius: 3
123
- }
134
+ },
135
+ dashPatterns: DEFAULT_DASH_PATTERNS
124
136
  };
137
+ var BRAND_LOGO_VIEWBOX_WIDTH = 190;
138
+ var BRAND_LOGO_VIEWBOX_HEIGHT = 186;
139
+ var BRAND_LOGO_PATH_A = "M0 93.0171V45.2271H48.9851V75.0332C48.9851 84.9545 57.0416 93.0001 66.9763 93.0001H94.9957V186H49.0531V110.984C49.0531 101.063 40.9965 93.0171 31.0619 93.0171H0Z";
140
+ var BRAND_LOGO_PATH_B = "M190 92.9915V140.782H141.015V110.975C141.015 101.054 132.958 93.0085 123.023 93.0085H95.0039V0H140.955V75.0162C140.955 84.9374 149.012 92.9831 158.946 92.9831H190V92.9915Z";
125
141
  function createChart(element, options = {}) {
126
142
  const mergedOptions = {
127
143
  ...DEFAULT_OPTIONS,
@@ -146,6 +162,10 @@ function createChart(element, options = {}) {
146
162
  tickerLine: {
147
163
  ...DEFAULT_OPTIONS.tickerLine,
148
164
  ...options.tickerLine
165
+ },
166
+ dashPatterns: {
167
+ ...DEFAULT_DASH_PATTERNS,
168
+ ...options.dashPatterns ?? {}
149
169
  }
150
170
  };
151
171
  let width = mergedOptions.width;
@@ -174,6 +194,8 @@ function createChart(element, options = {}) {
174
194
  let yMaxOverride = null;
175
195
  let autoYMin = null;
176
196
  let autoYMax = null;
197
+ const brandLogoPathA = new Path2D(BRAND_LOGO_PATH_A);
198
+ const brandLogoPathB = new Path2D(BRAND_LOGO_PATH_B);
177
199
  let watermarkImageSrc = null;
178
200
  let watermarkImage = null;
179
201
  let watermarkImageReady = false;
@@ -194,7 +216,9 @@ function createChart(element, options = {}) {
194
216
  element.innerHTML = "";
195
217
  element.appendChild(canvas);
196
218
  const margin = { top: 16, right: 72, bottom: 34, left: 12 };
197
- const maxPanBars = 1e6;
219
+ const minVisibleBars = Math.max(1, Math.floor(mergedOptions.minVisibleBars));
220
+ const maxVisibleBars = Math.max(minVisibleBars, Math.floor(mergedOptions.maxVisibleBars));
221
+ const maxPanBars = Math.max(0, Math.floor(mergedOptions.maxPanBars));
198
222
  const rightEdgePaddingBars = Math.max(0, mergedOptions.rightEdgePaddingBars);
199
223
  const getPixelRatio = () => {
200
224
  if (typeof window === "undefined") {
@@ -229,6 +253,23 @@ function createChart(element, options = {}) {
229
253
  const clamp = (value, min, max) => {
230
254
  return Math.min(max, Math.max(min, value));
231
255
  };
256
+ const dashPatterns = {
257
+ dotted: mergedOptions.dashPatterns.dotted ?? DEFAULT_DASH_PATTERNS.dotted,
258
+ dashed: mergedOptions.dashPatterns.dashed ?? DEFAULT_DASH_PATTERNS.dashed,
259
+ connectorDotted: mergedOptions.dashPatterns.connectorDotted ?? DEFAULT_DASH_PATTERNS.connectorDotted,
260
+ connectorDashed: mergedOptions.dashPatterns.connectorDashed ?? DEFAULT_DASH_PATTERNS.connectorDashed,
261
+ borderDotted: mergedOptions.dashPatterns.borderDotted ?? DEFAULT_DASH_PATTERNS.borderDotted,
262
+ borderDashed: mergedOptions.dashPatterns.borderDashed ?? DEFAULT_DASH_PATTERNS.borderDashed
263
+ };
264
+ const applyDashPattern = (style, dotted, dashed) => {
265
+ if (style === "dotted") {
266
+ ctx.setLineDash(dotted);
267
+ } else if (style === "dashed") {
268
+ ctx.setLineDash(dashed);
269
+ } else {
270
+ ctx.setLineDash([]);
271
+ }
272
+ };
232
273
  const clampXViewport = () => {
233
274
  const count = data.length;
234
275
  if (count === 0) {
@@ -236,8 +277,8 @@ function createChart(element, options = {}) {
236
277
  xSpan = 60;
237
278
  return;
238
279
  }
239
- const minSpan = 5;
240
- const maxSpan = Math.max(minSpan, count + maxPanBars * 2);
280
+ const minSpan = minVisibleBars;
281
+ const maxSpan = Math.min(maxVisibleBars, Math.max(minSpan, count + maxPanBars * 2));
241
282
  xSpan = clamp(xSpan, minSpan, maxSpan);
242
283
  xCenter = clamp(xCenter, -maxPanBars, count + maxPanBars);
243
284
  };
@@ -248,8 +289,9 @@ function createChart(element, options = {}) {
248
289
  xSpan = 60;
249
290
  return;
250
291
  }
251
- const requestedVisibleBars = Math.max(5, Math.floor(mergedOptions.initialVisibleBars));
252
- xSpan = Math.min(requestedVisibleBars, Math.max(5, count));
292
+ const maxSpan = Math.min(maxVisibleBars, Math.max(minVisibleBars, count + maxPanBars * 2));
293
+ const requestedVisibleBars = clamp(Math.floor(mergedOptions.initialVisibleBars), minVisibleBars, maxSpan);
294
+ xSpan = requestedVisibleBars;
253
295
  if (mergedOptions.initialViewport === "center") {
254
296
  xCenter = count / 2;
255
297
  } else {
@@ -401,13 +443,7 @@ function createChart(element, options = {}) {
401
443
  ctx.save();
402
444
  ctx.strokeStyle = color;
403
445
  ctx.lineWidth = Math.max(1, mergedLine.thickness);
404
- if (mergedLine.style === "dotted") {
405
- ctx.setLineDash([2, 4]);
406
- } else if (mergedLine.style === "dashed") {
407
- ctx.setLineDash([8, 6]);
408
- } else {
409
- ctx.setLineDash([]);
410
- }
446
+ applyDashPattern(mergedLine.style, dashPatterns.dotted, dashPatterns.dashed);
411
447
  ctx.beginPath();
412
448
  ctx.moveTo(crisp(chartLeft), crisp(lineY));
413
449
  ctx.lineTo(crisp(chartRight), crisp(lineY));
@@ -461,13 +497,7 @@ function createChart(element, options = {}) {
461
497
  ctx.save();
462
498
  ctx.strokeStyle = color;
463
499
  ctx.lineWidth = Math.max(1, mergedLine.thickness);
464
- if (mergedLine.style === "dotted") {
465
- ctx.setLineDash([2, 4]);
466
- } else if (mergedLine.style === "dashed") {
467
- ctx.setLineDash([8, 6]);
468
- } else {
469
- ctx.setLineDash([]);
470
- }
500
+ applyDashPattern(mergedLine.style, dashPatterns.dotted, dashPatterns.dashed);
471
501
  ctx.beginPath();
472
502
  ctx.moveTo(crisp(chartLeft), crisp(lineY));
473
503
  ctx.lineTo(crisp(chartRight), crisp(lineY));
@@ -479,13 +509,11 @@ function createChart(element, options = {}) {
479
509
  ctx.save();
480
510
  ctx.strokeStyle = mergedLine.connectorColor ?? color;
481
511
  ctx.lineWidth = Math.max(1, mergedLine.connectorThickness);
482
- if (mergedLine.connectorStyle === "dotted") {
483
- ctx.setLineDash([2, 5]);
484
- } else if (mergedLine.connectorStyle === "dashed") {
485
- ctx.setLineDash([6, 5]);
486
- } else {
487
- ctx.setLineDash([]);
488
- }
512
+ applyDashPattern(
513
+ mergedLine.connectorStyle,
514
+ dashPatterns.connectorDotted,
515
+ dashPatterns.connectorDashed
516
+ );
489
517
  ctx.beginPath();
490
518
  ctx.moveTo(crisp(connectorX), crisp(lineY));
491
519
  ctx.lineTo(crisp(connectorX), crisp(connectorY));
@@ -588,13 +616,11 @@ function createChart(element, options = {}) {
588
616
  ctx.save();
589
617
  ctx.strokeStyle = actionBorderColor;
590
618
  ctx.lineWidth = 1;
591
- if (actionBorderStyle === "dotted") {
592
- ctx.setLineDash([2, 3]);
593
- } else if (actionBorderStyle === "dashed") {
594
- ctx.setLineDash([6, 4]);
595
- } else {
596
- ctx.setLineDash([]);
597
- }
619
+ applyDashPattern(
620
+ actionBorderStyle,
621
+ dashPatterns.borderDotted,
622
+ dashPatterns.borderDashed
623
+ );
598
624
  strokeRoundedRect(Math.round(actionX), Math.round(actionY), actionW, actionH, actionRadius);
599
625
  ctx.restore();
600
626
  const baseFont = ctx.font;
@@ -933,13 +959,7 @@ function createChart(element, options = {}) {
933
959
  ctx.save();
934
960
  ctx.strokeStyle = crosshair.color;
935
961
  ctx.lineWidth = Math.max(1, crosshair.width);
936
- if (crosshair.style === "dotted") {
937
- ctx.setLineDash([2, 4]);
938
- } else if (crosshair.style === "dashed") {
939
- ctx.setLineDash([8, 6]);
940
- } else {
941
- ctx.setLineDash([]);
942
- }
962
+ applyDashPattern(crosshair.style, dashPatterns.dotted, dashPatterns.dashed);
943
963
  if (crosshair.showVertical) {
944
964
  ctx.beginPath();
945
965
  ctx.moveTo(crisp(cx), crisp(chartTop));
@@ -981,13 +1001,7 @@ function createChart(element, options = {}) {
981
1001
  ctx.save();
982
1002
  ctx.strokeStyle = tickerColor;
983
1003
  ctx.lineWidth = tickerThickness;
984
- if (tickerStyle === "dotted") {
985
- ctx.setLineDash([2, 4]);
986
- } else if (tickerStyle === "dashed") {
987
- ctx.setLineDash([8, 6]);
988
- } else {
989
- ctx.setLineDash([]);
990
- }
1004
+ applyDashPattern(tickerStyle, dashPatterns.dotted, dashPatterns.dashed);
991
1005
  ctx.beginPath();
992
1006
  ctx.moveTo(crisp(chartLeft), crisp(lineY));
993
1007
  ctx.lineTo(crisp(chartRight), crisp(lineY));
@@ -1031,6 +1045,17 @@ function createChart(element, options = {}) {
1031
1045
  });
1032
1046
  drawText(timeLabel, x, chartBottom + 8, "center", "top", axis.textColor);
1033
1047
  }
1048
+ const brandLogoWidth = 34;
1049
+ const brandLogoHeight = BRAND_LOGO_VIEWBOX_HEIGHT / BRAND_LOGO_VIEWBOX_WIDTH * brandLogoWidth;
1050
+ const brandLogoX = chartLeft + 16;
1051
+ const brandLogoY = chartBottom - brandLogoHeight - 10;
1052
+ ctx.save();
1053
+ ctx.translate(brandLogoX, brandLogoY);
1054
+ ctx.scale(brandLogoWidth / BRAND_LOGO_VIEWBOX_WIDTH, brandLogoHeight / BRAND_LOGO_VIEWBOX_HEIGHT);
1055
+ ctx.fillStyle = "#ffffff";
1056
+ ctx.fill(brandLogoPathA);
1057
+ ctx.fill(brandLogoPathB);
1058
+ ctx.restore();
1034
1059
  if (crosshair.visible && crosshairPoint) {
1035
1060
  const cx = clamp(crosshairPoint.x, chartLeft, chartRight);
1036
1061
  const cy = clamp(crosshairPoint.y, chartTop, chartBottom);
@@ -1049,13 +1074,11 @@ function createChart(element, options = {}) {
1049
1074
  ctx.save();
1050
1075
  ctx.strokeStyle = labelBorderColor;
1051
1076
  ctx.lineWidth = labelBorderWidth;
1052
- if (labelBorderStyle === "dotted") {
1053
- ctx.setLineDash([2, 3]);
1054
- } else if (labelBorderStyle === "dashed") {
1055
- ctx.setLineDash([6, 4]);
1056
- } else {
1057
- ctx.setLineDash([]);
1058
- }
1077
+ applyDashPattern(
1078
+ labelBorderStyle,
1079
+ dashPatterns.borderDotted,
1080
+ dashPatterns.borderDashed
1081
+ );
1059
1082
  strokeRoundedRect(Math.round(x), Math.round(y), widthValue, labelHeight, labelRadius);
1060
1083
  ctx.restore();
1061
1084
  };
@@ -1091,8 +1114,8 @@ function createChart(element, options = {}) {
1091
1114
  if (!drawState || data.length === 0) {
1092
1115
  return;
1093
1116
  }
1094
- const minSpan = 5;
1095
- const maxSpan = Math.max(minSpan, data.length + maxPanBars * 2);
1117
+ const minSpan = minVisibleBars;
1118
+ const maxSpan = Math.min(maxVisibleBars, Math.max(minSpan, data.length + maxPanBars * 2));
1096
1119
  const nextSpan = clamp(xSpan * factor, minSpan, maxSpan);
1097
1120
  const anchorRatio = clamp((anchorX - drawState.chartLeft) / drawState.chartWidth, 0, 1);
1098
1121
  const anchorIndex = drawState.xStart + anchorRatio * xSpan;
@@ -1106,8 +1129,8 @@ function createChart(element, options = {}) {
1106
1129
  if (!drawState || data.length === 0) {
1107
1130
  return;
1108
1131
  }
1109
- const minSpan = 5;
1110
- const maxSpan = Math.max(minSpan, data.length + maxPanBars * 2);
1132
+ const minSpan = minVisibleBars;
1133
+ const maxSpan = Math.min(maxVisibleBars, Math.max(minSpan, data.length + maxPanBars * 2));
1111
1134
  const nextSpan = clamp(xSpan * factor, minSpan, maxSpan);
1112
1135
  const rightEdgeIndex = data.length + rightEdgePaddingBars;
1113
1136
  const nextStart = rightEdgeIndex - nextSpan;
@@ -1156,6 +1179,64 @@ function createChart(element, options = {}) {
1156
1179
  }
1157
1180
  draw();
1158
1181
  };
1182
+ const resetYViewport = () => {
1183
+ yMinOverride = null;
1184
+ yMaxOverride = null;
1185
+ autoYMin = null;
1186
+ autoYMax = null;
1187
+ };
1188
+ const zoomInX = (factor = 1.25) => {
1189
+ if (!drawState) {
1190
+ return;
1191
+ }
1192
+ zoomX(1 / Math.max(1.01, factor), drawState.chartLeft + drawState.chartWidth / 2);
1193
+ };
1194
+ const zoomOutX = (factor = 1.25) => {
1195
+ if (!drawState) {
1196
+ return;
1197
+ }
1198
+ zoomX(Math.max(1.01, factor), drawState.chartLeft + drawState.chartWidth / 2);
1199
+ };
1200
+ const zoomInY = (factor = 1.25) => {
1201
+ if (!drawState) {
1202
+ return;
1203
+ }
1204
+ zoomY(1 / Math.max(1.01, factor), drawState.chartTop + drawState.chartHeight / 2);
1205
+ };
1206
+ const zoomOutY = (factor = 1.25) => {
1207
+ if (!drawState) {
1208
+ return;
1209
+ }
1210
+ zoomY(Math.max(1.01, factor), drawState.chartTop + drawState.chartHeight / 2);
1211
+ };
1212
+ const panX = (bars) => {
1213
+ if (!Number.isFinite(bars) || bars === 0) {
1214
+ return;
1215
+ }
1216
+ xCenter += bars;
1217
+ clampXViewport();
1218
+ draw();
1219
+ };
1220
+ const panY = (priceDelta) => {
1221
+ if (!drawState || !Number.isFinite(priceDelta) || priceDelta === 0) {
1222
+ return;
1223
+ }
1224
+ const currentMin = yMinOverride ?? drawState.yMin;
1225
+ const currentMax = yMaxOverride ?? drawState.yMax;
1226
+ const clamped = clampYRange(currentMin + priceDelta, currentMax + priceDelta);
1227
+ yMinOverride = clamped.min;
1228
+ yMaxOverride = clamped.max;
1229
+ draw();
1230
+ };
1231
+ const fitContent = () => {
1232
+ fitXViewport();
1233
+ draw();
1234
+ };
1235
+ const resetViewport = () => {
1236
+ fitXViewport();
1237
+ resetYViewport();
1238
+ draw();
1239
+ };
1159
1240
  const getCanvasPoint = (event) => {
1160
1241
  const rect = canvas.getBoundingClientRect();
1161
1242
  return {
@@ -1499,19 +1580,11 @@ function createChart(element, options = {}) {
1499
1580
  return;
1500
1581
  }
1501
1582
  if (region === "y-axis") {
1502
- yMinOverride = null;
1503
- yMaxOverride = null;
1504
- autoYMin = null;
1505
- autoYMax = null;
1583
+ resetYViewport();
1506
1584
  draw();
1507
1585
  return;
1508
1586
  }
1509
- fitXViewport();
1510
- yMinOverride = null;
1511
- yMaxOverride = null;
1512
- autoYMin = null;
1513
- autoYMax = null;
1514
- draw();
1587
+ resetViewport();
1515
1588
  };
1516
1589
  canvas.addEventListener("pointerdown", onPointerDown);
1517
1590
  canvas.addEventListener("pointermove", onPointerMove);
@@ -1535,19 +1608,13 @@ function createChart(element, options = {}) {
1535
1608
  if (data.length === 0) {
1536
1609
  xCenter = 0;
1537
1610
  xSpan = 60;
1538
- yMinOverride = null;
1539
- yMaxOverride = null;
1540
- autoYMin = null;
1541
- autoYMax = null;
1611
+ resetYViewport();
1542
1612
  draw();
1543
1613
  return;
1544
1614
  }
1545
1615
  if (!hadData) {
1546
1616
  fitXViewport();
1547
- yMinOverride = null;
1548
- yMaxOverride = null;
1549
- autoYMin = null;
1550
- autoYMax = null;
1617
+ resetYViewport();
1551
1618
  } else {
1552
1619
  if (mergedOptions.preserveViewportOnDataUpdate) {
1553
1620
  clampXViewport();
@@ -1646,6 +1713,14 @@ function createChart(element, options = {}) {
1646
1713
  onOrderAction,
1647
1714
  onChartClick,
1648
1715
  onCrosshairMove,
1716
+ zoomInX,
1717
+ zoomOutX,
1718
+ zoomInY,
1719
+ zoomOutY,
1720
+ panX,
1721
+ panY,
1722
+ resetViewport,
1723
+ fitContent,
1649
1724
  setDoubleClickEnabled,
1650
1725
  setDoubleClickAction,
1651
1726
  resize,