hyperprop-charting-library 0.1.5 → 0.1.7

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/index.cjs CHANGED
@@ -51,7 +51,13 @@ var DEFAULT_WATERMARK_OPTIONS = {
51
51
  opacity: 0.14,
52
52
  fontSize: 92,
53
53
  fontWeight: 700,
54
- thickness: 0
54
+ thickness: 0,
55
+ imageSrc: "",
56
+ imageScale: 1,
57
+ imageMaxWidthRatio: 0.42,
58
+ imageMaxHeightRatio: 0.3,
59
+ imageTintColor: "",
60
+ imageTintOpacity: 1
55
61
  };
56
62
  var DEFAULT_PRICE_LINE_OPTIONS = {
57
63
  visible: true,
@@ -101,6 +107,10 @@ var DEFAULT_OPTIONS = {
101
107
  axisColor: "#7f8289",
102
108
  axis: DEFAULT_AXIS_OPTIONS,
103
109
  priceDecimals: 2,
110
+ initialViewport: "latest",
111
+ initialVisibleBars: 60,
112
+ rightEdgePaddingBars: 2,
113
+ preserveViewportOnDataUpdate: true,
104
114
  upColor: "#2fb171",
105
115
  downColor: "#d35a5a",
106
116
  gridColor: "#252932",
@@ -178,6 +188,9 @@ function createChart(element, options = {}) {
178
188
  let yMaxOverride = null;
179
189
  let autoYMin = null;
180
190
  let autoYMax = null;
191
+ let watermarkImageSrc = null;
192
+ let watermarkImage = null;
193
+ let watermarkImageReady = false;
181
194
  let drawState = null;
182
195
  let orderDragState = null;
183
196
  let actionDragState = null;
@@ -196,13 +209,36 @@ function createChart(element, options = {}) {
196
209
  element.appendChild(canvas);
197
210
  const margin = { top: 16, right: 72, bottom: 34, left: 12 };
198
211
  const maxPanBars = 1e6;
199
- const rightEdgePaddingBars = 2;
212
+ const rightEdgePaddingBars = Math.max(0, mergedOptions.rightEdgePaddingBars);
200
213
  const getPixelRatio = () => {
201
214
  if (typeof window === "undefined") {
202
215
  return 1;
203
216
  }
204
217
  return Math.max(1, window.devicePixelRatio || 1);
205
218
  };
219
+ const ensureWatermarkImage = (src) => {
220
+ if (src === watermarkImageSrc) {
221
+ return;
222
+ }
223
+ watermarkImageSrc = src;
224
+ watermarkImageReady = false;
225
+ watermarkImage = null;
226
+ if (!src) {
227
+ return;
228
+ }
229
+ const image = new Image();
230
+ image.crossOrigin = "anonymous";
231
+ image.onload = () => {
232
+ watermarkImage = image;
233
+ watermarkImageReady = true;
234
+ draw();
235
+ };
236
+ image.onerror = () => {
237
+ watermarkImage = null;
238
+ watermarkImageReady = false;
239
+ };
240
+ image.src = src;
241
+ };
206
242
  const crisp = (value) => Math.round(value) + 0.5;
207
243
  const clamp = (value, min, max) => {
208
244
  return Math.min(max, Math.max(min, value));
@@ -226,8 +262,13 @@ function createChart(element, options = {}) {
226
262
  xSpan = 60;
227
263
  return;
228
264
  }
229
- xSpan = Math.min(60, count);
230
- xCenter = count - xSpan / 2;
265
+ const requestedVisibleBars = Math.max(5, Math.floor(mergedOptions.initialVisibleBars));
266
+ xSpan = Math.min(requestedVisibleBars, Math.max(5, count));
267
+ if (mergedOptions.initialViewport === "center") {
268
+ xCenter = count / 2;
269
+ } else {
270
+ xCenter = count - xSpan / 2 + rightEdgePaddingBars;
271
+ }
231
272
  clampXViewport();
232
273
  };
233
274
  const getYBounds = () => {
@@ -746,6 +787,31 @@ function createChart(element, options = {}) {
746
787
  const yFromPrice = (price) => {
747
788
  return chartBottom - (price - yMin) / yRange * chartHeight;
748
789
  };
790
+ if (watermark.visible && watermark.imageSrc.trim().length > 0) {
791
+ ensureWatermarkImage(watermark.imageSrc.trim());
792
+ if (watermarkImageReady && watermarkImage) {
793
+ const maxW = chartWidth * clamp(watermark.imageMaxWidthRatio, 0.05, 1);
794
+ const maxH = chartHeight * clamp(watermark.imageMaxHeightRatio, 0.05, 1);
795
+ const naturalW = Math.max(1, watermarkImage.naturalWidth || watermarkImage.width);
796
+ const naturalH = Math.max(1, watermarkImage.naturalHeight || watermarkImage.height);
797
+ const containScale = Math.min(maxW / naturalW, maxH / naturalH);
798
+ const scale = Math.max(0.05, containScale * Math.max(0.05, watermark.imageScale));
799
+ const drawW = naturalW * scale;
800
+ const drawH = naturalH * scale;
801
+ const drawX = chartLeft + (chartWidth - drawW) / 2;
802
+ const drawY = chartTop + (chartHeight - drawH) / 2;
803
+ ctx.save();
804
+ ctx.globalAlpha = clamp(watermark.opacity, 0, 1);
805
+ ctx.drawImage(watermarkImage, drawX, drawY, drawW, drawH);
806
+ if (watermark.imageTintColor.trim().length > 0) {
807
+ ctx.globalCompositeOperation = "source-atop";
808
+ ctx.globalAlpha = clamp(watermark.opacity, 0, 1) * clamp(watermark.imageTintOpacity, 0, 1);
809
+ ctx.fillStyle = watermark.imageTintColor;
810
+ ctx.fillRect(drawX, drawY, drawW, drawH);
811
+ }
812
+ ctx.restore();
813
+ }
814
+ }
749
815
  if (watermark.visible && watermark.text.trim().length > 0) {
750
816
  ctx.save();
751
817
  ctx.globalAlpha = clamp(watermark.opacity, 0, 1);
@@ -1370,7 +1436,11 @@ function createChart(element, options = {}) {
1370
1436
  autoYMin = null;
1371
1437
  autoYMax = null;
1372
1438
  } else {
1373
- clampXViewport();
1439
+ if (mergedOptions.preserveViewportOnDataUpdate) {
1440
+ clampXViewport();
1441
+ } else {
1442
+ fitXViewport();
1443
+ }
1374
1444
  }
1375
1445
  draw();
1376
1446
  };
package/dist/index.d.cts CHANGED
@@ -5,6 +5,10 @@ interface ChartOptions {
5
5
  axisColor?: string;
6
6
  axis?: AxisOptions;
7
7
  priceDecimals?: number;
8
+ initialViewport?: "latest" | "center";
9
+ initialVisibleBars?: number;
10
+ rightEdgePaddingBars?: number;
11
+ preserveViewportOnDataUpdate?: boolean;
8
12
  upColor?: string;
9
13
  downColor?: string;
10
14
  gridColor?: string;
@@ -52,6 +56,12 @@ interface WatermarkOptions {
52
56
  fontSize?: number;
53
57
  fontWeight?: number | string;
54
58
  thickness?: number;
59
+ imageSrc?: string;
60
+ imageScale?: number;
61
+ imageMaxWidthRatio?: number;
62
+ imageMaxHeightRatio?: number;
63
+ imageTintColor?: string;
64
+ imageTintOpacity?: number;
55
65
  }
56
66
  interface PriceLineOptions {
57
67
  id?: string;
package/dist/index.d.ts CHANGED
@@ -5,6 +5,10 @@ interface ChartOptions {
5
5
  axisColor?: string;
6
6
  axis?: AxisOptions;
7
7
  priceDecimals?: number;
8
+ initialViewport?: "latest" | "center";
9
+ initialVisibleBars?: number;
10
+ rightEdgePaddingBars?: number;
11
+ preserveViewportOnDataUpdate?: boolean;
8
12
  upColor?: string;
9
13
  downColor?: string;
10
14
  gridColor?: string;
@@ -52,6 +56,12 @@ interface WatermarkOptions {
52
56
  fontSize?: number;
53
57
  fontWeight?: number | string;
54
58
  thickness?: number;
59
+ imageSrc?: string;
60
+ imageScale?: number;
61
+ imageMaxWidthRatio?: number;
62
+ imageMaxHeightRatio?: number;
63
+ imageTintColor?: string;
64
+ imageTintOpacity?: number;
55
65
  }
56
66
  interface PriceLineOptions {
57
67
  id?: string;
package/dist/index.js CHANGED
@@ -27,7 +27,13 @@ var DEFAULT_WATERMARK_OPTIONS = {
27
27
  opacity: 0.14,
28
28
  fontSize: 92,
29
29
  fontWeight: 700,
30
- thickness: 0
30
+ thickness: 0,
31
+ imageSrc: "",
32
+ imageScale: 1,
33
+ imageMaxWidthRatio: 0.42,
34
+ imageMaxHeightRatio: 0.3,
35
+ imageTintColor: "",
36
+ imageTintOpacity: 1
31
37
  };
32
38
  var DEFAULT_PRICE_LINE_OPTIONS = {
33
39
  visible: true,
@@ -77,6 +83,10 @@ var DEFAULT_OPTIONS = {
77
83
  axisColor: "#7f8289",
78
84
  axis: DEFAULT_AXIS_OPTIONS,
79
85
  priceDecimals: 2,
86
+ initialViewport: "latest",
87
+ initialVisibleBars: 60,
88
+ rightEdgePaddingBars: 2,
89
+ preserveViewportOnDataUpdate: true,
80
90
  upColor: "#2fb171",
81
91
  downColor: "#d35a5a",
82
92
  gridColor: "#252932",
@@ -154,6 +164,9 @@ function createChart(element, options = {}) {
154
164
  let yMaxOverride = null;
155
165
  let autoYMin = null;
156
166
  let autoYMax = null;
167
+ let watermarkImageSrc = null;
168
+ let watermarkImage = null;
169
+ let watermarkImageReady = false;
157
170
  let drawState = null;
158
171
  let orderDragState = null;
159
172
  let actionDragState = null;
@@ -172,13 +185,36 @@ function createChart(element, options = {}) {
172
185
  element.appendChild(canvas);
173
186
  const margin = { top: 16, right: 72, bottom: 34, left: 12 };
174
187
  const maxPanBars = 1e6;
175
- const rightEdgePaddingBars = 2;
188
+ const rightEdgePaddingBars = Math.max(0, mergedOptions.rightEdgePaddingBars);
176
189
  const getPixelRatio = () => {
177
190
  if (typeof window === "undefined") {
178
191
  return 1;
179
192
  }
180
193
  return Math.max(1, window.devicePixelRatio || 1);
181
194
  };
195
+ const ensureWatermarkImage = (src) => {
196
+ if (src === watermarkImageSrc) {
197
+ return;
198
+ }
199
+ watermarkImageSrc = src;
200
+ watermarkImageReady = false;
201
+ watermarkImage = null;
202
+ if (!src) {
203
+ return;
204
+ }
205
+ const image = new Image();
206
+ image.crossOrigin = "anonymous";
207
+ image.onload = () => {
208
+ watermarkImage = image;
209
+ watermarkImageReady = true;
210
+ draw();
211
+ };
212
+ image.onerror = () => {
213
+ watermarkImage = null;
214
+ watermarkImageReady = false;
215
+ };
216
+ image.src = src;
217
+ };
182
218
  const crisp = (value) => Math.round(value) + 0.5;
183
219
  const clamp = (value, min, max) => {
184
220
  return Math.min(max, Math.max(min, value));
@@ -202,8 +238,13 @@ function createChart(element, options = {}) {
202
238
  xSpan = 60;
203
239
  return;
204
240
  }
205
- xSpan = Math.min(60, count);
206
- xCenter = count - xSpan / 2;
241
+ const requestedVisibleBars = Math.max(5, Math.floor(mergedOptions.initialVisibleBars));
242
+ xSpan = Math.min(requestedVisibleBars, Math.max(5, count));
243
+ if (mergedOptions.initialViewport === "center") {
244
+ xCenter = count / 2;
245
+ } else {
246
+ xCenter = count - xSpan / 2 + rightEdgePaddingBars;
247
+ }
207
248
  clampXViewport();
208
249
  };
209
250
  const getYBounds = () => {
@@ -722,6 +763,31 @@ function createChart(element, options = {}) {
722
763
  const yFromPrice = (price) => {
723
764
  return chartBottom - (price - yMin) / yRange * chartHeight;
724
765
  };
766
+ if (watermark.visible && watermark.imageSrc.trim().length > 0) {
767
+ ensureWatermarkImage(watermark.imageSrc.trim());
768
+ if (watermarkImageReady && watermarkImage) {
769
+ const maxW = chartWidth * clamp(watermark.imageMaxWidthRatio, 0.05, 1);
770
+ const maxH = chartHeight * clamp(watermark.imageMaxHeightRatio, 0.05, 1);
771
+ const naturalW = Math.max(1, watermarkImage.naturalWidth || watermarkImage.width);
772
+ const naturalH = Math.max(1, watermarkImage.naturalHeight || watermarkImage.height);
773
+ const containScale = Math.min(maxW / naturalW, maxH / naturalH);
774
+ const scale = Math.max(0.05, containScale * Math.max(0.05, watermark.imageScale));
775
+ const drawW = naturalW * scale;
776
+ const drawH = naturalH * scale;
777
+ const drawX = chartLeft + (chartWidth - drawW) / 2;
778
+ const drawY = chartTop + (chartHeight - drawH) / 2;
779
+ ctx.save();
780
+ ctx.globalAlpha = clamp(watermark.opacity, 0, 1);
781
+ ctx.drawImage(watermarkImage, drawX, drawY, drawW, drawH);
782
+ if (watermark.imageTintColor.trim().length > 0) {
783
+ ctx.globalCompositeOperation = "source-atop";
784
+ ctx.globalAlpha = clamp(watermark.opacity, 0, 1) * clamp(watermark.imageTintOpacity, 0, 1);
785
+ ctx.fillStyle = watermark.imageTintColor;
786
+ ctx.fillRect(drawX, drawY, drawW, drawH);
787
+ }
788
+ ctx.restore();
789
+ }
790
+ }
725
791
  if (watermark.visible && watermark.text.trim().length > 0) {
726
792
  ctx.save();
727
793
  ctx.globalAlpha = clamp(watermark.opacity, 0, 1);
@@ -1346,7 +1412,11 @@ function createChart(element, options = {}) {
1346
1412
  autoYMin = null;
1347
1413
  autoYMax = null;
1348
1414
  } else {
1349
- clampXViewport();
1415
+ if (mergedOptions.preserveViewportOnDataUpdate) {
1416
+ clampXViewport();
1417
+ } else {
1418
+ fitXViewport();
1419
+ }
1350
1420
  }
1351
1421
  draw();
1352
1422
  };
package/docs/API.md CHANGED
@@ -33,6 +33,10 @@ Top-level options:
33
33
  - `axisColor` (legacy shorthand for axis line/text color)
34
34
  - `axis?: AxisOptions`
35
35
  - `priceDecimals` (default `2`, used for axis/ticker/line price labels)
36
+ - `initialViewport` (`"latest"` | `"center"`, default `"latest"`)
37
+ - `initialVisibleBars` (default `60`)
38
+ - `rightEdgePaddingBars` (default `2`, used by latest-anchored viewport)
39
+ - `preserveViewportOnDataUpdate` (default `true`; set `false` to auto-fit on each `setData`)
36
40
  - `upColor` (default `#16a34a`)
37
41
  - `downColor` (default `#dc2626`)
38
42
  - `gridColor` (default `#e2e8f0`)
@@ -79,11 +83,17 @@ Top-level options:
79
83
 
80
84
  - `visible` (default `false`)
81
85
  - `text` (default `""`)
82
- - `color` (default `#94a3b8`)
83
- - `opacity` (default `0.2`)
86
+ - `color` (default `#81858d`)
87
+ - `opacity` (default `0.14`)
84
88
  - `fontSize` (default `92`)
85
89
  - `fontWeight` (default `700`)
86
90
  - `thickness` (stroke width, default `0`)
91
+ - `imageSrc` (default `""`, URL/path to watermark logo)
92
+ - `imageScale` (default `1`)
93
+ - `imageMaxWidthRatio` (default `0.42`)
94
+ - `imageMaxHeightRatio` (default `0.3`)
95
+ - `imageTintColor` (default `""`, set `"#ffffff"` for white logo tint)
96
+ - `imageTintOpacity` (default `1`)
87
97
 
88
98
  ### `TickerLineOptions`
89
99
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperprop-charting-library",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Lightweight TypeScript charting core",
5
5
  "type": "module",
6
6
  "main": "./dist/hyperprop-charting-library.cjs",