chore: add AGENTS.md with build, lint, test commands and style guidelines
This commit is contained in:
117
js/ui/markers-plugin.js
Normal file
117
js/ui/markers-plugin.js
Normal file
@ -0,0 +1,117 @@
|
||||
export class SeriesMarkersPrimitive {
|
||||
constructor(markers) {
|
||||
this._markers = markers || [];
|
||||
this._paneViews = [new MarkersPaneView(this)];
|
||||
}
|
||||
|
||||
setMarkers(markers) {
|
||||
this._markers = markers;
|
||||
if (this._requestUpdate) {
|
||||
this._requestUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
attached(param) {
|
||||
this._chart = param.chart;
|
||||
this._series = param.series;
|
||||
this._requestUpdate = param.requestUpdate;
|
||||
this._requestUpdate();
|
||||
}
|
||||
|
||||
detached() {
|
||||
this._chart = undefined;
|
||||
this._series = undefined;
|
||||
this._requestUpdate = undefined;
|
||||
}
|
||||
|
||||
updateAllViews() {}
|
||||
|
||||
paneViews() {
|
||||
return this._paneViews;
|
||||
}
|
||||
}
|
||||
|
||||
class MarkersPaneView {
|
||||
constructor(source) {
|
||||
this._source = source;
|
||||
}
|
||||
|
||||
renderer() {
|
||||
return new MarkersRenderer(this._source);
|
||||
}
|
||||
}
|
||||
|
||||
class MarkersRenderer {
|
||||
constructor(source) {
|
||||
this._source = source;
|
||||
}
|
||||
|
||||
draw(target) {
|
||||
if (!this._source._chart || !this._source._series) return;
|
||||
|
||||
// Lightweight Charts v5 wraps context
|
||||
const ctx = target.context;
|
||||
const series = this._source._series;
|
||||
const chart = this._source._chart;
|
||||
const markers = this._source._markers;
|
||||
|
||||
ctx.save();
|
||||
|
||||
// Ensure markers are sorted by time (usually already done)
|
||||
for (const marker of markers) {
|
||||
const timeCoordinate = chart.timeScale().timeToCoordinate(marker.time);
|
||||
if (timeCoordinate === null) continue;
|
||||
|
||||
// To position above or below bar, we need the candle data or we use the marker.value if provided
|
||||
// For true aboveBar/belowBar without candle data, we might just use series.priceToCoordinate on marker.value
|
||||
let price = marker.value;
|
||||
// Fallbacks if no value provided (which our calculator does provide)
|
||||
if (!price) continue;
|
||||
|
||||
const priceCoordinate = series.priceToCoordinate(price);
|
||||
if (priceCoordinate === null) continue;
|
||||
|
||||
const x = timeCoordinate;
|
||||
const size = 5;
|
||||
const margin = 12; // Gap between price and marker
|
||||
const isAbove = marker.position === 'aboveBar';
|
||||
const y = isAbove ? priceCoordinate - margin : priceCoordinate + margin;
|
||||
|
||||
ctx.fillStyle = marker.color || '#26a69a';
|
||||
ctx.beginPath();
|
||||
|
||||
if (marker.shape === 'arrowUp' || (!marker.shape && !isAbove)) {
|
||||
ctx.moveTo(x, y - size);
|
||||
ctx.lineTo(x - size, y + size);
|
||||
ctx.lineTo(x + size, y + size);
|
||||
} else if (marker.shape === 'arrowDown' || (!marker.shape && isAbove)) {
|
||||
ctx.moveTo(x, y + size);
|
||||
ctx.lineTo(x - size, y - size);
|
||||
ctx.lineTo(x + size, y - size);
|
||||
} else if (marker.shape === 'circle') {
|
||||
ctx.arc(x, y, size, 0, Math.PI * 2);
|
||||
} else if (marker.shape === 'square') {
|
||||
ctx.rect(x - size, y - size, size * 2, size * 2);
|
||||
} else if (marker.shape === 'custom' && marker.text) {
|
||||
ctx.font = '12px Arial';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(marker.text, x, y);
|
||||
continue;
|
||||
} else {
|
||||
// Default triangle
|
||||
if (isAbove) {
|
||||
ctx.moveTo(x, y + size);
|
||||
ctx.lineTo(x - size, y - size);
|
||||
ctx.lineTo(x + size, y - size);
|
||||
} else {
|
||||
ctx.moveTo(x, y - size);
|
||||
ctx.lineTo(x - size, y + size);
|
||||
ctx.lineTo(x + size, y + size);
|
||||
}
|
||||
}
|
||||
ctx.fill();
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user