hyperprop-charting-library 0.1.46 → 0.1.47

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/README.md CHANGED
@@ -65,13 +65,14 @@ const chart = createChart(root, {
65
65
  ```ts
66
66
  const chart = createChart(root, {
67
67
  tickerLine: {
68
- labelSubtext: "MNQ"
69
- // or:
70
- // showCountdownInLabel: true
68
+ labelSubtexts: ["MNQ", "RTH"],
69
+ showCountdownInLabel: true
71
70
  }
72
71
  });
73
72
  ```
74
73
 
74
+ The price label grows to fit all extra lines. `labelSubtext: "MNQ"` still works for the simple one-line case.
75
+
75
76
  ## Candle Color Behavior
76
77
 
77
78
  You can control how up/down candle color is decided:
@@ -202,6 +202,7 @@ var DEFAULT_OPTIONS = {
202
202
  thickness: 1,
203
203
  labelTextColor: "#0b1220",
204
204
  labelSubtext: "",
205
+ labelSubtexts: [],
205
206
  labelSubtextColor: "#0b1220",
206
207
  labelSubtextFontSize: 0,
207
208
  showCountdownInLabel: false,
@@ -2141,10 +2142,14 @@ function createChart(element, options = {}) {
2141
2142
  ctx.restore();
2142
2143
  }
2143
2144
  if ((ticker.visible ?? true) && labels.showLastPrice && tickerPrice !== null && tickerColor !== null) {
2144
- const tickerSubtext = ticker.showCountdownInLabel ? getCountdownText() : ticker.labelSubtext?.trim();
2145
+ const tickerSubtexts = [
2146
+ ...ticker.labelSubtext ? [ticker.labelSubtext] : [],
2147
+ ...ticker.labelSubtexts ?? [],
2148
+ ...ticker.showCountdownInLabel ? [getCountdownText()] : []
2149
+ ].map((value) => value === null || value === void 0 ? "" : String(value).trim()).filter((value) => value.length > 0);
2145
2150
  addPriceAxisLabel({
2146
2151
  text: formatPrice(tickerPrice),
2147
- ...tickerSubtext ? { subtext: tickerSubtext } : {},
2152
+ ...tickerSubtexts.length > 0 ? { subtexts: tickerSubtexts } : {},
2148
2153
  subtextColor: ticker.labelSubtextColor ?? ticker.labelTextColor ?? "#0b1220",
2149
2154
  ...ticker.labelSubtextFontSize === void 0 ? {} : { subtextFontSize: ticker.labelSubtextFontSize },
2150
2155
  price: tickerPrice,
@@ -2230,21 +2235,22 @@ function createChart(element, options = {}) {
2230
2235
  const priceLabelFontSize = Math.max(8, axis.fontSize);
2231
2236
  ctx.font = `${priceLabelFontSize}px ${mergedOptions.fontFamily}`;
2232
2237
  const positionedLabels = priceAxisLabels.map((label) => {
2233
- const subtext = label.subtext?.trim();
2238
+ const subtexts = (label.subtexts ?? []).map((value) => value.trim()).filter((value) => value.length > 0);
2234
2239
  const subtextFontSize = label.subtextFontSize !== void 0 && label.subtextFontSize > 0 ? Math.max(8, Math.round(label.subtextFontSize)) : priceLabelFontSize;
2235
- const labelHeight = baseLabelHeight + (subtext ? subtextFontSize + 5 : 0);
2240
+ const subtextLineGap = 5;
2241
+ const labelHeight = baseLabelHeight + (subtexts.length > 0 ? subtexts.length * subtextFontSize + subtexts.length * subtextLineGap : 0);
2236
2242
  const primaryWidth = label.text === formatPrice(label.price) ? getPriceLabelWidth(label.text, labelPaddingX) : Math.ceil(ctx.measureText(label.text).width) + labelPaddingX * 2;
2237
2243
  let subtextWidth = 0;
2238
- if (subtext) {
2244
+ if (subtexts.length > 0) {
2239
2245
  const baseFont = ctx.font;
2240
2246
  ctx.font = `${subtextFontSize}px ${mergedOptions.fontFamily}`;
2241
- subtextWidth = Math.ceil(ctx.measureText(subtext).width) + labelPaddingX * 2;
2247
+ subtextWidth = Math.max(...subtexts.map((subtext) => Math.ceil(ctx.measureText(subtext).width) + labelPaddingX * 2));
2242
2248
  ctx.font = baseFont;
2243
2249
  }
2244
2250
  const labelTextWidth = Math.max(primaryWidth, subtextWidth);
2245
2251
  return {
2246
2252
  ...label,
2247
- subtext,
2253
+ subtexts,
2248
2254
  subtextFontSize,
2249
2255
  height: labelHeight,
2250
2256
  width: labelTextWidth,
@@ -2255,7 +2261,7 @@ function createChart(element, options = {}) {
2255
2261
  const minY = chartTop;
2256
2262
  let cursorY = minY;
2257
2263
  for (const label of positionedLabels) {
2258
- const maxY = chartBottom - label.height;
2264
+ const maxY = Math.max(minY, chartBottom - label.height);
2259
2265
  label.y = labels.noOverlapping ? Math.max(clamp(label.targetY, minY, maxY), cursorY) : clamp(label.targetY, minY, maxY);
2260
2266
  cursorY = label.y + label.height + 2;
2261
2267
  }
@@ -2272,19 +2278,22 @@ function createChart(element, options = {}) {
2272
2278
  for (const label of positionedLabels) {
2273
2279
  ctx.fillStyle = label.backgroundColor;
2274
2280
  fillRoundedRect(Math.round(labelX), Math.round(label.y), label.width, label.height, labelRadius);
2275
- const primaryY = label.subtext ? label.y + baseLabelHeight / 2 - 1 : label.y + label.height / 2;
2281
+ const hasSubtexts = label.subtexts.length > 0;
2282
+ const primaryY = hasSubtexts ? label.y + baseLabelHeight / 2 - 1 : label.y + label.height / 2;
2276
2283
  drawText(label.text, labelX + labelPaddingX, primaryY, "left", "middle", label.textColor);
2277
- if (label.subtext) {
2284
+ if (hasSubtexts) {
2278
2285
  const baseFont = ctx.font;
2279
2286
  ctx.font = `${label.subtextFontSize}px ${mergedOptions.fontFamily}`;
2280
- drawText(
2281
- label.subtext,
2282
- labelX + labelPaddingX,
2283
- label.y + baseLabelHeight + label.subtextFontSize / 2,
2284
- "left",
2285
- "middle",
2286
- label.subtextColor ?? label.textColor
2287
- );
2287
+ label.subtexts.forEach((subtext, index) => {
2288
+ drawText(
2289
+ subtext,
2290
+ labelX + labelPaddingX,
2291
+ label.y + baseLabelHeight + index * (label.subtextFontSize + 5) + label.subtextFontSize / 2,
2292
+ "left",
2293
+ "middle",
2294
+ label.subtextColor ?? label.textColor
2295
+ );
2296
+ });
2288
2297
  ctx.font = baseFont;
2289
2298
  }
2290
2299
  }
@@ -261,6 +261,7 @@ interface TickerLineOptions {
261
261
  labelBackgroundColor?: string;
262
262
  labelTextColor?: string;
263
263
  labelSubtext?: string;
264
+ labelSubtexts?: Array<string | number>;
264
265
  labelSubtextColor?: string;
265
266
  labelSubtextFontSize?: number;
266
267
  showCountdownInLabel?: boolean;
@@ -178,6 +178,7 @@ var DEFAULT_OPTIONS = {
178
178
  thickness: 1,
179
179
  labelTextColor: "#0b1220",
180
180
  labelSubtext: "",
181
+ labelSubtexts: [],
181
182
  labelSubtextColor: "#0b1220",
182
183
  labelSubtextFontSize: 0,
183
184
  showCountdownInLabel: false,
@@ -2117,10 +2118,14 @@ function createChart(element, options = {}) {
2117
2118
  ctx.restore();
2118
2119
  }
2119
2120
  if ((ticker.visible ?? true) && labels.showLastPrice && tickerPrice !== null && tickerColor !== null) {
2120
- const tickerSubtext = ticker.showCountdownInLabel ? getCountdownText() : ticker.labelSubtext?.trim();
2121
+ const tickerSubtexts = [
2122
+ ...ticker.labelSubtext ? [ticker.labelSubtext] : [],
2123
+ ...ticker.labelSubtexts ?? [],
2124
+ ...ticker.showCountdownInLabel ? [getCountdownText()] : []
2125
+ ].map((value) => value === null || value === void 0 ? "" : String(value).trim()).filter((value) => value.length > 0);
2121
2126
  addPriceAxisLabel({
2122
2127
  text: formatPrice(tickerPrice),
2123
- ...tickerSubtext ? { subtext: tickerSubtext } : {},
2128
+ ...tickerSubtexts.length > 0 ? { subtexts: tickerSubtexts } : {},
2124
2129
  subtextColor: ticker.labelSubtextColor ?? ticker.labelTextColor ?? "#0b1220",
2125
2130
  ...ticker.labelSubtextFontSize === void 0 ? {} : { subtextFontSize: ticker.labelSubtextFontSize },
2126
2131
  price: tickerPrice,
@@ -2206,21 +2211,22 @@ function createChart(element, options = {}) {
2206
2211
  const priceLabelFontSize = Math.max(8, axis.fontSize);
2207
2212
  ctx.font = `${priceLabelFontSize}px ${mergedOptions.fontFamily}`;
2208
2213
  const positionedLabels = priceAxisLabels.map((label) => {
2209
- const subtext = label.subtext?.trim();
2214
+ const subtexts = (label.subtexts ?? []).map((value) => value.trim()).filter((value) => value.length > 0);
2210
2215
  const subtextFontSize = label.subtextFontSize !== void 0 && label.subtextFontSize > 0 ? Math.max(8, Math.round(label.subtextFontSize)) : priceLabelFontSize;
2211
- const labelHeight = baseLabelHeight + (subtext ? subtextFontSize + 5 : 0);
2216
+ const subtextLineGap = 5;
2217
+ const labelHeight = baseLabelHeight + (subtexts.length > 0 ? subtexts.length * subtextFontSize + subtexts.length * subtextLineGap : 0);
2212
2218
  const primaryWidth = label.text === formatPrice(label.price) ? getPriceLabelWidth(label.text, labelPaddingX) : Math.ceil(ctx.measureText(label.text).width) + labelPaddingX * 2;
2213
2219
  let subtextWidth = 0;
2214
- if (subtext) {
2220
+ if (subtexts.length > 0) {
2215
2221
  const baseFont = ctx.font;
2216
2222
  ctx.font = `${subtextFontSize}px ${mergedOptions.fontFamily}`;
2217
- subtextWidth = Math.ceil(ctx.measureText(subtext).width) + labelPaddingX * 2;
2223
+ subtextWidth = Math.max(...subtexts.map((subtext) => Math.ceil(ctx.measureText(subtext).width) + labelPaddingX * 2));
2218
2224
  ctx.font = baseFont;
2219
2225
  }
2220
2226
  const labelTextWidth = Math.max(primaryWidth, subtextWidth);
2221
2227
  return {
2222
2228
  ...label,
2223
- subtext,
2229
+ subtexts,
2224
2230
  subtextFontSize,
2225
2231
  height: labelHeight,
2226
2232
  width: labelTextWidth,
@@ -2231,7 +2237,7 @@ function createChart(element, options = {}) {
2231
2237
  const minY = chartTop;
2232
2238
  let cursorY = minY;
2233
2239
  for (const label of positionedLabels) {
2234
- const maxY = chartBottom - label.height;
2240
+ const maxY = Math.max(minY, chartBottom - label.height);
2235
2241
  label.y = labels.noOverlapping ? Math.max(clamp(label.targetY, minY, maxY), cursorY) : clamp(label.targetY, minY, maxY);
2236
2242
  cursorY = label.y + label.height + 2;
2237
2243
  }
@@ -2248,19 +2254,22 @@ function createChart(element, options = {}) {
2248
2254
  for (const label of positionedLabels) {
2249
2255
  ctx.fillStyle = label.backgroundColor;
2250
2256
  fillRoundedRect(Math.round(labelX), Math.round(label.y), label.width, label.height, labelRadius);
2251
- const primaryY = label.subtext ? label.y + baseLabelHeight / 2 - 1 : label.y + label.height / 2;
2257
+ const hasSubtexts = label.subtexts.length > 0;
2258
+ const primaryY = hasSubtexts ? label.y + baseLabelHeight / 2 - 1 : label.y + label.height / 2;
2252
2259
  drawText(label.text, labelX + labelPaddingX, primaryY, "left", "middle", label.textColor);
2253
- if (label.subtext) {
2260
+ if (hasSubtexts) {
2254
2261
  const baseFont = ctx.font;
2255
2262
  ctx.font = `${label.subtextFontSize}px ${mergedOptions.fontFamily}`;
2256
- drawText(
2257
- label.subtext,
2258
- labelX + labelPaddingX,
2259
- label.y + baseLabelHeight + label.subtextFontSize / 2,
2260
- "left",
2261
- "middle",
2262
- label.subtextColor ?? label.textColor
2263
- );
2263
+ label.subtexts.forEach((subtext, index) => {
2264
+ drawText(
2265
+ subtext,
2266
+ labelX + labelPaddingX,
2267
+ label.y + baseLabelHeight + index * (label.subtextFontSize + 5) + label.subtextFontSize / 2,
2268
+ "left",
2269
+ "middle",
2270
+ label.subtextColor ?? label.textColor
2271
+ );
2272
+ });
2264
2273
  ctx.font = baseFont;
2265
2274
  }
2266
2275
  }
package/dist/index.cjs CHANGED
@@ -202,6 +202,7 @@ var DEFAULT_OPTIONS = {
202
202
  thickness: 1,
203
203
  labelTextColor: "#0b1220",
204
204
  labelSubtext: "",
205
+ labelSubtexts: [],
205
206
  labelSubtextColor: "#0b1220",
206
207
  labelSubtextFontSize: 0,
207
208
  showCountdownInLabel: false,
@@ -2141,10 +2142,14 @@ function createChart(element, options = {}) {
2141
2142
  ctx.restore();
2142
2143
  }
2143
2144
  if ((ticker.visible ?? true) && labels.showLastPrice && tickerPrice !== null && tickerColor !== null) {
2144
- const tickerSubtext = ticker.showCountdownInLabel ? getCountdownText() : ticker.labelSubtext?.trim();
2145
+ const tickerSubtexts = [
2146
+ ...ticker.labelSubtext ? [ticker.labelSubtext] : [],
2147
+ ...ticker.labelSubtexts ?? [],
2148
+ ...ticker.showCountdownInLabel ? [getCountdownText()] : []
2149
+ ].map((value) => value === null || value === void 0 ? "" : String(value).trim()).filter((value) => value.length > 0);
2145
2150
  addPriceAxisLabel({
2146
2151
  text: formatPrice(tickerPrice),
2147
- ...tickerSubtext ? { subtext: tickerSubtext } : {},
2152
+ ...tickerSubtexts.length > 0 ? { subtexts: tickerSubtexts } : {},
2148
2153
  subtextColor: ticker.labelSubtextColor ?? ticker.labelTextColor ?? "#0b1220",
2149
2154
  ...ticker.labelSubtextFontSize === void 0 ? {} : { subtextFontSize: ticker.labelSubtextFontSize },
2150
2155
  price: tickerPrice,
@@ -2230,21 +2235,22 @@ function createChart(element, options = {}) {
2230
2235
  const priceLabelFontSize = Math.max(8, axis.fontSize);
2231
2236
  ctx.font = `${priceLabelFontSize}px ${mergedOptions.fontFamily}`;
2232
2237
  const positionedLabels = priceAxisLabels.map((label) => {
2233
- const subtext = label.subtext?.trim();
2238
+ const subtexts = (label.subtexts ?? []).map((value) => value.trim()).filter((value) => value.length > 0);
2234
2239
  const subtextFontSize = label.subtextFontSize !== void 0 && label.subtextFontSize > 0 ? Math.max(8, Math.round(label.subtextFontSize)) : priceLabelFontSize;
2235
- const labelHeight = baseLabelHeight + (subtext ? subtextFontSize + 5 : 0);
2240
+ const subtextLineGap = 5;
2241
+ const labelHeight = baseLabelHeight + (subtexts.length > 0 ? subtexts.length * subtextFontSize + subtexts.length * subtextLineGap : 0);
2236
2242
  const primaryWidth = label.text === formatPrice(label.price) ? getPriceLabelWidth(label.text, labelPaddingX) : Math.ceil(ctx.measureText(label.text).width) + labelPaddingX * 2;
2237
2243
  let subtextWidth = 0;
2238
- if (subtext) {
2244
+ if (subtexts.length > 0) {
2239
2245
  const baseFont = ctx.font;
2240
2246
  ctx.font = `${subtextFontSize}px ${mergedOptions.fontFamily}`;
2241
- subtextWidth = Math.ceil(ctx.measureText(subtext).width) + labelPaddingX * 2;
2247
+ subtextWidth = Math.max(...subtexts.map((subtext) => Math.ceil(ctx.measureText(subtext).width) + labelPaddingX * 2));
2242
2248
  ctx.font = baseFont;
2243
2249
  }
2244
2250
  const labelTextWidth = Math.max(primaryWidth, subtextWidth);
2245
2251
  return {
2246
2252
  ...label,
2247
- subtext,
2253
+ subtexts,
2248
2254
  subtextFontSize,
2249
2255
  height: labelHeight,
2250
2256
  width: labelTextWidth,
@@ -2255,7 +2261,7 @@ function createChart(element, options = {}) {
2255
2261
  const minY = chartTop;
2256
2262
  let cursorY = minY;
2257
2263
  for (const label of positionedLabels) {
2258
- const maxY = chartBottom - label.height;
2264
+ const maxY = Math.max(minY, chartBottom - label.height);
2259
2265
  label.y = labels.noOverlapping ? Math.max(clamp(label.targetY, minY, maxY), cursorY) : clamp(label.targetY, minY, maxY);
2260
2266
  cursorY = label.y + label.height + 2;
2261
2267
  }
@@ -2272,19 +2278,22 @@ function createChart(element, options = {}) {
2272
2278
  for (const label of positionedLabels) {
2273
2279
  ctx.fillStyle = label.backgroundColor;
2274
2280
  fillRoundedRect(Math.round(labelX), Math.round(label.y), label.width, label.height, labelRadius);
2275
- const primaryY = label.subtext ? label.y + baseLabelHeight / 2 - 1 : label.y + label.height / 2;
2281
+ const hasSubtexts = label.subtexts.length > 0;
2282
+ const primaryY = hasSubtexts ? label.y + baseLabelHeight / 2 - 1 : label.y + label.height / 2;
2276
2283
  drawText(label.text, labelX + labelPaddingX, primaryY, "left", "middle", label.textColor);
2277
- if (label.subtext) {
2284
+ if (hasSubtexts) {
2278
2285
  const baseFont = ctx.font;
2279
2286
  ctx.font = `${label.subtextFontSize}px ${mergedOptions.fontFamily}`;
2280
- drawText(
2281
- label.subtext,
2282
- labelX + labelPaddingX,
2283
- label.y + baseLabelHeight + label.subtextFontSize / 2,
2284
- "left",
2285
- "middle",
2286
- label.subtextColor ?? label.textColor
2287
- );
2287
+ label.subtexts.forEach((subtext, index) => {
2288
+ drawText(
2289
+ subtext,
2290
+ labelX + labelPaddingX,
2291
+ label.y + baseLabelHeight + index * (label.subtextFontSize + 5) + label.subtextFontSize / 2,
2292
+ "left",
2293
+ "middle",
2294
+ label.subtextColor ?? label.textColor
2295
+ );
2296
+ });
2288
2297
  ctx.font = baseFont;
2289
2298
  }
2290
2299
  }
package/dist/index.d.cts CHANGED
@@ -261,6 +261,7 @@ interface TickerLineOptions {
261
261
  labelBackgroundColor?: string;
262
262
  labelTextColor?: string;
263
263
  labelSubtext?: string;
264
+ labelSubtexts?: Array<string | number>;
264
265
  labelSubtextColor?: string;
265
266
  labelSubtextFontSize?: number;
266
267
  showCountdownInLabel?: boolean;
package/dist/index.d.ts CHANGED
@@ -261,6 +261,7 @@ interface TickerLineOptions {
261
261
  labelBackgroundColor?: string;
262
262
  labelTextColor?: string;
263
263
  labelSubtext?: string;
264
+ labelSubtexts?: Array<string | number>;
264
265
  labelSubtextColor?: string;
265
266
  labelSubtextFontSize?: number;
266
267
  showCountdownInLabel?: boolean;
package/dist/index.js CHANGED
@@ -178,6 +178,7 @@ var DEFAULT_OPTIONS = {
178
178
  thickness: 1,
179
179
  labelTextColor: "#0b1220",
180
180
  labelSubtext: "",
181
+ labelSubtexts: [],
181
182
  labelSubtextColor: "#0b1220",
182
183
  labelSubtextFontSize: 0,
183
184
  showCountdownInLabel: false,
@@ -2117,10 +2118,14 @@ function createChart(element, options = {}) {
2117
2118
  ctx.restore();
2118
2119
  }
2119
2120
  if ((ticker.visible ?? true) && labels.showLastPrice && tickerPrice !== null && tickerColor !== null) {
2120
- const tickerSubtext = ticker.showCountdownInLabel ? getCountdownText() : ticker.labelSubtext?.trim();
2121
+ const tickerSubtexts = [
2122
+ ...ticker.labelSubtext ? [ticker.labelSubtext] : [],
2123
+ ...ticker.labelSubtexts ?? [],
2124
+ ...ticker.showCountdownInLabel ? [getCountdownText()] : []
2125
+ ].map((value) => value === null || value === void 0 ? "" : String(value).trim()).filter((value) => value.length > 0);
2121
2126
  addPriceAxisLabel({
2122
2127
  text: formatPrice(tickerPrice),
2123
- ...tickerSubtext ? { subtext: tickerSubtext } : {},
2128
+ ...tickerSubtexts.length > 0 ? { subtexts: tickerSubtexts } : {},
2124
2129
  subtextColor: ticker.labelSubtextColor ?? ticker.labelTextColor ?? "#0b1220",
2125
2130
  ...ticker.labelSubtextFontSize === void 0 ? {} : { subtextFontSize: ticker.labelSubtextFontSize },
2126
2131
  price: tickerPrice,
@@ -2206,21 +2211,22 @@ function createChart(element, options = {}) {
2206
2211
  const priceLabelFontSize = Math.max(8, axis.fontSize);
2207
2212
  ctx.font = `${priceLabelFontSize}px ${mergedOptions.fontFamily}`;
2208
2213
  const positionedLabels = priceAxisLabels.map((label) => {
2209
- const subtext = label.subtext?.trim();
2214
+ const subtexts = (label.subtexts ?? []).map((value) => value.trim()).filter((value) => value.length > 0);
2210
2215
  const subtextFontSize = label.subtextFontSize !== void 0 && label.subtextFontSize > 0 ? Math.max(8, Math.round(label.subtextFontSize)) : priceLabelFontSize;
2211
- const labelHeight = baseLabelHeight + (subtext ? subtextFontSize + 5 : 0);
2216
+ const subtextLineGap = 5;
2217
+ const labelHeight = baseLabelHeight + (subtexts.length > 0 ? subtexts.length * subtextFontSize + subtexts.length * subtextLineGap : 0);
2212
2218
  const primaryWidth = label.text === formatPrice(label.price) ? getPriceLabelWidth(label.text, labelPaddingX) : Math.ceil(ctx.measureText(label.text).width) + labelPaddingX * 2;
2213
2219
  let subtextWidth = 0;
2214
- if (subtext) {
2220
+ if (subtexts.length > 0) {
2215
2221
  const baseFont = ctx.font;
2216
2222
  ctx.font = `${subtextFontSize}px ${mergedOptions.fontFamily}`;
2217
- subtextWidth = Math.ceil(ctx.measureText(subtext).width) + labelPaddingX * 2;
2223
+ subtextWidth = Math.max(...subtexts.map((subtext) => Math.ceil(ctx.measureText(subtext).width) + labelPaddingX * 2));
2218
2224
  ctx.font = baseFont;
2219
2225
  }
2220
2226
  const labelTextWidth = Math.max(primaryWidth, subtextWidth);
2221
2227
  return {
2222
2228
  ...label,
2223
- subtext,
2229
+ subtexts,
2224
2230
  subtextFontSize,
2225
2231
  height: labelHeight,
2226
2232
  width: labelTextWidth,
@@ -2231,7 +2237,7 @@ function createChart(element, options = {}) {
2231
2237
  const minY = chartTop;
2232
2238
  let cursorY = minY;
2233
2239
  for (const label of positionedLabels) {
2234
- const maxY = chartBottom - label.height;
2240
+ const maxY = Math.max(minY, chartBottom - label.height);
2235
2241
  label.y = labels.noOverlapping ? Math.max(clamp(label.targetY, minY, maxY), cursorY) : clamp(label.targetY, minY, maxY);
2236
2242
  cursorY = label.y + label.height + 2;
2237
2243
  }
@@ -2248,19 +2254,22 @@ function createChart(element, options = {}) {
2248
2254
  for (const label of positionedLabels) {
2249
2255
  ctx.fillStyle = label.backgroundColor;
2250
2256
  fillRoundedRect(Math.round(labelX), Math.round(label.y), label.width, label.height, labelRadius);
2251
- const primaryY = label.subtext ? label.y + baseLabelHeight / 2 - 1 : label.y + label.height / 2;
2257
+ const hasSubtexts = label.subtexts.length > 0;
2258
+ const primaryY = hasSubtexts ? label.y + baseLabelHeight / 2 - 1 : label.y + label.height / 2;
2252
2259
  drawText(label.text, labelX + labelPaddingX, primaryY, "left", "middle", label.textColor);
2253
- if (label.subtext) {
2260
+ if (hasSubtexts) {
2254
2261
  const baseFont = ctx.font;
2255
2262
  ctx.font = `${label.subtextFontSize}px ${mergedOptions.fontFamily}`;
2256
- drawText(
2257
- label.subtext,
2258
- labelX + labelPaddingX,
2259
- label.y + baseLabelHeight + label.subtextFontSize / 2,
2260
- "left",
2261
- "middle",
2262
- label.subtextColor ?? label.textColor
2263
- );
2263
+ label.subtexts.forEach((subtext, index) => {
2264
+ drawText(
2265
+ subtext,
2266
+ labelX + labelPaddingX,
2267
+ label.y + baseLabelHeight + index * (label.subtextFontSize + 5) + label.subtextFontSize / 2,
2268
+ "left",
2269
+ "middle",
2270
+ label.subtextColor ?? label.textColor
2271
+ );
2272
+ });
2264
2273
  ctx.font = baseFont;
2265
2274
  }
2266
2275
  }
package/docs/API.md CHANGED
@@ -149,10 +149,11 @@ watermark: {
149
149
  - `color` (default `#38bdf8`)
150
150
  - `labelBackgroundColor` (default `#38bdf8`)
151
151
  - `labelTextColor` (default `#0b1220`)
152
- - `labelSubtext` (optional second line inside the current-price label, for example `"MNQ"`)
152
+ - `labelSubtext` (optional single extra line inside the current-price label, for example `"MNQ"`)
153
+ - `labelSubtexts` (optional array of extra lines inside the current-price label, for example `["MNQ", "RTH"]`)
153
154
  - `labelSubtextColor` (defaults to the label text color)
154
- - `labelSubtextFontSize` (default `0`, meaning use the same font size as the price)
155
- - `showCountdownInLabel` (default `false`; renders bar-close countdown as the second line)
155
+ - `labelSubtextFontSize` (default `0`, meaning each extra line uses the same font size as the price)
156
+ - `showCountdownInLabel` (default `false`; appends bar-close countdown as another extra line)
156
157
  - `labelBorderRadius` (default `3`)
157
158
 
158
159
  ### `LabelsOptions`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperprop-charting-library",
3
- "version": "0.1.46",
3
+ "version": "0.1.47",
4
4
  "description": "Lightweight TypeScript charting core",
5
5
  "type": "module",
6
6
  "main": "./dist/hyperprop-charting-library.cjs",