wick-charts 0.10.0 → 0.11.0

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
@@ -256,13 +256,19 @@ const chart = createCandlestickChart(canvas, {
256
256
  `시가 ${candle.open.toLocaleString()}`,
257
257
  `고가 ${candle.high.toLocaleString()}`,
258
258
  `저가 ${candle.low.toLocaleString()}`,
259
- `종가 ${candle.close.toLocaleString()}`,
260
259
  // A line can be a plain string (drawn in legend.textColor, like the
261
- // four above) or { text, color } for one that needs its own color —
262
- // see "Coloring individual legend lines" just below.
260
+ // three above), { text, color } for a whole line in its own color, or
261
+ // an array of { text, color } segments to color only *part* of one
262
+ // line — see "Coloring individual legend lines" just below.
263
263
  change === null
264
- ? '등락 —'
265
- : { text: `등락 ${change >= 0 ? '+' : ''}${change.toFixed(2)}%`, color: change >= 0 ? '#22c55e' : '#ef4444' },
264
+ ? `종가 ${candle.close.toLocaleString()}`
265
+ : [
266
+ { text: `종가 ${candle.close.toLocaleString()} ` },
267
+ {
268
+ text: `(${change >= 0 ? '+' : ''}${change.toFixed(2)}%)`,
269
+ color: change >= 0 ? '#22c55e' : '#ef4444',
270
+ },
271
+ ],
266
272
  ];
267
273
  },
268
274
  });
@@ -276,12 +282,13 @@ the previous point regardless of where the user has panned/zoomed to.
276
282
 
277
283
  #### Coloring individual legend lines
278
284
 
279
- Every line returned by `formatLegend` is a `LegendLine` — either a plain `string`, drawn in
280
- `legend.textColor` like every line before this existed, or `{ text, color }` for one line that
281
- needs its own color independent of the rest (the `change` line above: green when positive, red
282
- when negative, regardless of `legend.textColor`). Mixing both shapes in one returned array, as
283
- above, is the normal case — only the lines that actually need a distinct color use the object
284
- form. There's no chart-wide "green means up" setting for this: which color means "up" is a
285
+ Every line returned by `formatLegend` is a `LegendLine` — a plain `string`, drawn in
286
+ `legend.textColor` like every line before this existed; `{ text, color }` for one line that
287
+ needs its own color independent of the rest; or a `LegendSegment[]` (`{ text, color? }[]`) for
288
+ several colored runs on the *same* line — the `종가` line above keeps its label in
289
+ `legend.textColor` and colors only the `(±%)` suffix, rather than tinting the whole line green
290
+ or red. Mixing all three shapes in one returned array, as above, is the normal case — only the
291
+ text that actually needs a distinct color gets it. There's no chart-wide "green means up" setting for this: which color means "up" is a
285
292
  convention that varies by market (green-up in most markets, red-up in South Korea and a few
286
293
  others) and isn't something the library should hardcode, so `formatLegend` decides it per line
287
294
  the same way it decides everything else about the legend's text. This is separate from
@@ -1,6 +1,16 @@
1
1
  import { formatHoverTime } from './axis.js';
2
2
  import { formatPrice } from './priceAxis.js';
3
3
  import { pixelToValue } from './valueAxis.js';
4
+ /** Normalizes any `LegendLine` shape to the one form actually drawn:
5
+ * a list of colored segments (a plain string/line becomes one segment
6
+ * with no color override). */
7
+ function toSegments(line) {
8
+ if (typeof line === 'string')
9
+ return [{ text: line }];
10
+ if (Array.isArray(line))
11
+ return line;
12
+ return [line];
13
+ }
4
14
  /**
5
15
  * Draws the hover crosshair (dashed lines), its price/time label chips,
6
16
  * and the OHLC-style legend tooltip. Split out of `ChartRenderer` for the
@@ -75,12 +85,12 @@ export class CrosshairRenderer {
75
85
  ctx.font = this.legendFont();
76
86
  ctx.textAlign = 'left';
77
87
  ctx.textBaseline = 'top';
78
- // Each line is either a plain string or { text, color } — normalize to
79
- // its text once up front for sizing, and read its own color (falling
80
- // back to legend.textColor) only when actually drawing it below.
81
- const texts = lines.map((line) => (typeof line === 'string' ? line : line.text));
88
+ // Normalize every line to its segments once up front — sizing needs
89
+ // each line's full concatenated width; drawing needs each segment's
90
+ // own color (falling back to legend.textColor).
91
+ const segmentedLines = lines.map(toSegments);
82
92
  const lineHeight = font.legendSize + 4;
83
- const textWidth = Math.max(...texts.map((text) => ctx.measureText(text).width));
93
+ const textWidth = Math.max(...segmentedLines.map((segments) => segments.reduce((w, seg) => w + ctx.measureText(seg.text).width, 0)));
84
94
  const boxWidth = textWidth + legend.paddingX * 2;
85
95
  const boxHeight = lines.length * lineHeight + legend.paddingY * 2;
86
96
  const anchorY = hoverY ?? 0;
@@ -88,9 +98,14 @@ export class CrosshairRenderer {
88
98
  const top = Math.min(Math.max(anchorY - boxHeight - legend.cursorGap, 0), Math.max(0, chartHeight - boxHeight));
89
99
  ctx.fillStyle = legend.background;
90
100
  ctx.fillRect(left, top, boxWidth, boxHeight);
91
- lines.forEach((line, i) => {
92
- ctx.fillStyle = typeof line === 'string' ? legend.textColor : (line.color ?? legend.textColor);
93
- ctx.fillText(texts[i], left + legend.paddingX, top + legend.paddingY + i * lineHeight);
101
+ segmentedLines.forEach((segments, i) => {
102
+ let segX = left + legend.paddingX;
103
+ const y = top + legend.paddingY + i * lineHeight;
104
+ for (const seg of segments) {
105
+ ctx.fillStyle = seg.color ?? legend.textColor;
106
+ ctx.fillText(seg.text, segX, y);
107
+ segX += ctx.measureText(seg.text).width;
108
+ }
94
109
  });
95
110
  }
96
111
  /** The highlighted price-axis label that follows the crosshair's
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ import type { ChartPlugin } from './plugins/types.js';
3
3
  import type { CandlestickStyle } from './series/candlestick.js';
4
4
  import type { LineStyle } from './series/line.js';
5
5
  import type { Candle, LegendFormatContext, LegendLine, LinePoint, PaneOptions, WickChartOptions, SeriesPoint, ValueRange, WickTime } from './types.js';
6
- export type { BusinessDay, Candle, LegendFormatContext, LegendLine, LinePoint, PaneOptions, WickChartOptions, WickTime, SeriesPoint, UnixMillis, ValueRange, } from './types.js';
6
+ export type { BusinessDay, Candle, LegendFormatContext, LegendLine, LegendSegment, LinePoint, PaneOptions, WickChartOptions, WickTime, SeriesPoint, UnixMillis, ValueRange, } from './types.js';
7
7
  export type { DataLoader, DataRequest } from './dataSource.js';
8
8
  export type { ChartPlugin, ChartPointerEvent, PluginRenderApi } from './plugins/types.js';
9
9
  export { distanceToSegment, hitTestPoint, hitTestSegment } from './hitTest.js';
package/dist/types.d.ts CHANGED
@@ -193,18 +193,22 @@ export interface LegendFormatContext<TPoint extends SeriesPoint = SeriesPoint> {
193
193
  * time — the same array `SeriesDrawContext.allPoints` is. */
194
194
  allPoints: readonly TPoint[];
195
195
  }
196
- /**
197
- * One line of the hover legend — a plain `string` (drawn in the legend's
198
- * configured `textColor`, same as every version of this library before
199
- * per-line color existed) or `{ text, color }` for a line that needs its
200
- * own color independent of the rest — a percent-change line that should
201
- * read green/red by sign, say. Mixing both in the same `formatLegend`
202
- * return array is fine: each line is colored independently.
203
- */
204
- export type LegendLine = string | {
196
+ /** One colored run of text within a legend line — see `LegendLine`. */
197
+ export interface LegendSegment {
205
198
  text: string;
206
199
  color?: string;
207
- };
200
+ }
201
+ /**
202
+ * One line of the hover legend:
203
+ * - a plain `string`, drawn in the legend's configured `textColor`;
204
+ * - `{ text, color }`, a whole line in its own color;
205
+ * - `LegendSegment[]`, several runs of text drawn left-to-right on the
206
+ * same line, each independently colored — e.g. a plain label followed
207
+ * by a colored `(+1.23%)` suffix, without tinting the label too.
208
+ * Mixing all three shapes in the same `formatLegend` return array is fine:
209
+ * each line is laid out independently.
210
+ */
211
+ export type LegendLine = string | LegendSegment | LegendSegment[];
208
212
  export interface WickChartOptions {
209
213
  /**
210
214
  * Which registered series type to render this chart as (see
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wick-charts",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "An open-source financial charting library — WASM (Rust) for compute, Canvas2D for rendering.",
5
5
  "license": "MIT",
6
6
  "author": "eatnows",