From 663826cdd62362235da192de0ecca58d11c82ec1 Mon Sep 17 00:00:00 2001 From: Eugene Korobko Date: Thu, 29 Oct 2020 11:45:03 +0300 Subject: [PATCH 001/188] 606: Update scales design --- src/api/options/crosshair-options-defaults.ts | 8 +++---- src/api/options/layout-options-defaults.ts | 2 +- src/gui/labels-image-cache.ts | 7 +++---- src/gui/price-axis-widget.ts | 4 ++-- src/gui/time-axis-widget.ts | 10 ++++----- .../price-axis-renderer-options-provider.ts | 6 +++--- src/renderers/price-axis-view-renderer.ts | 21 +++++++++++++++---- src/renderers/time-axis-view-renderer.ts | 2 +- 8 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/api/options/crosshair-options-defaults.ts b/src/api/options/crosshair-options-defaults.ts index 7d04145b19..0fa0116927 100644 --- a/src/api/options/crosshair-options-defaults.ts +++ b/src/api/options/crosshair-options-defaults.ts @@ -3,20 +3,20 @@ import { LineStyle } from '../../renderers/draw-line'; export const crosshairOptionsDefaults: CrosshairOptions = { vertLine: { - color: '#758696', + color: '#EF9A9A', width: 1, style: LineStyle.LargeDashed, visible: true, labelVisible: true, - labelBackgroundColor: '#4c525e', + labelBackgroundColor: '#131722', }, horzLine: { - color: '#758696', + color: '#EF9A9A', width: 1, style: LineStyle.LargeDashed, visible: true, labelVisible: true, - labelBackgroundColor: '#4c525e', + labelBackgroundColor: '#131722', }, mode: CrosshairMode.Magnet, }; diff --git a/src/api/options/layout-options-defaults.ts b/src/api/options/layout-options-defaults.ts index c59b7b732f..9cfbaff884 100644 --- a/src/api/options/layout-options-defaults.ts +++ b/src/api/options/layout-options-defaults.ts @@ -5,6 +5,6 @@ import { LayoutOptions } from '../../model/layout-options'; export const layoutOptionsDefaults: LayoutOptions = { backgroundColor: '#FFFFFF', textColor: '#191919', - fontSize: 11, + fontSize: 12, fontFamily: defaultFontFamily, }; diff --git a/src/gui/labels-image-cache.ts b/src/gui/labels-image-cache.ts index 2820543e65..cac5c7f39f 100644 --- a/src/gui/labels-image-cache.ts +++ b/src/gui/labels-image-cache.ts @@ -67,11 +67,10 @@ export class LabelsImageCache implements IDestroyable { const pixelRatio = getCanvasDevicePixelRatio(ctx.canvas); - const margin = Math.ceil(this._fontSize / 4.5); const baselineOffset = Math.round(this._fontSize / 10); const textWidth = Math.ceil(this._textWidthCache.measureText(ctx, text)); - const width = ceiledEven(Math.round(textWidth + margin * 2)); - const height = ceiledEven(this._fontSize + margin * 2); + const width = ceiledEven(Math.round(textWidth)); + const height = ceiledEven(this._fontSize); const canvas = createPreconfiguredCanvas(document, new Size(width, height)); // Allocate new @@ -92,7 +91,7 @@ export class LabelsImageCache implements IDestroyable { drawScaled(ctx, pixelRatio, () => { ctx.font = this._font; ctx.fillStyle = this._color; - ctx.fillText(text, 0, height - margin - baselineOffset); + ctx.fillText(text, 0, height - baselineOffset); }); } diff --git a/src/gui/price-axis-widget.ts b/src/gui/price-axis-widget.ts index 8c4d2c3dfd..1c3a39359e 100644 --- a/src/gui/price-axis-widget.ts +++ b/src/gui/price-axis-widget.ts @@ -422,8 +422,8 @@ export class PriceAxisWidget implements IDestroyable { const drawTicks = this._priceScale.options().borderVisible && this._priceScale.options().drawTicks; const tickMarkLeftX = this._isLeft ? - Math.floor((this._size.w - rendererOptions.tickLength) * pixelRatio - rendererOptions.borderSize * pixelRatio) : - Math.floor(rendererOptions.borderSize * pixelRatio); + Math.floor((this._size.w - rendererOptions.tickLength) * pixelRatio) : + 0; const textLeftX = this._isLeft ? Math.round(tickMarkLeftX - rendererOptions.paddingInner * pixelRatio) : diff --git a/src/gui/time-axis-widget.ts b/src/gui/time-axis-widget.ts index d3ffef40b8..f52fc38ce8 100644 --- a/src/gui/time-axis-widget.ts +++ b/src/gui/time-axis-widget.ts @@ -19,7 +19,7 @@ import { PriceAxisStub, PriceAxisStubParams } from './price-axis-stub'; const enum Constants { BorderSize = 1, - TickLength = 3, + TickLength = 5, } const enum CursorType { @@ -306,7 +306,6 @@ export class TimeAxisWidget implements MouseEventHandlers, IDestroyable { const rendererOptions = this._getRendererOptions(); const yText = ( rendererOptions.borderSize + - rendererOptions.tickLength + rendererOptions.paddingTop + rendererOptions.fontSize - rendererOptions.baselineOffset @@ -315,7 +314,6 @@ export class TimeAxisWidget implements MouseEventHandlers, IDestroyable { ctx.textAlign = 'center'; ctx.fillStyle = this._lineColor(); - const borderSize = Math.floor(this._getRendererOptions().borderSize * pixelRatio); const tickWidth = Math.max(1, Math.floor(pixelRatio)); const tickOffset = Math.floor(pixelRatio * 0.5); @@ -324,7 +322,7 @@ export class TimeAxisWidget implements MouseEventHandlers, IDestroyable { const tickLen = Math.round(rendererOptions.tickLength * pixelRatio); for (let index = tickMarks.length; index--;) { const x = Math.round(tickMarks[index].coord * pixelRatio); - ctx.rect(x - tickOffset, borderSize, tickWidth, tickLen); + ctx.rect(x - tickOffset, 0, tickWidth, tickLen); } ctx.fill(); @@ -406,8 +404,8 @@ export class TimeAxisWidget implements MouseEventHandlers, IDestroyable { const fontSize = this._fontSize(); rendererOptions.fontSize = fontSize; rendererOptions.font = newFont; - rendererOptions.paddingTop = Math.ceil(fontSize / 2.5); - rendererOptions.paddingBottom = rendererOptions.paddingTop; + rendererOptions.paddingTop = Math.ceil(fontSize / 2.5) + 2; + rendererOptions.paddingBottom = rendererOptions.paddingTop - 1; rendererOptions.paddingHorizontal = Math.ceil(fontSize / 2); rendererOptions.baselineOffset = Math.round(this._fontSize() / 5); rendererOptions.widthCache.reset(); diff --git a/src/renderers/price-axis-renderer-options-provider.ts b/src/renderers/price-axis-renderer-options-provider.ts index b6d8327768..0d862648e8 100644 --- a/src/renderers/price-axis-renderer-options-provider.ts +++ b/src/renderers/price-axis-renderer-options-provider.ts @@ -6,7 +6,7 @@ import { PriceAxisViewRendererOptions } from './iprice-axis-view-renderer'; const enum RendererConstants { BorderSize = 1, - TickLength = 4, + TickLength = 5, } export class PriceAxisRendererOptionsProvider { @@ -40,8 +40,8 @@ export class PriceAxisRendererOptionsProvider { rendererOptions.fontSize = currentFontSize; rendererOptions.fontFamily = currentFontFamily; rendererOptions.font = makeFont(currentFontSize, currentFontFamily); - rendererOptions.paddingTop = Math.floor(currentFontSize / 3.5); - rendererOptions.paddingBottom = rendererOptions.paddingTop; + rendererOptions.paddingTop = Math.max(0, Math.floor(currentFontSize / 3.5) - 1); + rendererOptions.paddingBottom = rendererOptions.paddingTop + 1; rendererOptions.paddingInner = Math.max( Math.ceil(currentFontSize / 2 - rendererOptions.tickLength / 2), 0 diff --git a/src/renderers/price-axis-view-renderer.ts b/src/renderers/price-axis-view-renderer.ts index 115dbc627d..e5d5576814 100644 --- a/src/renderers/price-axis-view-renderer.ts +++ b/src/renderers/price-axis-view-renderer.ts @@ -106,11 +106,24 @@ export class PriceAxisViewRenderer implements IPriceAxisViewRenderer { ctx.save(); + const radius = 2 * pixelRatio; + ctx.beginPath(); - ctx.moveTo(xInsideScaled, yTopScaled); - ctx.lineTo(xOutsideScaled, yTopScaled); - ctx.lineTo(xOutsideScaled, yBottomScaled); - ctx.lineTo(xInsideScaled, yBottomScaled); + if (alignRight) { + ctx.moveTo(xInsideScaled, yTopScaled); + ctx.lineTo(xOutsideScaled + radius, yTopScaled); + ctx.arcTo(xOutsideScaled, yTopScaled, xOutsideScaled, yTopScaled + radius, radius); + ctx.lineTo(xOutsideScaled, yBottomScaled - radius); + ctx.arcTo(xOutsideScaled, yBottomScaled, xOutsideScaled + radius, yBottomScaled, radius); + ctx.lineTo(xInsideScaled, yBottomScaled); + } else { + ctx.moveTo(xInsideScaled, yTopScaled); + ctx.lineTo(xOutsideScaled - radius, yTopScaled); + ctx.arcTo(xOutsideScaled, yTopScaled, xOutsideScaled, yTopScaled + radius, radius); + ctx.lineTo(xOutsideScaled, yBottomScaled - radius); + ctx.arcTo(xOutsideScaled, yBottomScaled, xOutsideScaled - radius, yBottomScaled, radius); + ctx.lineTo(xInsideScaled, yBottomScaled); + } ctx.fill(); // draw border diff --git a/src/renderers/time-axis-view-renderer.ts b/src/renderers/time-axis-view-renderer.ts index 31164c04d9..8fd72ded65 100644 --- a/src/renderers/time-axis-view-renderer.ts +++ b/src/renderers/time-axis-view-renderer.ts @@ -75,7 +75,7 @@ export class TimeAxisViewRenderer implements ITimeAxisViewRenderer { const tickX = Math.round(this._data.coordinate * pixelRatio); const tickTop = y1scaled; - const tickBottom = Math.round((tickTop + rendererOptions.borderSize + rendererOptions.tickLength) * pixelRatio); + const tickBottom = Math.round((tickTop + rendererOptions.tickLength) * pixelRatio); ctx.fillStyle = this._data.color; const tickWidth = Math.max(1, Math.floor(pixelRatio)); From 54c20dfb087cd7805df22c859d100c434d4d9481 Mon Sep 17 00:00:00 2001 From: Eugene Korobko Date: Fri, 6 Nov 2020 13:30:59 +0300 Subject: [PATCH 002/188] 606: Temporary restored font size --- src/api/options/layout-options-defaults.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/options/layout-options-defaults.ts b/src/api/options/layout-options-defaults.ts index 9cfbaff884..c59b7b732f 100644 --- a/src/api/options/layout-options-defaults.ts +++ b/src/api/options/layout-options-defaults.ts @@ -5,6 +5,6 @@ import { LayoutOptions } from '../../model/layout-options'; export const layoutOptionsDefaults: LayoutOptions = { backgroundColor: '#FFFFFF', textColor: '#191919', - fontSize: 12, + fontSize: 11, fontFamily: defaultFontFamily, }; From e3b085088facf9de446cf65d2bd64977597311c1 Mon Sep 17 00:00:00 2001 From: Eugene Korobko Date: Fri, 6 Nov 2020 13:37:29 +0300 Subject: [PATCH 003/188] 606: Fixed crosshair color --- src/api/options/crosshair-options-defaults.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/options/crosshair-options-defaults.ts b/src/api/options/crosshair-options-defaults.ts index 0fa0116927..ccd985acec 100644 --- a/src/api/options/crosshair-options-defaults.ts +++ b/src/api/options/crosshair-options-defaults.ts @@ -3,7 +3,7 @@ import { LineStyle } from '../../renderers/draw-line'; export const crosshairOptionsDefaults: CrosshairOptions = { vertLine: { - color: '#EF9A9A', + color: '#9598A1', width: 1, style: LineStyle.LargeDashed, visible: true, @@ -11,7 +11,7 @@ export const crosshairOptionsDefaults: CrosshairOptions = { labelBackgroundColor: '#131722', }, horzLine: { - color: '#EF9A9A', + color: '#9598A1', width: 1, style: LineStyle.LargeDashed, visible: true, From 6726328d868f6ea92669550a7b9d41eae353e186 Mon Sep 17 00:00:00 2001 From: Eugene Korobko Date: Tue, 17 Nov 2020 11:31:54 +0300 Subject: [PATCH 004/188] 606: Fixed painting issues --- src/api/options/layout-options-defaults.ts | 2 +- src/gui/labels-image-cache.ts | 3 ++- src/gui/price-axis-widget.ts | 7 ++++++- src/renderers/price-axis-renderer-options-provider.ts | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/api/options/layout-options-defaults.ts b/src/api/options/layout-options-defaults.ts index c59b7b732f..9cfbaff884 100644 --- a/src/api/options/layout-options-defaults.ts +++ b/src/api/options/layout-options-defaults.ts @@ -5,6 +5,6 @@ import { LayoutOptions } from '../../model/layout-options'; export const layoutOptionsDefaults: LayoutOptions = { backgroundColor: '#FFFFFF', textColor: '#191919', - fontSize: 11, + fontSize: 12, fontFamily: defaultFontFamily, }; diff --git a/src/gui/labels-image-cache.ts b/src/gui/labels-image-cache.ts index cac5c7f39f..2572622159 100644 --- a/src/gui/labels-image-cache.ts +++ b/src/gui/labels-image-cache.ts @@ -69,7 +69,8 @@ export class LabelsImageCache implements IDestroyable { const baselineOffset = Math.round(this._fontSize / 10); const textWidth = Math.ceil(this._textWidthCache.measureText(ctx, text)); - const width = ceiledEven(Math.round(textWidth)); + // small reserve for antialiasing, measureText is not sharp + const width = ceiledEven(Math.round(textWidth) + 4); const height = ceiledEven(this._fontSize); const canvas = createPreconfiguredCanvas(document, new Size(width, height)); diff --git a/src/gui/price-axis-widget.ts b/src/gui/price-axis-widget.ts index 1c3a39359e..4f093cd6ba 100644 --- a/src/gui/price-axis-widget.ts +++ b/src/gui/price-axis-widget.ts @@ -31,6 +31,10 @@ const enum CursorType { type IPriceAxisViewArray = readonly IPriceAxisView[]; +const enum Constants { + LabelOffset = 4, +} + export class PriceAxisWidget implements IDestroyable { private readonly _pane: PaneWidget; private readonly _options: LayoutOptions; @@ -204,7 +208,8 @@ export class PriceAxisWidget implements IDestroyable { rendererOptions.tickLength + rendererOptions.paddingInner + rendererOptions.paddingOuter + - tickMarkMaxWidth + tickMarkMaxWidth + + Constants.LabelOffset ); // make it even res += res % 2; diff --git a/src/renderers/price-axis-renderer-options-provider.ts b/src/renderers/price-axis-renderer-options-provider.ts index 0d862648e8..c63d501a04 100644 --- a/src/renderers/price-axis-renderer-options-provider.ts +++ b/src/renderers/price-axis-renderer-options-provider.ts @@ -46,7 +46,7 @@ export class PriceAxisRendererOptionsProvider { Math.ceil(currentFontSize / 2 - rendererOptions.tickLength / 2), 0 ); - rendererOptions.paddingOuter = Math.ceil(currentFontSize / 2 + rendererOptions.tickLength / 2); + rendererOptions.paddingOuter = rendererOptions.tickLength; rendererOptions.baselineOffset = Math.round(currentFontSize / 10); } From 8783d56a69555e0996aaa5caa2d7d0fb5424288f Mon Sep 17 00:00:00 2001 From: Eugene Korobko Date: Mon, 30 Nov 2020 13:57:55 +0300 Subject: [PATCH 005/188] 606: Fixed font size and margins --- src/gui/labels-image-cache.ts | 4 +- src/helpers/make-font.ts | 7 +- .../price-axis-renderer-options-provider.ts | 11 +-- src/renderers/price-axis-view-renderer.ts | 76 ++++++++++--------- 4 files changed, 51 insertions(+), 47 deletions(-) diff --git a/src/gui/labels-image-cache.ts b/src/gui/labels-image-cache.ts index 2572622159..145b953506 100644 --- a/src/gui/labels-image-cache.ts +++ b/src/gui/labels-image-cache.ts @@ -3,7 +3,7 @@ import { createPreconfiguredCanvas, getCanvasDevicePixelRatio, getContext2D, Siz import { ensureDefined } from '../helpers/assertions'; import { drawScaled } from '../helpers/canvas-helpers'; import { IDestroyable } from '../helpers/idestroyable'; -import { makeFont } from '../helpers/make-font'; +import { fontSizeToPixels, makeFont } from '../helpers/make-font'; import { ceiledEven } from '../helpers/mathex'; import { TextWidthCache } from '../model/text-width-cache'; @@ -71,7 +71,7 @@ export class LabelsImageCache implements IDestroyable { const textWidth = Math.ceil(this._textWidthCache.measureText(ctx, text)); // small reserve for antialiasing, measureText is not sharp const width = ceiledEven(Math.round(textWidth) + 4); - const height = ceiledEven(this._fontSize); + const height = ceiledEven(fontSizeToPixels(this._fontSize)); const canvas = createPreconfiguredCanvas(document, new Size(width, height)); // Allocate new diff --git a/src/helpers/make-font.ts b/src/helpers/make-font.ts index 4cc41e4c50..7ebf2a6680 100644 --- a/src/helpers/make-font.ts +++ b/src/helpers/make-font.ts @@ -4,11 +4,16 @@ */ export const defaultFontFamily = `'Trebuchet MS', Roboto, Ubuntu, sans-serif`; +export function fontSizeToPixels(size: number): number { + return Math.round(size * 96 / 72); +} + /** * Generates a font string, which can be used to set in canvas' font property. * If no family provided, [defaultFontFamily] will be used. */ export function makeFont(size: number, family?: string, style?: string): string { + const sizeInPixels = fontSizeToPixels(size); if (style !== undefined) { style = `${style} `; } else { @@ -19,5 +24,5 @@ export function makeFont(size: number, family?: string, style?: string): string family = defaultFontFamily; } - return `${style}${size}px ${family}`; + return `${style}${sizeInPixels}px ${family}`; } diff --git a/src/renderers/price-axis-renderer-options-provider.ts b/src/renderers/price-axis-renderer-options-provider.ts index c63d501a04..19b582d56c 100644 --- a/src/renderers/price-axis-renderer-options-provider.ts +++ b/src/renderers/price-axis-renderer-options-provider.ts @@ -40,14 +40,11 @@ export class PriceAxisRendererOptionsProvider { rendererOptions.fontSize = currentFontSize; rendererOptions.fontFamily = currentFontFamily; rendererOptions.font = makeFont(currentFontSize, currentFontFamily); - rendererOptions.paddingTop = Math.max(0, Math.floor(currentFontSize / 3.5) - 1); - rendererOptions.paddingBottom = rendererOptions.paddingTop + 1; - rendererOptions.paddingInner = Math.max( - Math.ceil(currentFontSize / 2 - rendererOptions.tickLength / 2), - 0 - ); + rendererOptions.paddingTop = Math.max(0, Math.floor(currentFontSize / 3)); + rendererOptions.paddingBottom = rendererOptions.paddingTop; + rendererOptions.paddingInner = rendererOptions.tickLength; rendererOptions.paddingOuter = rendererOptions.tickLength; - rendererOptions.baselineOffset = Math.round(currentFontSize / 10); + rendererOptions.baselineOffset = 0; } rendererOptions.color = this._textColor(); diff --git a/src/renderers/price-axis-view-renderer.ts b/src/renderers/price-axis-view-renderer.ts index e5d5576814..07816c48f8 100644 --- a/src/renderers/price-axis-view-renderer.ts +++ b/src/renderers/price-axis-view-renderer.ts @@ -49,28 +49,38 @@ export class PriceAxisViewRenderer implements IPriceAxisViewRenderer { const halfHeigth = Math.ceil(totalHeight * 0.5); const totalWidth = horzBorder + textWidth + paddingInner + paddingOuter + tickSize; + const halfHeigthScaled = Math.round(halfHeigth * pixelRatio); + const totalHeightScaled = Math.round(totalHeight * pixelRatio); + const totalWidthScaled = Math.round(totalWidth * pixelRatio); + // tick overlaps scale border + const tickSizeScaled = Math.round(tickSize * pixelRatio); + const widthScaled = Math.ceil(width * pixelRatio); + const horzBorderScaled = Math.max(1, Math.floor(horzBorder * pixelRatio)); + const paddingOuterScaled = Math.ceil(paddingOuter * pixelRatio); + const paddingInnerScaled = Math.ceil(paddingInner * pixelRatio); + const paddingBottomScaled = Math.round(paddingBottom * pixelRatio); + const baselineOffsetScaled = Math.round(baselineOffset * pixelRatio); + let yMid = this._commonData.coordinate; if (this._commonData.fixedCoordinate) { yMid = this._commonData.fixedCoordinate; } - yMid = Math.round(yMid); + yMid = Math.round(yMid * pixelRatio); - const yTop = yMid - halfHeigth; - const yBottom = yTop + totalHeight; + const yTop = yMid - halfHeigthScaled + 1; + const yBottom = yTop + totalHeightScaled - 1; const alignRight = align === 'right'; - const xInside = alignRight ? width : 0; - const rightScaled = Math.ceil(width * pixelRatio); + const xInside = alignRight ? widthScaled : 0; + const rightScaled = widthScaled; let xOutside = xInside; let xTick: number; let xText: number; ctx.fillStyle = this._commonData.background; - ctx.lineWidth = 1; - ctx.lineCap = 'butt'; if (text) { if (alignRight) { @@ -79,67 +89,59 @@ export class PriceAxisViewRenderer implements IPriceAxisViewRenderer { // 6 5 // // 3 4 - xOutside = xInside - totalWidth; - xTick = xInside - tickSize; - xText = xOutside + paddingOuter; + xOutside = xInside - totalWidthScaled; + xTick = xInside - tickSizeScaled; + xText = xOutside + paddingOuterScaled; } else { // 1 2 // // 6 5 // // 4 3 - xOutside = xInside + totalWidth; - xTick = xInside + tickSize; - xText = xInside + horzBorder + tickSize + paddingInner; + xOutside = xInside + totalWidthScaled; + xTick = xInside + tickSizeScaled; + xText = xInside + (tickSizeScaled || horzBorderScaled) + paddingInnerScaled; } const tickHeight = Math.max(1, Math.floor(pixelRatio)); - const horzBorderScaled = Math.max(1, Math.floor(horzBorder * pixelRatio)); - const xInsideScaled = alignRight ? rightScaled : 0; - const yTopScaled = Math.round(yTop * pixelRatio); - const xOutsideScaled = Math.round(xOutside * pixelRatio); - const yMidScaled = Math.round(yMid * pixelRatio) - Math.floor(pixelRatio * 0.5); - - const yBottomScaled = yMidScaled + tickHeight + (yMidScaled - yTopScaled); - const xTickScaled = Math.round(xTick * pixelRatio); - ctx.save(); const radius = 2 * pixelRatio; ctx.beginPath(); if (alignRight) { - ctx.moveTo(xInsideScaled, yTopScaled); - ctx.lineTo(xOutsideScaled + radius, yTopScaled); - ctx.arcTo(xOutsideScaled, yTopScaled, xOutsideScaled, yTopScaled + radius, radius); - ctx.lineTo(xOutsideScaled, yBottomScaled - radius); - ctx.arcTo(xOutsideScaled, yBottomScaled, xOutsideScaled + radius, yBottomScaled, radius); - ctx.lineTo(xInsideScaled, yBottomScaled); + ctx.moveTo(xInside, yTop); + ctx.lineTo(xOutside + radius, yTop); + ctx.arcTo(xOutside, yTop, xOutside, yTop + radius, radius); + ctx.lineTo(xOutside, yBottom - radius); + ctx.arcTo(xOutside, yBottom, xOutside + radius, yBottom, radius); + ctx.lineTo(xInside, yBottom); } else { - ctx.moveTo(xInsideScaled, yTopScaled); - ctx.lineTo(xOutsideScaled - radius, yTopScaled); - ctx.arcTo(xOutsideScaled, yTopScaled, xOutsideScaled, yTopScaled + radius, radius); - ctx.lineTo(xOutsideScaled, yBottomScaled - radius); - ctx.arcTo(xOutsideScaled, yBottomScaled, xOutsideScaled - radius, yBottomScaled, radius); - ctx.lineTo(xInsideScaled, yBottomScaled); + ctx.moveTo(xInside, yTop); + ctx.lineTo(xOutside - radius, yTop); + ctx.arcTo(xOutside, yTop, xOutside, yTop + radius, radius); + ctx.lineTo(xOutside, yBottom - radius); + ctx.arcTo(xOutside, yBottom, xOutside - radius, yBottom, radius); + ctx.lineTo(xInside, yBottom); } ctx.fill(); // draw border ctx.fillStyle = this._data.borderColor; - ctx.fillRect(alignRight ? rightScaled - horzBorderScaled : 0, yTopScaled, horzBorderScaled, yBottomScaled - yTopScaled); + ctx.fillRect(alignRight ? rightScaled - horzBorderScaled : 0, yTop, horzBorderScaled, yBottom - yTop); if (this._data.tickVisible) { ctx.fillStyle = this._commonData.color; - ctx.fillRect(xInsideScaled, yMidScaled, xTickScaled - xInsideScaled, tickHeight); + ctx.fillRect(xInside, yMid, xTick - xInside, tickHeight); } ctx.textAlign = 'left'; ctx.fillStyle = this._commonData.color; + ctx.translate(xText, yBottom - paddingBottomScaled - baselineOffsetScaled); drawScaled(ctx, pixelRatio, () => { - ctx.fillText(text, xText, yBottom - paddingBottom - baselineOffset); + ctx.fillText(text, 0, 0); }); ctx.restore(); From 94dbd39f4d177559c491822c4e6850bd876d9e90 Mon Sep 17 00:00:00 2001 From: Eugene Korobko Date: Mon, 30 Nov 2020 15:07:54 +0300 Subject: [PATCH 006/188] 606: Fixed unit tests --- tests/unittests/make-font.spec.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/unittests/make-font.spec.ts b/tests/unittests/make-font.spec.ts index 782b4237fb..05b763ed0f 100644 --- a/tests/unittests/make-font.spec.ts +++ b/tests/unittests/make-font.spec.ts @@ -5,16 +5,16 @@ import { defaultFontFamily, makeFont } from '../../src/helpers/make-font'; describe('makeFont', () => { it('should correct generate font family without style', () => { - expect(makeFont(12, 'Roboto')).to.be.equal('12px Roboto'); - expect(makeFont(120, 'Roboto')).to.be.equal('120px Roboto'); + expect(makeFont(12, 'Roboto')).to.be.equal('16px Roboto'); + expect(makeFont(120, 'Roboto')).to.be.equal('160px Roboto'); }); it('should correct generate font family with style', () => { - expect(makeFont(12, 'Roboto', 'italic')).to.be.equal('italic 12px Roboto'); - expect(makeFont(120, 'Roboto', 'bold')).to.be.equal('bold 120px Roboto'); + expect(makeFont(12, 'Roboto', 'italic')).to.be.equal('italic 16px Roboto'); + expect(makeFont(120, 'Roboto', 'bold')).to.be.equal('bold 160px Roboto'); }); it('should correct generate font with default family', () => { - expect(makeFont(12)).to.be.equal(`12px ${defaultFontFamily}`); + expect(makeFont(12)).to.be.equal(`16px ${defaultFontFamily}`); }); }); From 029df678f80e70d73cb8983e7c545d03e70d4417 Mon Sep 17 00:00:00 2001 From: Eugene Korobko Date: Mon, 30 Nov 2020 17:57:30 +0300 Subject: [PATCH 007/188] 606: Calculate actual font size --- src/gui/labels-image-cache.ts | 4 ++-- src/helpers/make-font.ts | 5 ++--- src/renderers/price-axis-view-renderer.ts | 7 ++++--- tests/unittests/make-font.spec.ts | 10 +++++----- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/gui/labels-image-cache.ts b/src/gui/labels-image-cache.ts index 145b953506..2572622159 100644 --- a/src/gui/labels-image-cache.ts +++ b/src/gui/labels-image-cache.ts @@ -3,7 +3,7 @@ import { createPreconfiguredCanvas, getCanvasDevicePixelRatio, getContext2D, Siz import { ensureDefined } from '../helpers/assertions'; import { drawScaled } from '../helpers/canvas-helpers'; import { IDestroyable } from '../helpers/idestroyable'; -import { fontSizeToPixels, makeFont } from '../helpers/make-font'; +import { makeFont } from '../helpers/make-font'; import { ceiledEven } from '../helpers/mathex'; import { TextWidthCache } from '../model/text-width-cache'; @@ -71,7 +71,7 @@ export class LabelsImageCache implements IDestroyable { const textWidth = Math.ceil(this._textWidthCache.measureText(ctx, text)); // small reserve for antialiasing, measureText is not sharp const width = ceiledEven(Math.round(textWidth) + 4); - const height = ceiledEven(fontSizeToPixels(this._fontSize)); + const height = ceiledEven(this._fontSize); const canvas = createPreconfiguredCanvas(document, new Size(width, height)); // Allocate new diff --git a/src/helpers/make-font.ts b/src/helpers/make-font.ts index 7ebf2a6680..a0ef1380ad 100644 --- a/src/helpers/make-font.ts +++ b/src/helpers/make-font.ts @@ -5,7 +5,7 @@ export const defaultFontFamily = `'Trebuchet MS', Roboto, Ubuntu, sans-serif`; export function fontSizeToPixels(size: number): number { - return Math.round(size * 96 / 72); + return Math.round(size * 72 / 96); } /** @@ -13,7 +13,6 @@ export function fontSizeToPixels(size: number): number { * If no family provided, [defaultFontFamily] will be used. */ export function makeFont(size: number, family?: string, style?: string): string { - const sizeInPixels = fontSizeToPixels(size); if (style !== undefined) { style = `${style} `; } else { @@ -24,5 +23,5 @@ export function makeFont(size: number, family?: string, style?: string): string family = defaultFontFamily; } - return `${style}${sizeInPixels}px ${family}`; + return `${style}${size}px ${family}`; } diff --git a/src/renderers/price-axis-view-renderer.ts b/src/renderers/price-axis-view-renderer.ts index 07816c48f8..0c46594196 100644 --- a/src/renderers/price-axis-view-renderer.ts +++ b/src/renderers/price-axis-view-renderer.ts @@ -1,4 +1,5 @@ import { drawScaled } from '../helpers/canvas-helpers'; +import { fontSizeToPixels } from '../helpers/make-font'; import { TextWidthCache } from '../model/text-width-cache'; @@ -45,7 +46,7 @@ export class PriceAxisViewRenderer implements IPriceAxisViewRenderer { const text = this._data.text; const textWidth = Math.ceil(textWidthCache.measureText(ctx, text)); const baselineOffset = rendererOptions.baselineOffset; - const totalHeight = rendererOptions.fontSize + paddingTop + paddingBottom; + const totalHeight = fontSizeToPixels(rendererOptions.fontSize) + paddingTop + paddingBottom; const halfHeigth = Math.ceil(totalHeight * 0.5); const totalWidth = horzBorder + textWidth + paddingInner + paddingOuter + tickSize; @@ -68,8 +69,8 @@ export class PriceAxisViewRenderer implements IPriceAxisViewRenderer { yMid = Math.round(yMid * pixelRatio); - const yTop = yMid - halfHeigthScaled + 1; - const yBottom = yTop + totalHeightScaled - 1; + const yTop = yMid - halfHeigthScaled; + const yBottom = yTop + totalHeightScaled; const alignRight = align === 'right'; diff --git a/tests/unittests/make-font.spec.ts b/tests/unittests/make-font.spec.ts index 05b763ed0f..782b4237fb 100644 --- a/tests/unittests/make-font.spec.ts +++ b/tests/unittests/make-font.spec.ts @@ -5,16 +5,16 @@ import { defaultFontFamily, makeFont } from '../../src/helpers/make-font'; describe('makeFont', () => { it('should correct generate font family without style', () => { - expect(makeFont(12, 'Roboto')).to.be.equal('16px Roboto'); - expect(makeFont(120, 'Roboto')).to.be.equal('160px Roboto'); + expect(makeFont(12, 'Roboto')).to.be.equal('12px Roboto'); + expect(makeFont(120, 'Roboto')).to.be.equal('120px Roboto'); }); it('should correct generate font family with style', () => { - expect(makeFont(12, 'Roboto', 'italic')).to.be.equal('italic 16px Roboto'); - expect(makeFont(120, 'Roboto', 'bold')).to.be.equal('bold 160px Roboto'); + expect(makeFont(12, 'Roboto', 'italic')).to.be.equal('italic 12px Roboto'); + expect(makeFont(120, 'Roboto', 'bold')).to.be.equal('bold 120px Roboto'); }); it('should correct generate font with default family', () => { - expect(makeFont(12)).to.be.equal(`16px ${defaultFontFamily}`); + expect(makeFont(12)).to.be.equal(`12px ${defaultFontFamily}`); }); }); From 989155a43f121ab2869648a6a8119f462e0fdc5d Mon Sep 17 00:00:00 2001 From: Eugene Korobko Date: Tue, 1 Dec 2020 09:02:57 +0300 Subject: [PATCH 008/188] 606: Improved look and feel --- src/renderers/price-axis-view-renderer.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/renderers/price-axis-view-renderer.ts b/src/renderers/price-axis-view-renderer.ts index 0c46594196..6ec7395241 100644 --- a/src/renderers/price-axis-view-renderer.ts +++ b/src/renderers/price-axis-view-renderer.ts @@ -44,11 +44,13 @@ export class PriceAxisViewRenderer implements IPriceAxisViewRenderer { const paddingInner = rendererOptions.paddingInner; const paddingOuter = rendererOptions.paddingOuter; const text = this._data.text; + const actualTextHeight = fontSizeToPixels(rendererOptions.fontSize); const textWidth = Math.ceil(textWidthCache.measureText(ctx, text)); + const correctedTextWidth = Math.max(textWidth, actualTextHeight); const baselineOffset = rendererOptions.baselineOffset; - const totalHeight = fontSizeToPixels(rendererOptions.fontSize) + paddingTop + paddingBottom; + const totalHeight = actualTextHeight + paddingTop + paddingBottom; const halfHeigth = Math.ceil(totalHeight * 0.5); - const totalWidth = horzBorder + textWidth + paddingInner + paddingOuter + tickSize; + const totalWidth = horzBorder + correctedTextWidth + paddingInner + paddingOuter + tickSize; const halfHeigthScaled = Math.round(halfHeigth * pixelRatio); const totalHeightScaled = Math.round(totalHeight * pixelRatio); @@ -104,11 +106,14 @@ export class PriceAxisViewRenderer implements IPriceAxisViewRenderer { xText = xInside + (tickSizeScaled || horzBorderScaled) + paddingInnerScaled; } + const textCorrectionOffset = Math.round(pixelRatio * (correctedTextWidth - textWidth) / 2); + xText += textCorrectionOffset; + const tickHeight = Math.max(1, Math.floor(pixelRatio)); ctx.save(); - const radius = 2 * pixelRatio; + const radius = 5 * pixelRatio; ctx.beginPath(); if (alignRight) { From 449959550cc1baf44c362a160d477c16a60496b1 Mon Sep 17 00:00:00 2001 From: Eugene Korobko Date: Mon, 14 Dec 2020 13:07:21 +0300 Subject: [PATCH 009/188] 606: Update time axis margins and restore radius --- src/gui/time-axis-widget.ts | 13 +++++++------ src/renderers/price-axis-view-renderer.ts | 2 +- src/renderers/time-axis-view-renderer.ts | 8 +++++--- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/gui/time-axis-widget.ts b/src/gui/time-axis-widget.ts index f52fc38ce8..86599623be 100644 --- a/src/gui/time-axis-widget.ts +++ b/src/gui/time-axis-widget.ts @@ -2,7 +2,7 @@ import { Binding as CanvasCoordinateSpaceBinding } from 'fancy-canvas/coordinate import { clearRect, drawScaled } from '../helpers/canvas-helpers'; import { IDestroyable } from '../helpers/idestroyable'; -import { makeFont } from '../helpers/make-font'; +import { fontSizeToPixels, makeFont } from '../helpers/make-font'; import { Coordinate } from '../model/coordinate'; import { IDataSource } from '../model/idata-source'; @@ -220,7 +220,7 @@ export class TimeAxisWidget implements MouseEventHandlers, IDestroyable { // rendererOptions.offsetSize + rendererOptions.borderSize + rendererOptions.tickLength + - rendererOptions.fontSize + + fontSizeToPixels(rendererOptions.fontSize) + rendererOptions.paddingTop + rendererOptions.paddingBottom ); @@ -307,7 +307,8 @@ export class TimeAxisWidget implements MouseEventHandlers, IDestroyable { const yText = ( rendererOptions.borderSize + rendererOptions.paddingTop + - rendererOptions.fontSize - + rendererOptions.tickLength + + fontSizeToPixels(rendererOptions.fontSize) - rendererOptions.baselineOffset ); @@ -404,10 +405,10 @@ export class TimeAxisWidget implements MouseEventHandlers, IDestroyable { const fontSize = this._fontSize(); rendererOptions.fontSize = fontSize; rendererOptions.font = newFont; - rendererOptions.paddingTop = Math.ceil(fontSize / 2.5) + 2; - rendererOptions.paddingBottom = rendererOptions.paddingTop - 1; + rendererOptions.paddingTop = Math.ceil(fontSize / 3); + rendererOptions.paddingBottom = Math.ceil(fontSize / 3) + 2; rendererOptions.paddingHorizontal = Math.ceil(fontSize / 2); - rendererOptions.baselineOffset = Math.round(this._fontSize() / 5); + rendererOptions.baselineOffset = 0; rendererOptions.widthCache.reset(); } diff --git a/src/renderers/price-axis-view-renderer.ts b/src/renderers/price-axis-view-renderer.ts index 6ec7395241..eb4018b6dd 100644 --- a/src/renderers/price-axis-view-renderer.ts +++ b/src/renderers/price-axis-view-renderer.ts @@ -113,7 +113,7 @@ export class PriceAxisViewRenderer implements IPriceAxisViewRenderer { ctx.save(); - const radius = 5 * pixelRatio; + const radius = 2 * pixelRatio; ctx.beginPath(); if (alignRight) { diff --git a/src/renderers/time-axis-view-renderer.ts b/src/renderers/time-axis-view-renderer.ts index 8fd72ded65..96dad9a3b1 100644 --- a/src/renderers/time-axis-view-renderer.ts +++ b/src/renderers/time-axis-view-renderer.ts @@ -1,5 +1,6 @@ import { ensureNotNull } from '../helpers/assertions'; import { drawScaled } from '../helpers/canvas-helpers'; +import { fontSizeToPixels } from '../helpers/make-font'; import { ITimeAxisViewRenderer, TimeAxisViewRendererOptions } from './itime-axis-view-renderer'; @@ -57,11 +58,12 @@ export class TimeAxisViewRenderer implements ITimeAxisViewRenderer { const x2 = x1 + labelWidth; const y1 = 0; - const y2 = ( + const y2 = Math.ceil( y1 + rendererOptions.borderSize + rendererOptions.paddingTop + - rendererOptions.fontSize + + rendererOptions.tickLength + + fontSizeToPixels(rendererOptions.fontSize) + rendererOptions.paddingBottom ); @@ -71,7 +73,7 @@ export class TimeAxisViewRenderer implements ITimeAxisViewRenderer { const y1scaled = Math.round(y1 * pixelRatio); const x2scaled = Math.round(x2 * pixelRatio); const y2scaled = Math.round(y2 * pixelRatio); - ctx.fillRect(x1scaled, y1scaled, x2scaled - x1scaled, y2scaled - y1scaled); + ctx.fillRect(x1scaled, y1scaled, x2scaled - x1scaled, y2scaled - y1scaled + Math.ceil(pixelRatio)); const tickX = Math.round(this._data.coordinate * pixelRatio); const tickTop = y1scaled; From 24b3f3bdb3935048bea78cc848312199458e16dc Mon Sep 17 00:00:00 2001 From: Eugene Korobko Date: Tue, 22 Dec 2020 15:40:26 +0300 Subject: [PATCH 010/188] 606: Change time axis rendering --- src/gui/time-axis-widget.ts | 6 ++++-- src/renderers/itime-axis-view-renderer.ts | 1 + src/renderers/time-axis-view-renderer.ts | 17 ++++++++++++++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/gui/time-axis-widget.ts b/src/gui/time-axis-widget.ts index 86599623be..265a2b5ea5 100644 --- a/src/gui/time-axis-widget.ts +++ b/src/gui/time-axis-widget.ts @@ -395,6 +395,7 @@ export class TimeAxisWidget implements MouseEventHandlers, IDestroyable { fontSize: NaN, font: '', widthCache: new TextWidthCache(), + labelBottomOffset: 0, }; } @@ -405,10 +406,11 @@ export class TimeAxisWidget implements MouseEventHandlers, IDestroyable { const fontSize = this._fontSize(); rendererOptions.fontSize = fontSize; rendererOptions.font = newFont; - rendererOptions.paddingTop = Math.ceil(fontSize / 3); - rendererOptions.paddingBottom = Math.ceil(fontSize / 3) + 2; + rendererOptions.paddingTop = Math.ceil(fontSize / 3) - 1; + rendererOptions.paddingBottom = Math.ceil(fontSize / 3) + 5; rendererOptions.paddingHorizontal = Math.ceil(fontSize / 2); rendererOptions.baselineOffset = 0; + rendererOptions.labelBottomOffset = Math.ceil(fontSize / 3) - 1; rendererOptions.widthCache.reset(); } diff --git a/src/renderers/itime-axis-view-renderer.ts b/src/renderers/itime-axis-view-renderer.ts index 6e66ba52cf..ecc5a89698 100644 --- a/src/renderers/itime-axis-view-renderer.ts +++ b/src/renderers/itime-axis-view-renderer.ts @@ -10,6 +10,7 @@ export interface TimeAxisViewRendererOptions { tickLength: number; paddingHorizontal: number; widthCache: TextWidthCache; + labelBottomOffset: number; } export interface ITimeAxisViewRenderer { diff --git a/src/renderers/time-axis-view-renderer.ts b/src/renderers/time-axis-view-renderer.ts index 96dad9a3b1..78d7d97e8a 100644 --- a/src/renderers/time-axis-view-renderer.ts +++ b/src/renderers/time-axis-view-renderer.ts @@ -15,6 +15,8 @@ export interface TimeAxisViewRendererData { const optimizationReplacementRe = /[1-9]/g; +const radius = 2; + export class TimeAxisViewRenderer implements ITimeAxisViewRenderer { private _data: TimeAxisViewRendererData | null; @@ -64,7 +66,8 @@ export class TimeAxisViewRenderer implements ITimeAxisViewRenderer { rendererOptions.paddingTop + rendererOptions.tickLength + fontSizeToPixels(rendererOptions.fontSize) + - rendererOptions.paddingBottom + rendererOptions.paddingBottom - + rendererOptions.labelBottomOffset ); ctx.fillStyle = this._data.background; @@ -73,7 +76,15 @@ export class TimeAxisViewRenderer implements ITimeAxisViewRenderer { const y1scaled = Math.round(y1 * pixelRatio); const x2scaled = Math.round(x2 * pixelRatio); const y2scaled = Math.round(y2 * pixelRatio); - ctx.fillRect(x1scaled, y1scaled, x2scaled - x1scaled, y2scaled - y1scaled + Math.ceil(pixelRatio)); + const radiusScaled = Math.round(radius * pixelRatio); + ctx.beginPath(); + ctx.moveTo(x1scaled, y1scaled); + ctx.lineTo(x1scaled, y2scaled - radiusScaled); + ctx.arcTo(x1scaled, y2scaled, x1scaled + radiusScaled, y2scaled, radiusScaled); + ctx.lineTo(x2scaled - radiusScaled, y2scaled); + ctx.arcTo(x2scaled, y2scaled, x2scaled, y2scaled - radiusScaled, radiusScaled); + ctx.lineTo(x2scaled, y1scaled); + ctx.fill(); const tickX = Math.round(this._data.coordinate * pixelRatio); const tickTop = y1scaled; @@ -84,7 +95,7 @@ export class TimeAxisViewRenderer implements ITimeAxisViewRenderer { const tickOffset = Math.floor(pixelRatio * 0.5); ctx.fillRect(tickX - tickOffset, tickTop, tickWidth, tickBottom - tickTop); - const yText = y2 - rendererOptions.baselineOffset - rendererOptions.paddingBottom; + const yText = y2 - rendererOptions.baselineOffset - rendererOptions.paddingBottom + rendererOptions.labelBottomOffset; ctx.textAlign = 'left'; ctx.fillStyle = this._data.color; From 63eb78f8d1bcf150569006cdd8a1da8c6da410db Mon Sep 17 00:00:00 2001 From: Eugene Korobko Date: Fri, 26 Mar 2021 13:14:38 +0300 Subject: [PATCH 011/188] 606: Migrated code from TV platform --- src/helpers/canvas-helpers.ts | 117 +++++++++++++ src/helpers/color.ts | 37 ++++ src/model/text-width-cache.ts | 96 +++++------ src/renderers/iprice-axis-view-renderer.ts | 5 + .../price-axis-renderer-options-provider.ts | 6 + src/renderers/price-axis-view-renderer.ts | 163 ++++++++++-------- src/views/price-axis/price-axis-view.ts | 6 + 7 files changed, 307 insertions(+), 123 deletions(-) diff --git a/src/helpers/canvas-helpers.ts b/src/helpers/canvas-helpers.ts index 957bcf8a0b..4eed5bfb25 100644 --- a/src/helpers/canvas-helpers.ts +++ b/src/helpers/canvas-helpers.ts @@ -49,3 +49,120 @@ export function clearRect(ctx: CanvasRenderingContext2D, x: number, y: number, w ctx.fillRect(x, y, w, h); ctx.restore(); } + +export type TopBottomRadii = [number, number]; +export type LeftTopRightTopRightBottomLeftBottomRadii = [number, number, number, number]; +export type DrawRoundRectRadii = number | TopBottomRadii | LeftTopRightTopRightBottomLeftBottomRadii; + +function changeBorderRadius(borderRadius: DrawRoundRectRadii, offset: number): typeof borderRadius { + if (Array.isArray(borderRadius)) { + return borderRadius.map((x: number) => x === 0 ? x : x + offset) as typeof borderRadius; + } + return borderRadius + offset; +} + +export function drawRoundRect( + // eslint:disable-next-line:max-params + ctx: CanvasRenderingContext2D, + x: number, + y: number, + w: number, + h: number, + radii: DrawRoundRectRadii +): void { + let radiusLeftTop: number; + let radiusRightTop: number; + let radiusRightBottom: number; + let radiusLeftBottom: number; + + if (!Array.isArray(radii)) { + const oneRadius = Math.max(0, radii); + radiusLeftTop = oneRadius; + radiusRightTop = oneRadius; + radiusRightBottom = oneRadius; + radiusLeftBottom = oneRadius; + } else if (radii.length === 2) { + const cornerRadius1 = Math.max(0, radii[0]); + const cornerRadius2 = Math.max(0, radii[1]); + radiusLeftTop = cornerRadius1; + radiusRightTop = cornerRadius1; + radiusRightBottom = cornerRadius2; + radiusLeftBottom = cornerRadius2; + } else if (radii.length === 4) { + radiusLeftTop = Math.max(0, radii[0]); + radiusRightTop = Math.max(0, radii[1]); + radiusRightBottom = Math.max(0, radii[2]); + radiusLeftBottom = Math.max(0, radii[3]); + } else { + throw new Error(`Wrong border radius - it should be like css border radius`); + } + + ctx.beginPath(); + ctx.moveTo(x + radiusLeftTop, y); + ctx.lineTo(x + w - radiusRightTop, y); + if (radiusRightTop !== 0) { + ctx.arcTo(x + w, y, x + w, y + radiusRightTop, radiusRightTop); + } + + ctx.lineTo(x + w, y + h - radiusRightBottom); + if (radiusRightBottom !== 0) { + ctx.arcTo(x + w, y + h, x + w - radiusRightBottom, y + h, radiusRightBottom); + } + + ctx.lineTo(x + radiusLeftBottom, y + h); + if (radiusLeftBottom !== 0) { + ctx.arcTo(x, y + h, x, y + h - radiusLeftBottom, radiusLeftBottom); + } + + ctx.lineTo(x, y + radiusLeftTop); + if (radiusLeftTop !== 0) { + ctx.arcTo(x, y, x + radiusLeftTop, y, radiusLeftTop); + } +} + +// eslint-disable-next-line max-params +export function drawRoundRectWithInnerBorder( + ctx: CanvasRenderingContext2D, + left: number, + top: number, + width: number, + height: number, + backgroundColor: string, + borderWidth: number = 0, + borderRadius: DrawRoundRectRadii = 0, + borderColor: string = '' +): void { + ctx.save(); + + if (!borderWidth || !borderColor || borderColor === backgroundColor) { + drawRoundRect(ctx, left, top, width, height, borderRadius); + ctx.fillStyle = backgroundColor; + ctx.fill(); + ctx.restore(); + return; + } + + const halfBorderWidth = borderWidth / 2; + + // Draw body + if (backgroundColor !== 'transparent') { + const innerRadii = changeBorderRadius(borderRadius, - borderWidth); + drawRoundRect(ctx, left + borderWidth, top + borderWidth, width - borderWidth * 2, height - borderWidth * 2, innerRadii); + + ctx.fillStyle = backgroundColor; + ctx.fill(); + } + + // Draw border + if (borderColor !== 'transparent') { + const outerRadii = changeBorderRadius(borderRadius, - halfBorderWidth); + drawRoundRect(ctx, left + halfBorderWidth, top + halfBorderWidth, width - borderWidth, height - borderWidth, outerRadii); + + ctx.lineWidth = borderWidth; + ctx.strokeStyle = borderColor; + ctx.closePath(); + ctx.stroke(); + } + + ctx.restore(); +} diff --git a/src/helpers/color.ts b/src/helpers/color.ts index 2277eb5422..bb2e6ec4ea 100644 --- a/src/helpers/color.ts +++ b/src/helpers/color.ts @@ -18,8 +18,16 @@ type GreenComponent = Nominal; */ type BlueComponent = Nominal; +/** + * Alpha component of the RGBA color value + * The valid values are float in range [0, 1] + */ +type AlphaComponent = Nominal; + type Rgb = [RedComponent, GreenComponent, BlueComponent]; +type Rgba = [RedComponent, GreenComponent, BlueComponent, AlphaComponent]; + /** @public see https://developer.mozilla.org/en-US/docs/Web/CSS/color_value */ const namedColorRgbHexStrings: Record = { // The order of properties in this Record is not important for the internal logic. @@ -263,6 +271,23 @@ function colorStringToRgb(colorString: string): Rgb { throw new Error(`Cannot parse color: ${colorString}`); } +function rgba(rgb: Rgb, alpha: AlphaComponent): Rgba { + return [ + rgb[0], + rgb[1], + rgb[2], + alpha, + ]; +} + +function rgbaToString(rgbaObj: Rgba): string { + const red = rgbaObj[0]; + const green = rgbaObj[1]; + const blue = rgbaObj[2]; + const alpha = rgbaObj[3]; + return `rgba(${red}, ${green}, ${blue}, ${alpha})`; +} + function rgbToGrayscale(rgbValue: Rgb): number { // Originally, the NTSC RGB to YUV formula // perfected by @eugene-korobko's black magic @@ -290,3 +315,15 @@ export function generateContrastColors(backgroundColor: string): ContrastColors foreground: rgbToGrayscale(rgb) > 160 ? 'black' : 'white', }; } + +export function isHexColor(color: string): boolean { + return color.indexOf('#') === 0; +} + +export function resetTransparency(color: string): string { + if (isHexColor(color)) { + return color; + } + + return rgbaToString(rgba(colorStringToRgb(color), 1 as AlphaComponent)); +} diff --git a/src/model/text-width-cache.ts b/src/model/text-width-cache.ts index 143a2ce8d2..d4d1a7efe1 100644 --- a/src/model/text-width-cache.ts +++ b/src/model/text-width-cache.ts @@ -1,71 +1,65 @@ -export type CanvasCtxLike = Pick; - const defaultReplacementRe = /[2-9]/g; export class TextWidthCache { - private _cache: Map = new Map(); - /** A "cyclic buffer" of cache keys */ - private _keys: (string | undefined)[]; - /** Current index in the "cyclic buffer" */ - private _keysIndex: number = 0; + private readonly _maxSize: number; + private _actualSize: number = 0; + private _usageTick: number = 1; + private _oldestTick: number = 1; + private _tick2Labels: Record = {}; + private _cache: Record = {}; public constructor(size: number = 50) { - // A trick to keep array PACKED_ELEMENTS - this._keys = Array.from(new Array(size)); + this._maxSize = size; } public reset(): void { - this._cache.clear(); - this._keys.fill(undefined); - // We don't care where exactly the _keysIndex points, - // so there's no point in resetting it + this._actualSize = 0; + this._cache = {}; + this._usageTick = 1; + this._oldestTick = 1; + this._tick2Labels = {}; } - public measureText(ctx: CanvasCtxLike, text: string, optimizationReplacementRe?: RegExp): number { - const re = optimizationReplacementRe || defaultReplacementRe; - const cacheString = String(text).replace(re, '0'); - - let width = this._cache.get(cacheString); - - if (width === undefined) { - width = ctx.measureText(cacheString).width; + public measureText(ctx: CanvasRenderingContext2D, text: string, optimizationReplacementRe?: RegExp): number { + return this._getMetrics(ctx, text, optimizationReplacementRe).width; + } - if (width === 0 && text.length !== 0) { - // measureText can return 0 in FF depending on a canvas size, don't cache it - return 0; - } + public yMidCorrection(ctx: CanvasRenderingContext2D, text: string, optimizationReplacementRe?: RegExp): number { + const metrics = this._getMetrics(ctx, text, optimizationReplacementRe); + // if actualBoundingBoxAscent/actualBoundingBoxDescent are not supported we use 0 as a fallback + return ((metrics.actualBoundingBoxAscent || 0) - (metrics.actualBoundingBoxDescent || 0)) / 2; + } - // A cyclic buffer is used to keep track of the cache keys and to delete - // the oldest one before a new one is inserted. - // ├──────┬──────┬──────┬──────┤ - // │ foo │ bar │ │ │ - // ├──────┴──────┴──────┴──────┤ - // ↑ index + private _getMetrics(ctx: CanvasRenderingContext2D, text: string, optimizationReplacementRe?: RegExp): TextMetrics { + const re = optimizationReplacementRe || defaultReplacementRe; + const cacheString = String(text).replace(re, '0'); - // Eventually, the index reach the end of an array and roll-over to 0. - // ├──────┬──────┬──────┬──────┤ - // │ foo │ bar │ baz │ quux │ - // ├──────┴──────┴──────┴──────┤ - // ↑ index = 0 + if (this._cache[cacheString]) { + return this._cache[cacheString].metrics; + } - // After that the oldest value will be overwritten. - // ├──────┬──────┬──────┬──────┤ - // │ WOOT │ bar │ baz │ quux │ - // ├──────┴──────┴──────┴──────┤ - // ↑ index = 1 + if (this._actualSize === this._maxSize) { + const oldestValue = this._tick2Labels[this._oldestTick]; + delete this._tick2Labels[this._oldestTick]; + delete this._cache[oldestValue]; + this._oldestTick++; + this._actualSize--; + } - const oldestKey = this._keys[this._keysIndex]; - if (oldestKey !== undefined) { - this._cache.delete(oldestKey); - } - // Set a newest key in place of the just deleted one - this._keys[this._keysIndex] = cacheString; - // Advance the index so it always points the oldest value - this._keysIndex = (this._keysIndex + 1) % this._keys.length; + ctx.save(); + ctx.textBaseline = 'middle'; + const metrics = ctx.measureText(cacheString); + ctx.restore(); - this._cache.set(cacheString, width); + if (metrics.width === 0 && !!text.length) { + // measureText can return 0 in FF depending on a canvas size, don't cache it + return metrics; } - return width; + this._cache[cacheString] = { metrics: metrics, tick: this._usageTick }; + this._tick2Labels[this._usageTick] = cacheString; + this._actualSize++; + this._usageTick++; + return metrics; } } diff --git a/src/renderers/iprice-axis-view-renderer.ts b/src/renderers/iprice-axis-view-renderer.ts index 60e1a31e04..6f57dea8f2 100644 --- a/src/renderers/iprice-axis-view-renderer.ts +++ b/src/renderers/iprice-axis-view-renderer.ts @@ -7,6 +7,8 @@ export interface PriceAxisViewRendererCommonData { color: string; coordinate: number; fixedCoordinate?: number; + additionalPaddingTop: number; + additionalPaddingBottom: number; } export interface PriceAxisViewRendererData { @@ -15,7 +17,9 @@ export interface PriceAxisViewRendererData { tickVisible: boolean; moveTextToInvisibleTick: boolean; borderColor: string; + color: string; lineWidth?: LineWidth; + borderVisible: boolean; } export interface PriceAxisViewRendererOptions { @@ -24,6 +28,7 @@ export interface PriceAxisViewRendererOptions { font: string; fontFamily: string; color: string; + paneBackgroundColor: string; fontSize: number; paddingBottom: number; paddingInner: number; diff --git a/src/renderers/price-axis-renderer-options-provider.ts b/src/renderers/price-axis-renderer-options-provider.ts index 19b582d56c..b813af672f 100644 --- a/src/renderers/price-axis-renderer-options-provider.ts +++ b/src/renderers/price-axis-renderer-options-provider.ts @@ -19,6 +19,7 @@ export class PriceAxisRendererOptionsProvider { font: '', fontFamily: '', color: '', + paneBackgroundColor: '', paddingBottom: 0, paddingInner: 0, paddingOuter: 0, @@ -48,6 +49,7 @@ export class PriceAxisRendererOptionsProvider { } rendererOptions.color = this._textColor(); + rendererOptions.paneBackgroundColor = this._paneBackgroundColor(); return this._rendererOptions; } @@ -56,6 +58,10 @@ export class PriceAxisRendererOptionsProvider { return this._chartModel.options().layout.textColor; } + private _paneBackgroundColor(): string { + return this._chartModel.options().layout.backgroundColor; + } + private _fontSize(): number { return this._chartModel.options().layout.fontSize; } diff --git a/src/renderers/price-axis-view-renderer.ts b/src/renderers/price-axis-view-renderer.ts index eb4018b6dd..da74fcdec7 100644 --- a/src/renderers/price-axis-view-renderer.ts +++ b/src/renderers/price-axis-view-renderer.ts @@ -1,5 +1,5 @@ -import { drawScaled } from '../helpers/canvas-helpers'; -import { fontSizeToPixels } from '../helpers/make-font'; +import { drawRoundRectWithInnerBorder, drawScaled } from '../helpers/canvas-helpers'; +import { resetTransparency } from '../helpers/color'; import { TextWidthCache } from '../model/text-width-cache'; @@ -37,41 +37,43 @@ export class PriceAxisViewRenderer implements IPriceAxisViewRenderer { ctx.font = rendererOptions.font; - const tickSize = (this._data.tickVisible || !this._data.moveTextToInvisibleTick) ? rendererOptions.tickLength : 0; + const tickSize = (this._data.tickVisible) ? rendererOptions.tickLength : 0; const horzBorder = rendererOptions.borderSize; - const paddingTop = rendererOptions.paddingTop; - const paddingBottom = rendererOptions.paddingBottom; + const paddingTop = rendererOptions.paddingTop + this._commonData.additionalPaddingTop; + const paddingBottom = rendererOptions.paddingBottom + this._commonData.additionalPaddingBottom; const paddingInner = rendererOptions.paddingInner; const paddingOuter = rendererOptions.paddingOuter; const text = this._data.text; - const actualTextHeight = fontSizeToPixels(rendererOptions.fontSize); + const actualTextHeight = rendererOptions.fontSize; + const textMidCorrection = textWidthCache.yMidCorrection(ctx, text) * pixelRatio; + const textWidth = Math.ceil(textWidthCache.measureText(ctx, text)); const correctedTextWidth = Math.max(textWidth, actualTextHeight); - const baselineOffset = rendererOptions.baselineOffset; const totalHeight = actualTextHeight + paddingTop + paddingBottom; - const halfHeigth = Math.ceil(totalHeight * 0.5); - const totalWidth = horzBorder + correctedTextWidth + paddingInner + paddingOuter + tickSize; - const halfHeigthScaled = Math.round(halfHeigth * pixelRatio); - const totalHeightScaled = Math.round(totalHeight * pixelRatio); + const totalWidth = horzBorder + paddingInner + paddingOuter + correctedTextWidth + tickSize; + + const tickHeight = Math.max(1, Math.floor(pixelRatio)); + + let totalHeightScaled = Math.round(totalHeight * pixelRatio); + if (totalHeightScaled % 2 !== tickHeight % 2) { + totalHeightScaled += 1; + } + + const horzBorderScaled = Math.max(1, Math.floor(horzBorder * pixelRatio)); const totalWidthScaled = Math.round(totalWidth * pixelRatio); // tick overlaps scale border const tickSizeScaled = Math.round(tickSize * pixelRatio); const widthScaled = Math.ceil(width * pixelRatio); - const horzBorderScaled = Math.max(1, Math.floor(horzBorder * pixelRatio)); - const paddingOuterScaled = Math.ceil(paddingOuter * pixelRatio); const paddingInnerScaled = Math.ceil(paddingInner * pixelRatio); - const paddingBottomScaled = Math.round(paddingBottom * pixelRatio); - const baselineOffsetScaled = Math.round(baselineOffset * pixelRatio); let yMid = this._commonData.coordinate; if (this._commonData.fixedCoordinate) { yMid = this._commonData.fixedCoordinate; } - yMid = Math.round(yMid * pixelRatio); - - const yTop = yMid - halfHeigthScaled; + yMid = Math.round(yMid * pixelRatio) - Math.floor(pixelRatio * 0.5); + const yTop = Math.floor(yMid + tickHeight / 2 - totalHeightScaled / 2); const yBottom = yTop + totalHeightScaled; const alignRight = align === 'right'; @@ -83,73 +85,90 @@ export class PriceAxisViewRenderer implements IPriceAxisViewRenderer { let xTick: number; let xText: number; - ctx.fillStyle = this._commonData.background; - - if (text) { - if (alignRight) { - // 2 1 - // - // 6 5 - // - // 3 4 - xOutside = xInside - totalWidthScaled; - xTick = xInside - tickSizeScaled; - xText = xOutside + paddingOuterScaled; - } else { - // 1 2 - // - // 6 5 - // - // 4 3 - xOutside = xInside + totalWidthScaled; - xTick = xInside + tickSizeScaled; - xText = xInside + (tickSizeScaled || horzBorderScaled) + paddingInnerScaled; - } - - const textCorrectionOffset = Math.round(pixelRatio * (correctedTextWidth - textWidth) / 2); - xText += textCorrectionOffset; + ctx.fillStyle = resetTransparency(this._commonData.background); + const radius = 2 * pixelRatio; + + ctx.textAlign = alignRight ? 'right' : 'left'; + ctx.textBaseline = 'middle'; + + if (alignRight) { + // 2 1 + // + // 6 5 + // + // 3 4 + xOutside = xInside - totalWidthScaled; + xTick = xInside - tickSizeScaled; + xText = xInside - tickSizeScaled - paddingInnerScaled - 1; + } else { + // 1 2 + // + // 6 5 + // + // 4 3 + xOutside = xInside + totalWidthScaled; + xTick = xInside + tickSizeScaled; + xText = xInside + tickSizeScaled + paddingInnerScaled; + } - const tickHeight = Math.max(1, Math.floor(pixelRatio)); + const textCorrectionOffset = Math.round(pixelRatio * (correctedTextWidth - textWidth) / 2); + xText += textCorrectionOffset; - ctx.save(); + const textColor = this._data.color || this._commonData.color; + const backgroundColor = resetTransparency(this._commonData.background); - const radius = 2 * pixelRatio; - - ctx.beginPath(); - if (alignRight) { - ctx.moveTo(xInside, yTop); - ctx.lineTo(xOutside + radius, yTop); - ctx.arcTo(xOutside, yTop, xOutside, yTop + radius, radius); - ctx.lineTo(xOutside, yBottom - radius); - ctx.arcTo(xOutside, yBottom, xOutside + radius, yBottom, radius); - ctx.lineTo(xInside, yBottom); - } else { - ctx.moveTo(xInside, yTop); - ctx.lineTo(xOutside - radius, yTop); - ctx.arcTo(xOutside, yTop, xOutside, yTop + radius, radius); - ctx.lineTo(xOutside, yBottom - radius); - ctx.arcTo(xOutside, yBottom, xOutside - radius, yBottom, radius); - ctx.lineTo(xInside, yBottom); - } - ctx.fill(); + if (text) { + const drawLabelBody = (labelBackgroundColor: string, labelBorderColor?: string): void => { + if (alignRight) { + drawRoundRectWithInnerBorder( + ctx, + xOutside, + yTop, + totalWidthScaled, + totalHeightScaled, + labelBackgroundColor, + horzBorderScaled, + [radius, 0, 0, radius], + labelBorderColor + ); + } else { + drawRoundRectWithInnerBorder( + ctx, + xInside, + yTop, + totalWidthScaled, + totalHeightScaled, + labelBackgroundColor, + horzBorderScaled, + [0, radius, radius, 0], + labelBorderColor + ); + } + }; // draw border - ctx.fillStyle = this._data.borderColor; - ctx.fillRect(alignRight ? rightScaled - horzBorderScaled : 0, yTop, horzBorderScaled, yBottom - yTop); - + // draw label background + drawLabelBody(backgroundColor, 'transparent'); + // draw tick if (this._data.tickVisible) { - ctx.fillStyle = this._commonData.color; + ctx.fillStyle = textColor; ctx.fillRect(xInside, yMid, xTick - xInside, tickHeight); } + // draw label border above the tick + drawLabelBody('transparent', backgroundColor); - ctx.textAlign = 'left'; - ctx.fillStyle = this._commonData.color; + // draw separator + if (this._data.borderVisible) { + ctx.fillStyle = rendererOptions.paneBackgroundColor; + ctx.fillRect(alignRight ? rightScaled - horzBorderScaled : 0, yTop, horzBorderScaled, yBottom - yTop); + } - ctx.translate(xText, yBottom - paddingBottomScaled - baselineOffsetScaled); + ctx.save(); + ctx.translate(xText, (yTop + yBottom) / 2 + textMidCorrection); drawScaled(ctx, pixelRatio, () => { + ctx.fillStyle = textColor; ctx.fillText(text, 0, 0); }); - ctx.restore(); } } diff --git a/src/views/price-axis/price-axis-view.ts b/src/views/price-axis/price-axis-view.ts index 804ec5482c..9e09e4e78f 100644 --- a/src/views/price-axis/price-axis-view.ts +++ b/src/views/price-axis/price-axis-view.ts @@ -15,6 +15,8 @@ export abstract class PriceAxisView implements IPriceAxisView { coordinate: 0, color: '#FFF', background: '#000', + additionalPaddingBottom: 0, + additionalPaddingTop: 0, }; private readonly _axisRendererData: PriceAxisViewRendererData = { @@ -23,6 +25,8 @@ export abstract class PriceAxisView implements IPriceAxisView { tickVisible: true, moveTextToInvisibleTick: false, borderColor: '', + color: '#FFF', + borderVisible: false, }; private readonly _paneRendererData: PriceAxisViewRendererData = { @@ -31,6 +35,8 @@ export abstract class PriceAxisView implements IPriceAxisView { tickVisible: false, moveTextToInvisibleTick: true, borderColor: '', + color: '#FFF', + borderVisible: true, }; private readonly _axisRenderer: IPriceAxisViewRenderer; From be28b3964167f4b13ab06c5a629fd7de067ca30a Mon Sep 17 00:00:00 2001 From: Eugene Korobko Date: Tue, 30 Mar 2021 10:16:25 +0300 Subject: [PATCH 012/188] 606: fixed text alignment --- src/renderers/price-axis-view-renderer.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/renderers/price-axis-view-renderer.ts b/src/renderers/price-axis-view-renderer.ts index da74fcdec7..6c75084079 100644 --- a/src/renderers/price-axis-view-renderer.ts +++ b/src/renderers/price-axis-view-renderer.ts @@ -48,10 +48,9 @@ export class PriceAxisViewRenderer implements IPriceAxisViewRenderer { const textMidCorrection = textWidthCache.yMidCorrection(ctx, text) * pixelRatio; const textWidth = Math.ceil(textWidthCache.measureText(ctx, text)); - const correctedTextWidth = Math.max(textWidth, actualTextHeight); const totalHeight = actualTextHeight + paddingTop + paddingBottom; - const totalWidth = horzBorder + paddingInner + paddingOuter + correctedTextWidth + tickSize; + const totalWidth = horzBorder + paddingInner + paddingOuter + textWidth + tickSize; const tickHeight = Math.max(1, Math.floor(pixelRatio)); @@ -78,7 +77,7 @@ export class PriceAxisViewRenderer implements IPriceAxisViewRenderer { const alignRight = align === 'right'; - const xInside = alignRight ? widthScaled : 0; + const xInside = alignRight ? widthScaled - horzBorderScaled : horzBorderScaled; const rightScaled = widthScaled; let xOutside = xInside; @@ -111,9 +110,6 @@ export class PriceAxisViewRenderer implements IPriceAxisViewRenderer { xText = xInside + tickSizeScaled + paddingInnerScaled; } - const textCorrectionOffset = Math.round(pixelRatio * (correctedTextWidth - textWidth) / 2); - xText += textCorrectionOffset; - const textColor = this._data.color || this._commonData.color; const backgroundColor = resetTransparency(this._commonData.background); From b9111c30f811f39ae0450b72ad934cc81384b095 Mon Sep 17 00:00:00 2001 From: Eugene Korobko Date: Tue, 30 Mar 2021 16:10:30 +0300 Subject: [PATCH 013/188] 606: fixed test compilation --- src/model/text-width-cache.ts | 8 +++++--- tests/unittests/text-width-cache.spec.ts | 6 ++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/model/text-width-cache.ts b/src/model/text-width-cache.ts index d4d1a7efe1..e33a6b741f 100644 --- a/src/model/text-width-cache.ts +++ b/src/model/text-width-cache.ts @@ -1,3 +1,5 @@ +export type CanvasCtxLike = Pick; + const defaultReplacementRe = /[2-9]/g; export class TextWidthCache { @@ -20,17 +22,17 @@ export class TextWidthCache { this._tick2Labels = {}; } - public measureText(ctx: CanvasRenderingContext2D, text: string, optimizationReplacementRe?: RegExp): number { + public measureText(ctx: CanvasCtxLike, text: string, optimizationReplacementRe?: RegExp): number { return this._getMetrics(ctx, text, optimizationReplacementRe).width; } - public yMidCorrection(ctx: CanvasRenderingContext2D, text: string, optimizationReplacementRe?: RegExp): number { + public yMidCorrection(ctx: CanvasCtxLike, text: string, optimizationReplacementRe?: RegExp): number { const metrics = this._getMetrics(ctx, text, optimizationReplacementRe); // if actualBoundingBoxAscent/actualBoundingBoxDescent are not supported we use 0 as a fallback return ((metrics.actualBoundingBoxAscent || 0) - (metrics.actualBoundingBoxDescent || 0)) / 2; } - private _getMetrics(ctx: CanvasRenderingContext2D, text: string, optimizationReplacementRe?: RegExp): TextMetrics { + private _getMetrics(ctx: CanvasCtxLike, text: string, optimizationReplacementRe?: RegExp): TextMetrics { const re = optimizationReplacementRe || defaultReplacementRe; const cacheString = String(text).replace(re, '0'); diff --git a/tests/unittests/text-width-cache.spec.ts b/tests/unittests/text-width-cache.spec.ts index 8e89a52a09..7f665f50ad 100644 --- a/tests/unittests/text-width-cache.spec.ts +++ b/tests/unittests/text-width-cache.spec.ts @@ -6,11 +6,17 @@ import { CanvasCtxLike, TextWidthCache } from '../../src/model/text-width-cache' class FakeCtx implements CanvasCtxLike { public readonly invocations: string[] = []; + public textBaseline: CanvasTextBaseline = 'alphabetic'; + public measureText(text: string): TextMetrics { this.invocations.push(text); return { width: this._impl(text) } as unknown as TextMetrics; } + public save(): void {} + + public restore(): void {} + protected _impl(text: string): number { let fakeWidth = 0; for (let i = 0; i < text.length; i++) { From 452310fe7f8593048de52602fb0a37319760cbcc Mon Sep 17 00:00:00 2001 From: Eugene Korobko Date: Tue, 30 Mar 2021 16:49:33 +0300 Subject: [PATCH 014/188] 606: removed useless code --- src/helpers/color.ts | 37 ----------------------- src/model/text-width-cache.ts | 14 +++++---- src/renderers/price-axis-view-renderer.ts | 5 ++- 3 files changed, 10 insertions(+), 46 deletions(-) diff --git a/src/helpers/color.ts b/src/helpers/color.ts index bb2e6ec4ea..2277eb5422 100644 --- a/src/helpers/color.ts +++ b/src/helpers/color.ts @@ -18,16 +18,8 @@ type GreenComponent = Nominal; */ type BlueComponent = Nominal; -/** - * Alpha component of the RGBA color value - * The valid values are float in range [0, 1] - */ -type AlphaComponent = Nominal; - type Rgb = [RedComponent, GreenComponent, BlueComponent]; -type Rgba = [RedComponent, GreenComponent, BlueComponent, AlphaComponent]; - /** @public see https://developer.mozilla.org/en-US/docs/Web/CSS/color_value */ const namedColorRgbHexStrings: Record = { // The order of properties in this Record is not important for the internal logic. @@ -271,23 +263,6 @@ function colorStringToRgb(colorString: string): Rgb { throw new Error(`Cannot parse color: ${colorString}`); } -function rgba(rgb: Rgb, alpha: AlphaComponent): Rgba { - return [ - rgb[0], - rgb[1], - rgb[2], - alpha, - ]; -} - -function rgbaToString(rgbaObj: Rgba): string { - const red = rgbaObj[0]; - const green = rgbaObj[1]; - const blue = rgbaObj[2]; - const alpha = rgbaObj[3]; - return `rgba(${red}, ${green}, ${blue}, ${alpha})`; -} - function rgbToGrayscale(rgbValue: Rgb): number { // Originally, the NTSC RGB to YUV formula // perfected by @eugene-korobko's black magic @@ -315,15 +290,3 @@ export function generateContrastColors(backgroundColor: string): ContrastColors foreground: rgbToGrayscale(rgb) > 160 ? 'black' : 'white', }; } - -export function isHexColor(color: string): boolean { - return color.indexOf('#') === 0; -} - -export function resetTransparency(color: string): string { - if (isHexColor(color)) { - return color; - } - - return rgbaToString(rgba(colorStringToRgb(color), 1 as AlphaComponent)); -} diff --git a/src/model/text-width-cache.ts b/src/model/text-width-cache.ts index e33a6b741f..86e7a39f46 100644 --- a/src/model/text-width-cache.ts +++ b/src/model/text-width-cache.ts @@ -1,3 +1,5 @@ +import { ensureDefined } from '../helpers/assertions'; + export type CanvasCtxLike = Pick; const defaultReplacementRe = /[2-9]/g; @@ -8,7 +10,7 @@ export class TextWidthCache { private _usageTick: number = 1; private _oldestTick: number = 1; private _tick2Labels: Record = {}; - private _cache: Record = {}; + private _cache: Map = new Map(); public constructor(size: number = 50) { this._maxSize = size; @@ -16,7 +18,7 @@ export class TextWidthCache { public reset(): void { this._actualSize = 0; - this._cache = {}; + this._cache.clear(); this._usageTick = 1; this._oldestTick = 1; this._tick2Labels = {}; @@ -36,14 +38,14 @@ export class TextWidthCache { const re = optimizationReplacementRe || defaultReplacementRe; const cacheString = String(text).replace(re, '0'); - if (this._cache[cacheString]) { - return this._cache[cacheString].metrics; + if (this._cache.has(cacheString)) { + return ensureDefined(this._cache.get(cacheString)).metrics; } if (this._actualSize === this._maxSize) { const oldestValue = this._tick2Labels[this._oldestTick]; delete this._tick2Labels[this._oldestTick]; - delete this._cache[oldestValue]; + this._cache.delete(oldestValue); this._oldestTick++; this._actualSize--; } @@ -58,7 +60,7 @@ export class TextWidthCache { return metrics; } - this._cache[cacheString] = { metrics: metrics, tick: this._usageTick }; + this._cache.set(cacheString, { metrics: metrics, tick: this._usageTick }); this._tick2Labels[this._usageTick] = cacheString; this._actualSize++; this._usageTick++; diff --git a/src/renderers/price-axis-view-renderer.ts b/src/renderers/price-axis-view-renderer.ts index 6c75084079..389b681fc4 100644 --- a/src/renderers/price-axis-view-renderer.ts +++ b/src/renderers/price-axis-view-renderer.ts @@ -1,5 +1,4 @@ import { drawRoundRectWithInnerBorder, drawScaled } from '../helpers/canvas-helpers'; -import { resetTransparency } from '../helpers/color'; import { TextWidthCache } from '../model/text-width-cache'; @@ -84,7 +83,7 @@ export class PriceAxisViewRenderer implements IPriceAxisViewRenderer { let xTick: number; let xText: number; - ctx.fillStyle = resetTransparency(this._commonData.background); + ctx.fillStyle = this._commonData.background; const radius = 2 * pixelRatio; ctx.textAlign = alignRight ? 'right' : 'left'; @@ -111,7 +110,7 @@ export class PriceAxisViewRenderer implements IPriceAxisViewRenderer { } const textColor = this._data.color || this._commonData.color; - const backgroundColor = resetTransparency(this._commonData.background); + const backgroundColor = (this._commonData.background); if (text) { const drawLabelBody = (labelBackgroundColor: string, labelBorderColor?: string): void => { From 8cce504d1a50b863a83ad259fed9c25b14c4ed77 Mon Sep 17 00:00:00 2001 From: Eugene Korobko Date: Thu, 1 Apr 2021 11:53:03 +0300 Subject: [PATCH 015/188] 606: Upgrate ts-transformer version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3ecdf97998..9939f75fe5 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "rollup-plugin-terser": "~7.0.2", "size-limit": "~4.9.2", "ts-node": "~9.1.1", - "ts-transformer-properties-rename": "~0.9.0", + "ts-transformer-properties-rename": "~0.10.0", "ts-transformer-strip-const-enums": "~1.0.1", "tslib": "2.1.0", "tslint": "6.1.3", From 73d9e2efed714868d4a3476e06945c76bc449218 Mon Sep 17 00:00:00 2001 From: Eugene Korobko Date: Wed, 7 Apr 2021 11:54:07 +0300 Subject: [PATCH 016/188] 606: Fixed price labels rendering --- src/api/options/layout-options-defaults.ts | 2 +- src/renderers/iprice-axis-view-renderer.ts | 1 + src/renderers/price-axis-view-renderer.ts | 181 +++++++++++------- src/views/price-axis/price-axis-view.ts | 2 + .../price-axis/series-price-axis-view.ts | 2 + 5 files changed, 119 insertions(+), 69 deletions(-) diff --git a/src/api/options/layout-options-defaults.ts b/src/api/options/layout-options-defaults.ts index 9cfbaff884..c59b7b732f 100644 --- a/src/api/options/layout-options-defaults.ts +++ b/src/api/options/layout-options-defaults.ts @@ -5,6 +5,6 @@ import { LayoutOptions } from '../../model/layout-options'; export const layoutOptionsDefaults: LayoutOptions = { backgroundColor: '#FFFFFF', textColor: '#191919', - fontSize: 12, + fontSize: 11, fontFamily: defaultFontFamily, }; diff --git a/src/renderers/iprice-axis-view-renderer.ts b/src/renderers/iprice-axis-view-renderer.ts index 6f57dea8f2..8682429ea9 100644 --- a/src/renderers/iprice-axis-view-renderer.ts +++ b/src/renderers/iprice-axis-view-renderer.ts @@ -20,6 +20,7 @@ export interface PriceAxisViewRendererData { color: string; lineWidth?: LineWidth; borderVisible: boolean; + separatorVisible: boolean; } export interface PriceAxisViewRendererOptions { diff --git a/src/renderers/price-axis-view-renderer.ts b/src/renderers/price-axis-view-renderer.ts index 389b681fc4..679a6d3806 100644 --- a/src/renderers/price-axis-view-renderer.ts +++ b/src/renderers/price-axis-view-renderer.ts @@ -9,6 +9,24 @@ import { PriceAxisViewRendererOptions, } from './iprice-axis-view-renderer'; +interface Geometry { + alignRight: boolean; + yTop: number; + yMid: number; + yBottom: number; + totalWidthScaled: number; + totalHeightScaled: number; + radius: number; + horzBorderScaled: number; + xOutside: number; + xInside: number; + xTick: number; + xText: number; + tickHeight: number; + rightScaled: number; + textMidCorrection: number; +} + export class PriceAxisViewRenderer implements IPriceAxisViewRenderer { private _data!: PriceAxisViewRendererData; private _commonData!: PriceAxisViewRendererCommonData; @@ -33,9 +51,86 @@ export class PriceAxisViewRenderer implements IPriceAxisViewRenderer { if (!this._data.visible) { return; } + const geometry = this._calculateGeometry(ctx, rendererOptions, textWidthCache, width, align, pixelRatio); + + const textColor = this._data.color || this._commonData.color; + const backgroundColor = (this._commonData.background); ctx.font = rendererOptions.font; + ctx.fillStyle = this._commonData.background; + + if (this._data.text) { + const drawLabelBody = (labelBackgroundColor: string, labelBorderColor?: string): void => { + if (geometry.alignRight) { + drawRoundRectWithInnerBorder( + ctx, + geometry.xOutside, + geometry.yTop, + geometry.totalWidthScaled, + geometry.totalHeightScaled, + labelBackgroundColor, + geometry.horzBorderScaled, + [geometry.radius, 0, 0, geometry.radius], + labelBorderColor + ); + } else { + drawRoundRectWithInnerBorder( + ctx, + geometry.xInside, + geometry.yTop, + geometry.totalWidthScaled, + geometry.totalHeightScaled, + labelBackgroundColor, + geometry.horzBorderScaled, + [0, geometry.radius, geometry.radius, 0], + labelBorderColor + ); + } + }; + + // draw border + // draw label background + drawLabelBody(backgroundColor, 'transparent'); + // draw tick + if (this._data.tickVisible) { + ctx.fillStyle = textColor; + ctx.fillRect(geometry.xInside, geometry.yMid, geometry.xTick - geometry.xInside, geometry.tickHeight); + } + // draw label border above the tick + drawLabelBody('transparent', backgroundColor); + + // draw separator + if (this._data.borderVisible) { + ctx.fillStyle = rendererOptions.paneBackgroundColor; + ctx.fillRect(geometry.alignRight ? geometry.rightScaled - geometry.horzBorderScaled : 0, geometry.yTop, geometry.horzBorderScaled, geometry.yBottom - geometry.yTop); + } + ctx.save(); + ctx.translate(geometry.xText, (geometry.yTop + geometry.yBottom) / 2 + geometry.textMidCorrection); + drawScaled(ctx, pixelRatio, () => { + ctx.fillStyle = textColor; + ctx.fillText(this._data.text, 0, 0); + }); + ctx.restore(); + } + } + + public height(rendererOptions: PriceAxisViewRendererOptions, useSecondLine: boolean): number { + if (!this._data.visible) { + return 0; + } + + return rendererOptions.fontSize + rendererOptions.paddingTop + rendererOptions.paddingBottom; + } + + private _calculateGeometry( + ctx: CanvasRenderingContext2D, + rendererOptions: PriceAxisViewRendererOptions, + textWidthCache: TextWidthCache, + width: number, + align: 'left' | 'right', + pixelRatio: number + ): Geometry { const tickSize = (this._data.tickVisible) ? rendererOptions.tickLength : 0; const horzBorder = rendererOptions.borderSize; const paddingTop = rendererOptions.paddingTop + this._commonData.additionalPaddingTop; @@ -58,7 +153,7 @@ export class PriceAxisViewRenderer implements IPriceAxisViewRenderer { totalHeightScaled += 1; } - const horzBorderScaled = Math.max(1, Math.floor(horzBorder * pixelRatio)); + const horzBorderScaled = this._data.separatorVisible ? Math.max(1, Math.floor(horzBorder * pixelRatio)) : 0; const totalWidthScaled = Math.round(totalWidth * pixelRatio); // tick overlaps scale border const tickSizeScaled = Math.round(tickSize * pixelRatio); @@ -83,7 +178,6 @@ export class PriceAxisViewRenderer implements IPriceAxisViewRenderer { let xTick: number; let xText: number; - ctx.fillStyle = this._commonData.background; const radius = 2 * pixelRatio; ctx.textAlign = alignRight ? 'right' : 'left'; @@ -108,71 +202,22 @@ export class PriceAxisViewRenderer implements IPriceAxisViewRenderer { xTick = xInside + tickSizeScaled; xText = xInside + tickSizeScaled + paddingInnerScaled; } - - const textColor = this._data.color || this._commonData.color; - const backgroundColor = (this._commonData.background); - - if (text) { - const drawLabelBody = (labelBackgroundColor: string, labelBorderColor?: string): void => { - if (alignRight) { - drawRoundRectWithInnerBorder( - ctx, - xOutside, - yTop, - totalWidthScaled, - totalHeightScaled, - labelBackgroundColor, - horzBorderScaled, - [radius, 0, 0, radius], - labelBorderColor - ); - } else { - drawRoundRectWithInnerBorder( - ctx, - xInside, - yTop, - totalWidthScaled, - totalHeightScaled, - labelBackgroundColor, - horzBorderScaled, - [0, radius, radius, 0], - labelBorderColor - ); - } - }; - - // draw border - // draw label background - drawLabelBody(backgroundColor, 'transparent'); - // draw tick - if (this._data.tickVisible) { - ctx.fillStyle = textColor; - ctx.fillRect(xInside, yMid, xTick - xInside, tickHeight); - } - // draw label border above the tick - drawLabelBody('transparent', backgroundColor); - - // draw separator - if (this._data.borderVisible) { - ctx.fillStyle = rendererOptions.paneBackgroundColor; - ctx.fillRect(alignRight ? rightScaled - horzBorderScaled : 0, yTop, horzBorderScaled, yBottom - yTop); - } - - ctx.save(); - ctx.translate(xText, (yTop + yBottom) / 2 + textMidCorrection); - drawScaled(ctx, pixelRatio, () => { - ctx.fillStyle = textColor; - ctx.fillText(text, 0, 0); - }); - ctx.restore(); - } - } - - public height(rendererOptions: PriceAxisViewRendererOptions, useSecondLine: boolean): number { - if (!this._data.visible) { - return 0; - } - - return rendererOptions.fontSize + rendererOptions.paddingTop + rendererOptions.paddingBottom; + return { + alignRight, + yTop, + yMid, + yBottom, + totalWidthScaled, + totalHeightScaled, + radius, + horzBorderScaled, + xOutside, + xInside, + xTick, + xText, + tickHeight, + rightScaled, + textMidCorrection, + }; } } diff --git a/src/views/price-axis/price-axis-view.ts b/src/views/price-axis/price-axis-view.ts index 9e09e4e78f..53a466b157 100644 --- a/src/views/price-axis/price-axis-view.ts +++ b/src/views/price-axis/price-axis-view.ts @@ -27,6 +27,7 @@ export abstract class PriceAxisView implements IPriceAxisView { borderColor: '', color: '#FFF', borderVisible: false, + separatorVisible: false, }; private readonly _paneRendererData: PriceAxisViewRendererData = { @@ -37,6 +38,7 @@ export abstract class PriceAxisView implements IPriceAxisView { borderColor: '', color: '#FFF', borderVisible: true, + separatorVisible: true, }; private readonly _axisRenderer: IPriceAxisViewRenderer; diff --git a/src/views/price-axis/series-price-axis-view.ts b/src/views/price-axis/series-price-axis-view.ts index e92b9ce33c..1705d002a4 100644 --- a/src/views/price-axis/series-price-axis-view.ts +++ b/src/views/price-axis/series-price-axis-view.ts @@ -56,6 +56,8 @@ export class SeriesPriceAxisView extends PriceAxisView { commonRendererData.coordinate = lastValueData.coordinate; paneRendererData.borderColor = this._source.model().options().layout.backgroundColor; axisRendererData.borderColor = lastValueColor; + axisRendererData.color = commonRendererData.color; + paneRendererData.color = commonRendererData.color; } protected _paneText( From cdf95aa4c49baee6ca797a6ec658d6670fe6172d Mon Sep 17 00:00:00 2001 From: Eugene Korobko Date: Fri, 30 Apr 2021 15:22:15 +0300 Subject: [PATCH 017/188] 606: Try to increase CI timeout --- .circleci/config.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e4ded9e650..5b32e07c8d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -63,7 +63,10 @@ commands: echo 'export TESTS_REPORT_FILE="test-results/graphics/results.xml"' >> $BASH_ENV echo 'export DEVICE_PIXEL_RATIO=<< parameters.devicePixelRatio >>' >> $BASH_ENV echo 'export PRODUCTION_BUILD=<< parameters.productionBuild >>' >> $BASH_ENV - - run: scripts/run-graphics-tests.sh + - run: + command: scripts/run-graphics-tests.sh + no_output_timeout: 20m + - store_test_results: path: test-results/ - store_artifacts: From 47d5356199edc937c068abf32bef38760e02d7e4 Mon Sep 17 00:00:00 2001 From: Eugene Korobko Date: Fri, 30 Apr 2021 15:27:44 +0300 Subject: [PATCH 018/188] 606: Try to increase CI timeout --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5b32e07c8d..918507219e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -64,8 +64,8 @@ commands: echo 'export DEVICE_PIXEL_RATIO=<< parameters.devicePixelRatio >>' >> $BASH_ENV echo 'export PRODUCTION_BUILD=<< parameters.productionBuild >>' >> $BASH_ENV - run: - command: scripts/run-graphics-tests.sh - no_output_timeout: 20m + command: scripts/run-graphics-tests.sh + no_output_timeout: 20m - store_test_results: path: test-results/ From 9be2f7c990aee67ed89f208cf8379b9e3efb206e Mon Sep 17 00:00:00 2001 From: Edward Dewhurst Date: Mon, 24 Jan 2022 12:45:50 +0000 Subject: [PATCH 019/188] Render chart from codeblock --- .eslintrc.js | 10 +- .vscode/settings.json | 1 - package.json | 1 + website/docs/intro.md | 14 +- website/docs/series-types.md | 178 ++++++------------ website/docusaurus.config.js | 1 + website/plugins/chart-codeblock/index.js | 9 + .../chart-codeblock/theme/CodeBlock/index.jsx | 64 +++++++ .../theme/CodeBlock/styles.module.css | 4 + website/static/img/area-series.png | Bin 78733 -> 0 bytes website/static/img/bar-series.png | Bin 52306 -> 0 bytes website/static/img/baseline-series.png | Bin 78232 -> 0 bytes website/static/img/candlestick-series.png | Bin 52700 -> 0 bytes website/static/img/first-chart.png | Bin 35458 -> 0 bytes website/static/img/histogram-series.png | Bin 50905 -> 0 bytes website/static/img/line-series.png | Bin 67849 -> 0 bytes 16 files changed, 153 insertions(+), 129 deletions(-) create mode 100644 website/plugins/chart-codeblock/index.js create mode 100644 website/plugins/chart-codeblock/theme/CodeBlock/index.jsx create mode 100644 website/plugins/chart-codeblock/theme/CodeBlock/styles.module.css delete mode 100644 website/static/img/area-series.png delete mode 100644 website/static/img/bar-series.png delete mode 100644 website/static/img/baseline-series.png delete mode 100644 website/static/img/candlestick-series.png delete mode 100644 website/static/img/first-chart.png delete mode 100644 website/static/img/histogram-series.png delete mode 100644 website/static/img/line-series.png diff --git a/.eslintrc.js b/.eslintrc.js index e2885f1def..c7a793dde1 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -59,6 +59,7 @@ module.exports = { 'prefer-arrow', 'unicorn', 'jsdoc', + 'eslint-plugin-react', ], settings: { jsdoc: { @@ -137,6 +138,12 @@ module.exports = { semi: ['error', 'always'], }, }, + { + files: ['**/*.jsx'], + rules: { + 'react/prop-types': 'off', + }, + }, { files: ['**/*.md'], processor: 'markdown/markdown', @@ -174,13 +181,12 @@ module.exports = { histogramSeries: true, lineSeries: true, series: true, + createChart: true, }, rules: { 'no-console': 'off', - 'no-undef': 'off', 'no-unused-vars': 'off', indent: ['error', 4], - 'unicorn/filename-case': 'off', }, }, diff --git a/.vscode/settings.json b/.vscode/settings.json index 0c39654774..bde0ff5079 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,7 +2,6 @@ "files.exclude": { "dist/**": true, "lib/**": true, - "tests/e2e/graphics/.gendata/**": true, "website/.docusaurus/**": true, "website/build/**": true, "website/docs/api/**": true, diff --git a/package.json b/package.json index 4fd7d96404..d93498282b 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "eslint-plugin-markdown": "~2.2.1", "eslint-plugin-mdx": "~1.16.0", "eslint-plugin-prefer-arrow": "~1.2.1", + "eslint-plugin-react": "^7.28.0", "eslint-plugin-tsdoc": "~0.2.14", "eslint-plugin-unicorn": "~41.0.0", "eslint-plugin-react": "~7.28.0", diff --git a/website/docs/intro.md b/website/docs/intro.md index 0e084059ba..aa76bcdcee 100644 --- a/website/docs/intro.md +++ b/website/docs/intro.md @@ -44,8 +44,8 @@ import { createChart } from 'lightweight-charts'; // ... // somewhere in your code -const firstChart = createChart(firstContainer); -const secondChart = createChart(secondContainer); +const firstChart = createChart(document.getElementById('firstContainer')); +const secondChart = createChart(document.getElementById('secondContainer')); ``` The result of this function is a [`IChartApi`](/api/interfaces/IChartApi.md) object, which you need to use to work with a chart instance. @@ -92,9 +92,7 @@ Note that regardless of the series type, the API calls are the same (the type of To set the data (or to replace all data items) to a series you need to use [`ISeriesApi.setData`](/api/interfaces/ISeriesApi.md#setdata) method: -```js -import { createChart } from 'lightweight-charts'; - +```js chart const chart = createChart(container); const areaSeries = chart.addAreaSeries(); @@ -124,11 +122,9 @@ candlestickSeries.setData([ { time: '2018-12-30', open: 106.33, high: 110.20, low: 90.39, close: 98.10 }, { time: '2018-12-31', open: 109.87, high: 114.69, low: 85.66, close: 111.26 }, ]); -``` - -It's pretty easy, isn't it? That's it, your chart is ready to be displayed on the page: -![First simple chart](/img/first-chart.png "First simple chart") +chart.timeScale().fitContent(); +``` ### Updating the data in a series diff --git a/website/docs/series-types.md b/website/docs/series-types.md index 512df9b4a4..2a8b4724c8 100644 --- a/website/docs/series-types.md +++ b/website/docs/series-types.md @@ -42,7 +42,16 @@ If you'd like to change any option of a series, you could do this in different w An area chart is basically a colored area between the line connecting all data points and [the time scale](./time-scale.md): -![Area chart example](/img/area-series.png "Area chart example") +```js chart +const chart = createChart(document.getElementById('container')); +const areaSeries = chart.addAreaSeries(); + +const data = [{ value: 0, time: 1642425322 }, { value: 8, time: 1642511722 }, { value: 10, time: 1642598122 }, { value: 20, time: 1642684522 }, { value: 3, time: 1642770922 }, { value: 43, time: 1642857322 }, { value: 41, time: 1642943722 }, { value: 43, time: 1643030122 }, { value: 56, time: 1643116522 }, { value: 46, time: 1643202922 }]; + +areaSeries.setData(data); + +chart.timeScale().fitContent(); +``` ## Bar @@ -55,7 +64,16 @@ A bar chart shows price movements in the form of bars. Vertical line length of a bar is limited by the highest and lowest price values. Open & Close values are represented by tick marks, on the left & right hand side of the bar respectively: -![Bar chart example](/img/bar-series.png "Bar chart example") +```js chart +const chart = createChart(document.getElementById('container')); +const barSeries = chart.addBarSeries(); + +const data = [{ open: 10, high: 10.63, low: 9.49, close: 9.55, time: 1642427876 }, { open: 9.55, high: 10.30, low: 9.42, close: 9.94, time: 1642514276 }, { open: 9.94, high: 10.17, low: 9.92, close: 9.78, time: 1642600676 }, { open: 9.78, high: 10.59, low: 9.18, close: 9.51, time: 1642687076 }, { open: 9.51, high: 10.46, low: 9.10, close: 10.17, time: 1642773476 }, { open: 10.17, high: 10.96, low: 10.16, close: 10.47, time: 1642859876 }, { open: 10.47, high: 11.39, low: 10.40, close: 10.81, time: 1642946276 }, { open: 10.81, high: 11.60, low: 10.30, close: 10.75, time: 1643032676 }, { open: 10.75, high: 11.60, low: 10.49, close: 10.93, time: 1643119076 }, { open: 10.93, high: 11.53, low: 10.76, close: 10.96, time: 1643205476 }]; + +barSeries.setData(data); + +chart.timeScale().fitContent(); +``` ## Baseline @@ -65,7 +83,16 @@ Open & Close values are represented by tick marks, on the left & right hand side A baseline is basically two colored areas (top and bottom) between the line connecting all data points and [the base value line](/api/interfaces/BaselineStyleOptions.md#basevalue): -![Baseline chart example](/img/baseline-series.png) +```js chart +const chart = createChart(document.getElementById('container')); +const baselineSeries = chart.addBaselineSeries({ baseValue: { type: 'price', price: 25 } }); + +const data = [{ value: 1, time: 1642425322 }, { value: 8, time: 1642511722 }, { value: 10, time: 1642598122 }, { value: 20, time: 1642684522 }, { value: 3, time: 1642770922 }, { value: 43, time: 1642857322 }, { value: 41, time: 1642943722 }, { value: 43, time: 1643030122 }, { value: 56, time: 1643116522 }, { value: 46, time: 1643202922 }]; + +baselineSeries.setData(data); + +chart.timeScale().fitContent(); +``` ## Candlestick @@ -76,7 +103,16 @@ A baseline is basically two colored areas (top and bottom) between the line conn A candlestick chart shows price movements in the form of candlesticks. On the candlestick chart, open & close values form a solid body of a candle while wicks show high & low values for a candlestick's time interval: -![Candlestick chart example](/img/candlestick-series.png "Candlestick chart example") +```js chart +const chart = createChart(document.getElementById('container')); +const candlestickSeries = chart.addCandlestickSeries(); + +const data = [{ open: 10, high: 10.63, low: 9.49, close: 9.55, time: 1642427876 }, { open: 9.55, high: 10.30, low: 9.42, close: 9.94, time: 1642514276 }, { open: 9.94, high: 10.17, low: 9.92, close: 9.78, time: 1642600676 }, { open: 9.78, high: 10.59, low: 9.18, close: 9.51, time: 1642687076 }, { open: 9.51, high: 10.46, low: 9.10, close: 10.17, time: 1642773476 }, { open: 10.17, high: 10.96, low: 10.16, close: 10.47, time: 1642859876 }, { open: 10.47, high: 11.39, low: 10.40, close: 10.81, time: 1642946276 }, { open: 10.81, high: 11.60, low: 10.30, close: 10.75, time: 1643032676 }, { open: 10.75, high: 11.60, low: 10.49, close: 10.93, time: 1643119076 }, { open: 10.93, high: 11.53, low: 10.76, close: 10.96, time: 1643205476 }]; + +candlestickSeries.setData(data); + +chart.timeScale().fitContent(); +``` ## Histogram @@ -87,7 +123,18 @@ On the candlestick chart, open & close values form a solid body of a candle whil A histogram series is a graphical representation of the value distribution. Histogram creates intervals (columns) and counts how many values fall into each column: -![Histogram example](/img/histogram-series.png "Histogram chart example") +```js chart +const chart = createChart( + document.getElementById('container') +); +const histogramSeries = chart.addHistogramSeries(); + +const data = [{ value: 1, time: 1642425322 }, { value: 8, time: 1642511722 }, { value: 10, time: 1642598122 }, { value: 20, time: 1642684522 }, { value: 3, time: 1642770922, color: 'red' }, { value: 43, time: 1642857322 }, { value: 41, time: 1642943722, color: 'red' }, { value: 43, time: 1643030122 }, { value: 56, time: 1643116522 }, { value: 46, time: 1643202922, color: 'red' }]; + +histogramSeries.setData(data); + +chart.timeScale().fitContent(); +``` ## Line @@ -97,116 +144,13 @@ Histogram creates intervals (columns) and counts how many values fall into each A line chart is a type of chart that displays information as series of the data points connected by straight line segments: -![Line chart example](/img/line-series.png "Line chart example") - - +const data = [{ value: 0, time: 1642425322 }, { value: 8, time: 1642511722 }, { value: 10, time: 1642598122 }, { value: 20, time: 1642684522 }, { value: 3, time: 1642770922 }, { value: 43, time: 1642857322 }, { value: 41, time: 1642943722 }, { value: 43, time: 1643030122 }, { value: 56, time: 1643116522 }, { value: 46, time: 1643202922 }]; + +lineSeries.setData(data); + +chart.timeScale().fitContent(); +``` diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 707afbe112..758bf73bb6 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -333,6 +333,7 @@ async function getConfig() { }), ], ...versions.map(typedocPluginForVersion), + './plugins/chart-codeblock', ], }; diff --git a/website/plugins/chart-codeblock/index.js b/website/plugins/chart-codeblock/index.js new file mode 100644 index 0000000000..7d78b509fa --- /dev/null +++ b/website/plugins/chart-codeblock/index.js @@ -0,0 +1,9 @@ +const path = require('path'); + +module.exports = function chartCodeBlockPlugin(context, options) { + return { + name: 'chart-codeblock', + + getThemePath: () => path.resolve(__dirname, './theme'), + }; +}; diff --git a/website/plugins/chart-codeblock/theme/CodeBlock/index.jsx b/website/plugins/chart-codeblock/theme/CodeBlock/index.jsx new file mode 100644 index 0000000000..b8b49ee7e7 --- /dev/null +++ b/website/plugins/chart-codeblock/theme/CodeBlock/index.jsx @@ -0,0 +1,64 @@ +import * as React from 'react'; +import CodeBlock from '@theme-init/CodeBlock'; +import BrowserOnly from '@docusaurus/BrowserOnly'; +import { createChart } from 'lightweight-charts'; + +import styles from './styles.module.css'; + +function getSrcDocWithScript(script) { + return ` + +
+ + `; +} + +const Chart = props => { + const { script } = props; + const srcDoc = getSrcDocWithScript(script); + + const ref = React.useRef(); + + React.useEffect(() => { + const contentWindow = ref.current.contentWindow; + + contentWindow.createChart = (container, options) => { + const chart = createChart(container, options); + const onResize = () => { + const boundingClientRect = container.getBoundingClientRect(); + chart.resize(boundingClientRect.width, boundingClientRect.height); + }; + + contentWindow.onresize = onResize; + + return chart; + }; + }, []); + + return ( + + + View in a new window + + +## Next steps + +In the next step, we will change the colors for the candlestick series. + +## Download + +You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. diff --git a/website/tutorials/customization/conclusion.mdx b/website/tutorials/customization/conclusion.mdx new file mode 100644 index 0000000000..f67f94a4db --- /dev/null +++ b/website/tutorials/customization/conclusion.mdx @@ -0,0 +1,24 @@ +--- +sidebar_position: 9 +title: Conclusion +pagination_title: Conclusion +sidebar_label: Conclusion +description: Conclusion to the customization tutorial +keywords: + - customization + - appearance + - styling +pagination_prev: customization/finishing-touches +pagination_next: null +--- + +Thank you for taking the time to complete this tutorial, and we hope that you've learnt how to customise your charts to meet your needs and more importantly where to find the available options in the documentation and how to apply them to your chart. + +We look forward to seeing what you create. + +We welcome any and all feedback on this tutorial and library in general. You can reach us via the following channels: + +- [Discord](https://discord.gg/UC7cGkvn4U) +- [GitHub Discussions](https://github.com/tradingview/lightweight-charts/discussions) + +and if you've spotted any errors or bugs then please post an issue on our [GitHub page](https://github.com/tradingview/lightweight-charts/issues). diff --git a/website/tutorials/customization/creating-a-chart.mdx b/website/tutorials/customization/creating-a-chart.mdx new file mode 100644 index 0000000000..014407dc5a --- /dev/null +++ b/website/tutorials/customization/creating-a-chart.mdx @@ -0,0 +1,112 @@ +--- +sidebar_position: 1 +title: First steps +pagination_title: First steps +sidebar_label: First steps +description: In this section, we will be creating a simple chart. +keywords: + - customization + - appearance + - styling +pagination_prev: customization/intro +pagination_next: customization/chart-colors +--- + +:::tip + +If you haven't already, please read the [Introduction](intro). At the bottom of that page, you will find a starting file containing everything you need to get up and running with this tutorial (including some sample data to display on the chart). + +::: + +Our first task will be to create a chart and get it visible on the page. A more detailed explanation of these steps is available [here](/docs). + +## Adding the Lightweight Charts script + +For this example, we will be loading the Lightweight Charts library using the standalone version hosted on a CDN server. This approach allows us to just include the script tag within our HTML file and not be concerned about spinning up a web server to host our files, and thus open the HTML file directly on our computer. + +We can add the script tag to the HTML page by including the following code within the `` element of the page. In this case, we will insert the code between the `` and `<style>` elements. + +```html +<title>Lightweight Charts Customization Tutorial + + + + + +``` + +and then add the following `
` element within the container element used for the chart. + +```html + +``` + +## Automatically resizing the chart when the window is resized + +Adding the following code snippet to the end of the script tag will make the chart resize automatically to fit the browser window whenever the window is resized. + +:::caution + +This will only work correctly if you want the chart to completely fill the window. + +::: + +```js +// Adding a window resize event handler to resize the chart when +// the window size changes. +// Note: for more advanced examples (when the chart doesn't fill the entire window) +// you may need to use ResizeObserver -> https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver +window.addEventListener('resize', () => { + chart.resize(window.innerWidth, window.innerHeight); +}); +``` + +## Result + +🎉 Congrats! At this point you should have the final chart which looks like this: + + + + View in a new window + + +## Download + +You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. diff --git a/website/tutorials/customization/intro.mdx b/website/tutorials/customization/intro.mdx new file mode 100644 index 0000000000..764bbbed27 --- /dev/null +++ b/website/tutorials/customization/intro.mdx @@ -0,0 +1,83 @@ +--- +sidebar_position: 0 +sidebar_label: Introduction +pagination_title: Introduction +title: Customizing the Chart +description: This tutorial provides an introduction to customizing Lightweight Chart's appearance and functionality. +keywords: + - customization + - appearance + - styling +pagination_prev: null +pagination_next: customization/creating-a-chart +--- + +# Customization - Introduction + +This tutorial provides an introduction to customizing Lightweight Chart's appearance and functionality. It is not meant as an exhaustive tutorial but rather as a guided tour on how and where to apply options within the API to adjust specific parts of the chart. Along the way, we will provide links to the API documentation which outline the full set of options available for each part of the chart. It is highly recommended that you explore these API links to discover the wide range of possible customization and feature flags contained within Lightweight Charts. + +## What we will be building + +Before we get started, let us have a look at what we will be building in this tutorial. + + + + View in a new window + + +## Topics to be covered + +The following topics will be covered within the tutorial: + +- Styling the main chart +- Styling a series +- Setting a custom price formatter +- Adjusting the Price Scale +- Adjusting the Time Scale +- Customising the Crosshair +- Adding a second series +- Customising the appearance of a few data points +- Adding a simple attribution message +- Setting a different font + +## Prerequisite knowledge + +The tutorial requires basic knowledge of: + +- Javascript +- HTML +- CSS + +:::tip + +The tutorial will assume that you've already read the [Getting Started](/docs) section even though we may repeat a few aspects from that guide. + +::: + +## Terminology + +- **Data Series (aka data/dataset):** A collection of data points representing a specific metric over time. +- **Series Type:** A series type specifies how to draw the data on the chart. For example, a line series type will plot the data series on the chart as a series of the data points connected by straight line segments. Available series types: [Series types | Lightweight Charts](/series-types) +- **Series:** A combination of a specified series type and a data series. +- **Price Scale:** Price Scale (or price axis) is a vertical scale that mostly maps prices to coordinates and vice versa. +- **Time Scale:** Time scale (or time axis) is a horizontal scale at the bottom of the chart that displays the time of bars. +- **Crosshair:** Thin vertical and horizontal lines centered on a data point in the chart. + +## How to set up the example so you can follow along + +This guide makes use of a single HTML file which you can download to your computer and run in the browser without the need for any build steps or web servers. The only thing required is an active internet connection such that the Lightweight Charts library can be downloaded from the CDN. + +Provided below is the 'starting point' file for the guide which is a simple HTML page scaffolded out with a single div element (`#container`) and a JS function to generate the sample data set. **At this point, you won't see anything on the page until we add the chart in the next step.** + +You can either: + +- + Download the file + and then edit and run the example on your computer, +- or [open this JSFiddle](https://jsfiddle.net/msilverwood/391hxf6b/) and then edit and run the example within the browser. + + +At the end of each section will be a link to download the example at that stage of the guide and a full code block. diff --git a/website/tutorials/customization/price-format.mdx b/website/tutorials/customization/price-format.mdx new file mode 100644 index 0000000000..5c86119d1f --- /dev/null +++ b/website/tutorials/customization/price-format.mdx @@ -0,0 +1,78 @@ +--- +sidebar_position: 4 +title: Price format +pagination_title: Price format +sidebar_label: Price format +description: In this section, we will be replacing the default price formatter function with our implementation. +keywords: + - customization + - appearance + - styling +pagination_prev: customization/series +pagination_next: customization/price-scale +--- + +In this section, we will be replacing the default price formatter function with our implementation. Currently, the prices on the chart are been shown with two decimal places and without a currency symbol. Let's implement a formatter which will format the number correctly based on their current locale and present it as the Euro currency. + +## Price Formatter functions + +To provide a price formatter, we need to create a function which accepts a `number` as the input and returns a `string` as the output. A simple price formatter which takes a number and returns the number (as a string) formatted with two decimal points would look as follows: + +```js +const myPriceFormatter = p => p.toFixed(2); +``` + +We can make use of the built-in functionality provided by the browser to build a more versatile price formatter by using the [Intl.NumberFormat API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat). + +```js +// Get the current users primary locale +const currentLocale = window.navigator.languages[0]; +// Create a number format using Intl.NumberFormat +const myPriceFormatter = Intl.NumberFormat(currentLocale, { + style: 'currency', + currency: 'EUR', // Currency for data points +}).format; +``` + +First, we are grabbing the primary locale for the current user which will pass into the `Intl.NumberFormat` constructor. The constructor takes a second argument for options where we can specify the `style` and `currency` properties. The instance created contains a `format` method which we can then pass into Lightweight Charts 9as shown below). + +## Setting the price formatter + +We can set the default price formatter to be used throughout the chart by passing our own custom price formatter function into the `priceformatter` property of the `localization` property ([LocalizationOptions](/docs/api/interfaces/LocalizationOptions)) within the chart options. + +```js +// Apply the custom priceFormatter to the chart +chart.applyOptions({ + localization: { + priceFormatter: myPriceFormatter, + }, +}); +``` + +Price values displayed on the vertical price scale will now be displayed as Euros. + +Price formatters can also be applied to each series individually by adjusting the [priceformat](/docs/api/interfaces/SeriesOptionsCommon#priceformat) property of the series options. + +## Built-in price formatting + +Lightweight Charts includes a few options to adjust the built-in price formatting which can be see here: [PriceFormatBuiltIn](/docs/api/interfaces/PriceFormatBuiltIn). + +## Result + +At this point we should have a chart like this: + + + + View in a new window + + +## Next steps + +In the next step, we will be making some adjustments to the functionality of the vertical price scale. + +## Download + +You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. diff --git a/website/tutorials/customization/price-scale.mdx b/website/tutorials/customization/price-scale.mdx new file mode 100644 index 0000000000..11e4e10d0b --- /dev/null +++ b/website/tutorials/customization/price-scale.mdx @@ -0,0 +1,69 @@ +--- +sidebar_position: 5 +title: Price scale +pagination_title: Price scale +sidebar_label: Price scale +description: In this section, we are going to adjust the functionality of the vertical price scale. +keywords: + - customization + - appearance + - styling +pagination_prev: customization/price-format +pagination_next: customization/time-scale +--- + +In this section, we are going to adjust the functionality of the vertical price scale. Currently, the price scale will auto-scale to fit the visible data on the chart. You can observe this behaviour by scrolling the chart to the right such that some of the data points are no longer visible. We will disable this feature such that the price scale can only be manually adjusted by the user (by clicking and dragging on the scale). + +:::tip + +A chart can have more than one price scale and their options can be individually adjusted, however in this example we only have one price scale. + +::: + +## Adjusting settings for the price scale + +We can get the current [IPriceScaleApi](/docs/api/interfaces/IPriceScaleApi) instance for the chart by evoking the `priceScale()` method on the candlestick series reference. + +Once again, we can use the `applyOptions()` method on this API instance to adjust it's options. You can add the following code to the example at any point after the `mainSeries` reference has been created, so let us place it at the end of the script. The options available are shown here: [PriceScaleOptions](/docs/api/interfaces/PriceScaleOptions). + +```js +// Adjust the options for the priceScale of the mainSeries +mainSeries.priceScale().applyOptions({ + autoScale: false, // disables auto scaling based on visible content + scaleMargins: { + top: 0.1, + bottom: 0.2, + }, +}); +``` + +As discussed above we are disabling the `autoScale` feature on this price scale. We are additionally adjusting the `scaleMargins` which are used when the chart is first rendered to determine the 'zoom' and position of the scale. The scale margins set the proportion of the chart to be empty above and below the data points currently visible. + +## Result + +At this point, we should have a chart like the one presented below. Play around with scrolling the data left and right to see the change of behaviour versus the previous step. + +### Before + + + +### After + + + + View in a new window + + +## Next steps + +In the next step, we will be adjusting the settings on the horizontal time scale. + +## Download + +You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. diff --git a/website/tutorials/customization/second-series.mdx b/website/tutorials/customization/second-series.mdx new file mode 100644 index 0000000000..01ba6e24d0 --- /dev/null +++ b/website/tutorials/customization/second-series.mdx @@ -0,0 +1,94 @@ +--- +sidebar_position: 8 +title: Adding a second series +pagination_title: Adding a second series +sidebar_label: Adding a second series +description: In this section, we will be adding an area series to the chart with a subtle vertical gradient. +keywords: + - customization + - appearance + - styling +pagination_prev: customization/crosshair +pagination_next: customization/data-points +--- + +In this section, we will be adding an [area series](/docs/series-types#area) to the chart with a subtle vertical gradient. It's purpose is solely for aesthetic reasons (only to make the chart more visually appealing). However, it will teach us a few key points about the differences between different series types and the visual stacking order. + +## Preparing the data for the area series + +The data structure required for the area series isn't the same as for the candlestick series. The area series is expecting each data point to have the following properties: `time` and `value`. Whereas the candlestick data points don't have a `value` property but rather the following properties: `open`, `close`, `high`, `low`, and `time`. + +We can create a copy of the candlestick data and transform it in a single step by using a `map` higher-order function ([more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)). We will set the `value` for the area series as the midpoint between the `open` and `close` values of the candlestick data points. + +```js +// Generate sample data to use within a candlestick series +const candleStickData = generateCandlestickData(); + +// highlight-start +// Convert the candlestick data for use with a line series +const lineData = candleStickData.map(datapoint => ({ + time: datapoint.time, + value: (datapoint.close + datapoint.open) / 2, +})); +// highlight-end +``` + +## Adding the area series and setting it's options + +We can add the area series as we did with the candlestick series by calling the `addAreaSeries()` method on the chart instance. We will pass the options for the series as the first argument to `addAreaSeries()` method instead of separately calling `applyOptions()` at a later stage. + +:::caution + +Make sure to add this code **before** the `addCandlestickSeries()` call already in the code because we want it to appear below the candlesticks (as explained in the next section). + +::: + +```js +// Convert the candlestick data for use with a line series +const lineData = candleStickData.map(datapoint => ({ + time: datapoint.time, + value: (datapoint.close + datapoint.open) / 2, +})); + +// highlight-start +// Add an area series to the chart, +// Adding this before we add the candlestick chart +// so that it will appear beneath the candlesticks +const areaSeries = chart.addAreaSeries({ + lastValueVisible: false, // hide the last value marker for this series + crosshairMarkerVisible: false, // hide the crosshair marker for this series + lineColor: 'transparent', // hide the line + topColor: 'rgba(56, 33, 110,0.6)', + bottomColor: 'rgba(56, 33, 110, 0.1)', +}); +// Set the data for the Area Series +areaSeries.setData(lineData); +// highlight-end + +// Create the Main Series (Candlesticks) +const mainSeries = chart.addCandlestickSeries(); +``` + +## Visual stacking order of series + +When adding multiple series to a single chart, it is important to take note of the order in which they are added because that will determine the visual stacking order (when they overlap). The first series added will appear at the bottom of the stack and each series added will be placed on top of the stack. Thus in the current example, we want the area series to appear below the candlestick series so we will make sure to first add the area series and then the candlestick series. + +## Result + +At this point we should have a chart like this: + + + + View in a new window + + +## Next steps + +In the next step, we will look at how to adjust the colour of individual candlesticks on our chart. + +## Download + +You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. diff --git a/website/tutorials/customization/series.mdx b/website/tutorials/customization/series.mdx new file mode 100644 index 0000000000..7b45230130 --- /dev/null +++ b/website/tutorials/customization/series.mdx @@ -0,0 +1,60 @@ +--- +sidebar_position: 3 +title: Series colors +pagination_title: Series colors +sidebar_label: Series colors +description: In this section, we will be customizing the visual styling of the candlestick series. +keywords: + - customization + - appearance + - styling +pagination_prev: customization/chart-colors +pagination_next: customization/price-format +--- + +In this section, we will be customizing the visual styling of the candlestick series. + +We can add our custom options to the series by using the `applyOptions` method on the ISeriesApi instance for the candlestick series. In other words, we can call the `applyOptions` method on the `mainSeries` variable (which was returned when we evoked `addCandlestickSeries()` earlier). + +The available options for the candlestick series is a combination of the following interfaces: [CandlestickStyleOptions](/docs/api/interfaces/CandlestickStyleOptions) and [SeriesOptionsCommon](/docs/api/interfaces/SeriesOptionsCommon). + +## Setting custom colors for the candlestick series + +We are going to set the colors such that upward candles will be a light blue and downward candles will be a vibrant red. The color for the body of the candle is determined by the `upColor` and `downColor` properties, whilst the wick colors are determined by `wickUpColor` and `wickDownColor`. We will additionally disable the border on the candlestick for this example. + +We can apply these options at any point in the code after we have created the candlestick series, and in this case, we will place the code below the `setData()` call (but it would still work if was placed before). + +```js +mainSeries.setData(candleStickData); + +// highlight-start +// Changing the Candlestick colors +mainSeries.applyOptions({ + wickUpColor: 'rgb(54, 116, 217)', + upColor: 'rgb(54, 116, 217)', + wickDownColor: 'rgb(225, 50, 85)', + downColor: 'rgb(225, 50, 85)', + borderVisible: false, +}); +// highlight-end +``` + +## Result + +At this point we should have a chart like this: + + + + View in a new window + + +## Next steps + +In the next step, we will set a price formatter so we can customize the formatting of numbers on the chart. + +## Download + +You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. diff --git a/website/tutorials/customization/time-scale.mdx b/website/tutorials/customization/time-scale.mdx new file mode 100644 index 0000000000..b5781ad903 --- /dev/null +++ b/website/tutorials/customization/time-scale.mdx @@ -0,0 +1,71 @@ +--- +sidebar_position: 6 +title: Time scale +pagination_title: Time scale +sidebar_label: Time scale +description: In the previous step, we adjusted the vertical price scale, now let us adjust the horizontal time scale. +keywords: + - customization + - appearance + - styling +pagination_prev: customization/price-scale +pagination_next: customization/crosshair +--- + +In the previous step, we adjusted the vertical price scale, now let us adjust the horizontal time scale. We previously adjusted the border color of this scale and now we are going to adjust the starting 'zoom' level. + +We will be adjusting the [`barSpacing`](/docs/api/interfaces/TimeScaleOptions#barspacing) option on the time scale which is used when the chart is first rendered to determine the horizontal 'zoom' level. The property sets 'The space between bars in pixels.', where a larger number will result in wider bars and fewer bars visible on the chart. The default value is `6` and we will be increasing it to `10` which will effectively 'zoom in' for the time scale. + +## Adjusting settings for the time scale + +We can get the [ITimeScaleApi](/docs/api/interfaces/ITimeScaleApi) instance for the chart by evoking the `timeScale()` method on the chart reference. + +Just as with the previous step, we can use the `applyOptions()` method on this API instance to adjust it's options. You can add the following code to the example at any point after the `chart` reference has been created, but we will place it directly below where we set the border color for the time scale. The options available are shown here: [TimeScaleOptions](/docs/api/interfaces/TimeScaleOptions). + +```js +// Setting the border color for the horizontal axis +chart.timeScale().applyOptions({ + borderColor: '#71649C', +}); + +// highlight-start +// Adjust the starting bar width (essentially the horizontal zoom) +chart.timeScale().applyOptions({ + barSpacing: 10, +}); +// highlight-end +``` + +The `applyOptions()` calls on the time scale were purposely split into two to show that it is possible to apply the options individually if that leads to cleaner code to read. It is also possible to apply both options in a single step as shown below: + +```js +// Example of applying both properties in a single call +chart.timeScale().applyOptions({ + borderColor: '#71649C', + barSpacing: 10, +}); +``` + +## Auto fitting all the content + +It is possible to auto fit all the content into the visable area of the chart by calling the [`fitContent()`](/docs/api/interfaces/ITimeScaleApi#fitcontent) method on the time scale instance, for example: `chart.timeScale().fitContent();` + +## Result + +At this point we should have a chart like this (noting the wider candlestick bars): + + + + View in a new window + + +## Next steps + +In the next step, we will be adjusting the visual style and behaviour of the crosshair. + +## Download + +You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. diff --git a/website/tutorials/index.mdx b/website/tutorials/index.mdx index ac314d13de..5962ecd54e 100644 --- a/website/tutorials/index.mdx +++ b/website/tutorials/index.mdx @@ -1,4 +1,5 @@ --- +sidebar_position: 0 pagination_next: null --- From 08ce4e9431aed251cfddc4f57ce34a34eca0bc34 Mon Sep 17 00:00:00 2001 From: Mark Silverwood Date: Mon, 15 Aug 2022 16:55:19 +0200 Subject: [PATCH 113/188] update `useColorMode` usage Updated `useColorMode` usage for newer version of docusaurus. --- .../src/components/tutorials/themed-chart-colors-wrapper.tsx | 3 ++- website/src/pages/index.tsx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/website/src/components/tutorials/themed-chart-colors-wrapper.tsx b/website/src/components/tutorials/themed-chart-colors-wrapper.tsx index a004c8d394..24468d20c0 100644 --- a/website/src/components/tutorials/themed-chart-colors-wrapper.tsx +++ b/website/src/components/tutorials/themed-chart-colors-wrapper.tsx @@ -28,7 +28,8 @@ function getThemeColors(isDarkTheme: boolean): ThemedChartColors { } export function useThemedChartColors(): ThemedChartColors { - const { isDarkTheme } = useColorMode(); + const { colorMode } = useColorMode(); + const isDarkTheme = colorMode === 'dark'; const [colors, setColors] = useState(getThemeColors(isDarkTheme)); useEffect( diff --git a/website/src/pages/index.tsx b/website/src/pages/index.tsx index 27569de48c..df13f3eefc 100644 --- a/website/src/pages/index.tsx +++ b/website/src/pages/index.tsx @@ -25,7 +25,8 @@ function getLayoutOptionsForTheme(isDarkTheme: boolean): DeepPartial { - const { isDarkTheme } = useColorMode(); + const { colorMode } = useColorMode(); + const isDarkTheme = colorMode === 'dark'; const [layoutOptions, setLayoutOptions] = React.useState>(getLayoutOptionsForTheme(isDarkTheme)); From 4deda43e33ec914290612c6c7f68e4ae58ae8fb9 Mon Sep 17 00:00:00 2001 From: Mark Silverwood Date: Mon, 15 Aug 2022 20:04:49 +0200 Subject: [PATCH 114/188] reworked tutorials index page Reworked tutorials index page. - Added 'Guides' and 'Framework Integrations' categories. - Added `CardLink` component for document card links that support images. --- website/sidebars-tutorials.js | 27 ++++++- website/src/components/CardLink/index.tsx | 79 +++++++++++++++++++ .../src/components/CardLink/styles.module.css | 44 +++++++++++ website/src/components/CardLinkList/index.tsx | 22 ++++++ website/src/img/react.svg | 1 + website/tutorials/index.mdx | 45 ++++++++--- 6 files changed, 204 insertions(+), 14 deletions(-) create mode 100644 website/src/components/CardLink/index.tsx create mode 100644 website/src/components/CardLink/styles.module.css create mode 100644 website/src/components/CardLinkList/index.tsx create mode 100644 website/src/img/react.svg diff --git a/website/sidebars-tutorials.js b/website/sidebars-tutorials.js index 605525843a..e609c78deb 100644 --- a/website/sidebars-tutorials.js +++ b/website/sidebars-tutorials.js @@ -2,10 +2,29 @@ /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ const sidebars = { - tutorialsSidebar: [{ - type: 'autogenerated', - dirName: '.', - }], + tutorialsSidebar: [ + 'index', + { + Guides: [ + { Customization: [ + { + type: 'autogenerated', + dirName: 'customization', + }, + ] }, + ], + }, + { + 'Framework Integrations': [ + { React: [ + { + type: 'autogenerated', + dirName: 'react', + }, + ] }, + ], + }, + ], }; module.exports = sidebars; diff --git a/website/src/components/CardLink/index.tsx b/website/src/components/CardLink/index.tsx new file mode 100644 index 0000000000..eaa581939b --- /dev/null +++ b/website/src/components/CardLink/index.tsx @@ -0,0 +1,79 @@ +import Link from '@docusaurus/Link'; +import clsx from 'clsx'; +import React, { type ReactNode } from 'react'; + +import styles from './styles.module.css'; + +function CardContainer({ + href, + children, +}: { + href: string; + children: ReactNode; +}): JSX.Element { + return ( + + {children} + + ); +} + +function CardLayout({ + href, + icon, + title, + description, + image, +}: { + href: string; + title: string; + description?: string; + image?: ReactNode; + icon?: ReactNode; +}): JSX.Element { + return ( + + {image &&
{image}
} +
+

+ {icon} {title} +

+ {description && ( +

+ {description} +

+ )} +
+
+ ); +} + +export interface CardLinkItem { + href: string; + title: string; + icon?: string; + image?: ReactNode; + description?: string; +} + +export default function CardLink({ + item, +}: { + item: CardLinkItem; +}): JSX.Element { + return ( + + ); +} diff --git a/website/src/components/CardLink/styles.module.css b/website/src/components/CardLink/styles.module.css new file mode 100644 index 0000000000..b2229342be --- /dev/null +++ b/website/src/components/CardLink/styles.module.css @@ -0,0 +1,44 @@ +.cardContainer { + --ifm-link-color: var(--ifm-color-emphasis-800); + --ifm-link-hover-color: var(--ifm-color-emphasis-700); + --ifm-link-hover-decoration: none; + + box-shadow: 0 1.5px 3px 0 rgb(0 0 0 / 15%); + border: 1px solid var(--ifm-color-emphasis-200); + transition: all var(--ifm-transition-fast) ease; + transition-property: border, box-shadow; + + flex-direction: row; +} + +.cardContainer:hover { + border-color: var(--ifm-color-primary); + box-shadow: 0 3px 6px 0 rgb(0 0 0 / 20%); +} + +.cardContainer *:last-child { + margin-bottom: 0; +} + +.cardDetails { + display: flex; + flex-direction: column; + overflow: auto; +} + +.cardImage { + margin-right: 1rem; +} + +.cardImage svg { + height: 62px; + width: 62px; +} + +.cardTitle { + font-size: 1.2rem; +} + +.cardDescription { + font-size: 0.8rem; +} diff --git a/website/src/components/CardLinkList/index.tsx b/website/src/components/CardLinkList/index.tsx new file mode 100644 index 0000000000..a9b8d11a4c --- /dev/null +++ b/website/src/components/CardLinkList/index.tsx @@ -0,0 +1,22 @@ +import clsx from 'clsx'; +import React from 'react'; + +import { default as CardLink, type CardLinkItem } from '../CardLink'; + +interface Props { + className: string; + items: CardLinkItem[]; +} + +export default function DocCardList({ items, className }: Props): JSX.Element { + return ( +
+ {items.map((item: CardLinkItem, index: number) => ( +
+ +
+ ))} +
+ ); +} + diff --git a/website/src/img/react.svg b/website/src/img/react.svg new file mode 100644 index 0000000000..06d9d58129 --- /dev/null +++ b/website/src/img/react.svg @@ -0,0 +1 @@ + diff --git a/website/tutorials/index.mdx b/website/tutorials/index.mdx index 5962ecd54e..f448bf2ccc 100644 --- a/website/tutorials/index.mdx +++ b/website/tutorials/index.mdx @@ -3,22 +3,47 @@ sidebar_position: 0 pagination_next: null --- +import CardLinkList from "@site/src/components/CardLinkList"; +import Shapes from "@site/src/img/shapes.svg"; +import ReactLogo from "@site/src/img/react.svg"; + # Tutorials :::caution These tutorials are for the latest published version of Lightweight Charts. ::: -:::info +## Guides + +, + description: "Customizing appearance & features", + }, + ]} +/> + +## Framework integrations + This section contains some tutorials how to use Lightweight Charts with some popular frameworks. -If you think that a tutorial is missing feel free to ask [in the discussions](https://github.com/tradingview/lightweight-charts/discussions) or submit your own. -::: -import DocCardList from '@theme/DocCardList'; -import { useDocsSidebar } from '@docusaurus/theme-common/internal'; +, + description: "Integration guide for React", + }, + ]} +/> + +:::info - x.docId !== 'index') -} /> +If you think that a tutorial is missing feel free to ask [in the discussions](https://github.com/tradingview/lightweight-charts/discussions) +or submit your own. + +::: From 2ee6a73ba51dec2c520bed8add74b0ada27eced9 Mon Sep 17 00:00:00 2001 From: Mark Silverwood Date: Tue, 16 Aug 2022 12:39:14 +0200 Subject: [PATCH 115/188] add full code blocks to customization tut pages --- website/tutorials/customization/chart-colors.mdx | 12 ++++++++++++ website/tutorials/customization/creating-a-chart.mdx | 12 ++++++++++++ website/tutorials/customization/crosshair.mdx | 12 ++++++++++++ website/tutorials/customization/data-points.mdx | 12 ++++++++++++ .../tutorials/customization/finishing-touches.mdx | 12 ++++++++++++ website/tutorials/customization/price-format.mdx | 12 ++++++++++++ website/tutorials/customization/price-scale.mdx | 12 ++++++++++++ website/tutorials/customization/second-series.mdx | 12 ++++++++++++ website/tutorials/customization/series.mdx | 12 ++++++++++++ website/tutorials/customization/time-scale.mdx | 12 ++++++++++++ 10 files changed, 120 insertions(+) diff --git a/website/tutorials/customization/chart-colors.mdx b/website/tutorials/customization/chart-colors.mdx index 11787dcbf0..91a2819bb7 100644 --- a/website/tutorials/customization/chart-colors.mdx +++ b/website/tutorials/customization/chart-colors.mdx @@ -132,3 +132,15 @@ In the next step, we will change the colors for the candlestick series. ## Download You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. + +## Complete code + +import CodeBlock from '@theme/CodeBlock'; +import code from '!!raw-loader!./assets/step2.html'; + +
+ +Click here to reveal the complete code for the example at this stage of the guide. + +{code} +
diff --git a/website/tutorials/customization/creating-a-chart.mdx b/website/tutorials/customization/creating-a-chart.mdx index 014407dc5a..cd565c5818 100644 --- a/website/tutorials/customization/creating-a-chart.mdx +++ b/website/tutorials/customization/creating-a-chart.mdx @@ -110,3 +110,15 @@ The rest of the tutorial will focus on how to customise this chart to further ma ## Download You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. + +## Complete code + +import CodeBlock from '@theme/CodeBlock'; +import code from '!!raw-loader!./assets/step1.html'; + +
+ +Click here to reveal the complete code for the example at this stage of the guide. + +{code} +
diff --git a/website/tutorials/customization/crosshair.mdx b/website/tutorials/customization/crosshair.mdx index ec610c1a25..c2ad7e0d8d 100644 --- a/website/tutorials/customization/crosshair.mdx +++ b/website/tutorials/customization/crosshair.mdx @@ -72,3 +72,15 @@ In the next step, we will be adding a second series to the chart such that we ca ## Download You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. + +## Complete code + +import CodeBlock from '@theme/CodeBlock'; +import code from '!!raw-loader!./assets/step7.html'; + +
+ +Click here to reveal the complete code for the example at this stage of the guide. + +{code} +
diff --git a/website/tutorials/customization/data-points.mdx b/website/tutorials/customization/data-points.mdx index 3edba07f45..8c1c1ddb72 100644 --- a/website/tutorials/customization/data-points.mdx +++ b/website/tutorials/customization/data-points.mdx @@ -57,3 +57,15 @@ In the next step, we will add a few finishing touches to the chart. ## Download You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. + +## Complete code + +import CodeBlock from '@theme/CodeBlock'; +import code from '!!raw-loader!./assets/step9.html'; + +
+ +Click here to reveal the complete code for the example at this stage of the guide. + +{code} +
diff --git a/website/tutorials/customization/finishing-touches.mdx b/website/tutorials/customization/finishing-touches.mdx index 37ee24e290..30fc8bd33a 100644 --- a/website/tutorials/customization/finishing-touches.mdx +++ b/website/tutorials/customization/finishing-touches.mdx @@ -129,3 +129,15 @@ window.addEventListener('resize', () => { ## Download You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. + +## Complete code + +import CodeBlock from '@theme/CodeBlock'; +import code from '!!raw-loader!./assets/step10.html'; + +
+ +Click here to reveal the complete code for the example at this stage of the guide. + +{code} +
diff --git a/website/tutorials/customization/price-format.mdx b/website/tutorials/customization/price-format.mdx index 5c86119d1f..4716748890 100644 --- a/website/tutorials/customization/price-format.mdx +++ b/website/tutorials/customization/price-format.mdx @@ -76,3 +76,15 @@ In the next step, we will be making some adjustments to the functionality of the ## Download You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. + +## Complete code + +import CodeBlock from '@theme/CodeBlock'; +import code from '!!raw-loader!./assets/step4.html'; + +
+ +Click here to reveal the complete code for the example at this stage of the guide. + +{code} +
diff --git a/website/tutorials/customization/price-scale.mdx b/website/tutorials/customization/price-scale.mdx index 11e4e10d0b..e444f86d78 100644 --- a/website/tutorials/customization/price-scale.mdx +++ b/website/tutorials/customization/price-scale.mdx @@ -67,3 +67,15 @@ In the next step, we will be adjusting the settings on the horizontal time scale ## Download You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. + +## Complete code + +import CodeBlock from '@theme/CodeBlock'; +import code from '!!raw-loader!./assets/step5.html'; + +
+ +Click here to reveal the complete code for the example at this stage of the guide. + +{code} +
diff --git a/website/tutorials/customization/second-series.mdx b/website/tutorials/customization/second-series.mdx index 01ba6e24d0..ad0163e85a 100644 --- a/website/tutorials/customization/second-series.mdx +++ b/website/tutorials/customization/second-series.mdx @@ -92,3 +92,15 @@ In the next step, we will look at how to adjust the colour of individual candles ## Download You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. + +## Complete code + +import CodeBlock from '@theme/CodeBlock'; +import code from '!!raw-loader!./assets/step8.html'; + +
+ +Click here to reveal the complete code for the example at this stage of the guide. + +{code} +
diff --git a/website/tutorials/customization/series.mdx b/website/tutorials/customization/series.mdx index 7b45230130..f018927202 100644 --- a/website/tutorials/customization/series.mdx +++ b/website/tutorials/customization/series.mdx @@ -58,3 +58,15 @@ In the next step, we will set a price formatter so we can customize the formatti ## Download You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. + +## Complete code + +import CodeBlock from '@theme/CodeBlock'; +import code from '!!raw-loader!./assets/step3.html'; + +
+ +Click here to reveal the complete code for the example at this stage of the guide. + +{code} +
diff --git a/website/tutorials/customization/time-scale.mdx b/website/tutorials/customization/time-scale.mdx index b5781ad903..fc1b64b054 100644 --- a/website/tutorials/customization/time-scale.mdx +++ b/website/tutorials/customization/time-scale.mdx @@ -69,3 +69,15 @@ In the next step, we will be adjusting the visual style and behaviour of the cro ## Download You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. + +## Complete code + +import CodeBlock from '@theme/CodeBlock'; +import code from '!!raw-loader!./assets/step6.html'; + +
+ +Click here to reveal the complete code for the example at this stage of the guide. + +{code} +
From b16f426ce0b4a745c63f82705e47c9758e87363d Mon Sep 17 00:00:00 2001 From: Mark Silverwood Date: Tue, 16 Aug 2022 13:07:23 +0200 Subject: [PATCH 116/188] add 'iterative guide' note to customization tutorial pages Add a warning message to the top of the customization tutorial pages that the guide is iterative. change last note on intro page to a `tip` block --- website/tutorials/customization/chart-colors.mdx | 8 ++++++++ website/tutorials/customization/conclusion.mdx | 2 +- website/tutorials/customization/crosshair.mdx | 8 ++++++++ website/tutorials/customization/data-points.mdx | 8 ++++++++ website/tutorials/customization/finishing-touches.mdx | 8 ++++++++ website/tutorials/customization/intro.mdx | 6 +++++- website/tutorials/customization/price-format.mdx | 8 ++++++++ website/tutorials/customization/price-scale.mdx | 8 ++++++++ website/tutorials/customization/second-series.mdx | 8 ++++++++ website/tutorials/customization/series.mdx | 8 ++++++++ website/tutorials/customization/time-scale.mdx | 8 ++++++++ 11 files changed, 78 insertions(+), 2 deletions(-) diff --git a/website/tutorials/customization/chart-colors.mdx b/website/tutorials/customization/chart-colors.mdx index 91a2819bb7..96f72e7514 100644 --- a/website/tutorials/customization/chart-colors.mdx +++ b/website/tutorials/customization/chart-colors.mdx @@ -12,6 +12,14 @@ pagination_prev: customization/creating-a-chart pagination_next: customization/series --- +:::info + +**This page is part of an iterative guide (where we build onto code from the previous steps).** + +It is recommend that you follow the guide from the [start](intro). However, if you are only interested in the content on this page then take a look at the [full code](#complete-code) at the bottom of page for context of how this code fits into a working example. + +::: + In this section, we will be customizing the appearance of the chart 'backdrop'. We will be adjusting the colors to better suit a dark theme. ## Setting the background color of the HTML body diff --git a/website/tutorials/customization/conclusion.mdx b/website/tutorials/customization/conclusion.mdx index f67f94a4db..cff9677cd3 100644 --- a/website/tutorials/customization/conclusion.mdx +++ b/website/tutorials/customization/conclusion.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 9 +sidebar_position: 10 title: Conclusion pagination_title: Conclusion sidebar_label: Conclusion diff --git a/website/tutorials/customization/crosshair.mdx b/website/tutorials/customization/crosshair.mdx index c2ad7e0d8d..e4f130bfd2 100644 --- a/website/tutorials/customization/crosshair.mdx +++ b/website/tutorials/customization/crosshair.mdx @@ -12,6 +12,14 @@ pagination_prev: customization/time-scale pagination_next: customization/second-series --- +:::info + +**This page is part of an iterative guide (where we build onto code from the previous steps).** + +It is recommend that you follow the guide from the [start](intro). However, if you are only interested in the content on this page then take a look at the [full code](#complete-code) at the bottom of page for context of how this code fits into a working example. + +::: + In this section, we will be customizing the appearance of the crosshair and it's functionality. The crosshair can be customized within the options of the [IChartApi](/docs/api/interfaces/IChartApi) instance. You can view the full set of crosshair options available here: [CrosshairOptions](/docs/api/interfaces/CrosshairOptions). diff --git a/website/tutorials/customization/data-points.mdx b/website/tutorials/customization/data-points.mdx index 8c1c1ddb72..c014a9f46c 100644 --- a/website/tutorials/customization/data-points.mdx +++ b/website/tutorials/customization/data-points.mdx @@ -12,6 +12,14 @@ pagination_prev: customization/second-series pagination_next: customization/finishing-touches --- +:::info + +**This page is part of an iterative guide (where we build onto code from the previous steps).** + +It is recommend that you follow the guide from the [start](intro). However, if you are only interested in the content on this page then take a look at the [full code](#complete-code) at the bottom of page for context of how this code fits into a working example. + +::: + In this section, we will modify the candlestick data such that we can individually set the color for each candlestick. If we take a look at the interface for the candlestick data here: [CandlestickData](/docs/api/interfaces/CandlestickData), we can see a few properties related to color: `color`, `borderColor`, and `wickColor`. Lightweight Charts allows the data points to override the colors specified for series as a whole. In other words, if a datapoint has one of these properties set then it will use that color for that data point instead of the colors used for the rest of the series. We can use this feature to highlight a few key data points in a different color. diff --git a/website/tutorials/customization/finishing-touches.mdx b/website/tutorials/customization/finishing-touches.mdx index 30fc8bd33a..3e4679aaa3 100644 --- a/website/tutorials/customization/finishing-touches.mdx +++ b/website/tutorials/customization/finishing-touches.mdx @@ -12,6 +12,14 @@ pagination_prev: customization/data-points pagination_next: customization/conclusion --- +:::info + +**This page is part of an iterative guide (where we build onto code from the previous steps).** + +It is recommend that you follow the guide from the [start](intro). However, if you are only interested in the content on this page then take a look at the [full code](#complete-code) at the bottom of page for context of how this code fits into a working example. + +::: + In this section we are going to add a few finishing touches to the chart which are entirely optional. ## Changing the font diff --git a/website/tutorials/customization/intro.mdx b/website/tutorials/customization/intro.mdx index 764bbbed27..1640950530 100644 --- a/website/tutorials/customization/intro.mdx +++ b/website/tutorials/customization/intro.mdx @@ -80,4 +80,8 @@ You can either: - or [open this JSFiddle](https://jsfiddle.net/msilverwood/391hxf6b/) and then edit and run the example within the browser. -At the end of each section will be a link to download the example at that stage of the guide and a full code block. +:::tip + +At the end of each section will be a link to download the example at that stage of the guide, and a full code block. + +::: diff --git a/website/tutorials/customization/price-format.mdx b/website/tutorials/customization/price-format.mdx index 4716748890..e04df62f7b 100644 --- a/website/tutorials/customization/price-format.mdx +++ b/website/tutorials/customization/price-format.mdx @@ -12,6 +12,14 @@ pagination_prev: customization/series pagination_next: customization/price-scale --- +:::info + +**This page is part of an iterative guide (where we build onto code from the previous steps).** + +It is recommend that you follow the guide from the [start](intro). However, if you are only interested in the content on this page then take a look at the [full code](#complete-code) at the bottom of page for context of how this code fits into a working example. + +::: + In this section, we will be replacing the default price formatter function with our implementation. Currently, the prices on the chart are been shown with two decimal places and without a currency symbol. Let's implement a formatter which will format the number correctly based on their current locale and present it as the Euro currency. ## Price Formatter functions diff --git a/website/tutorials/customization/price-scale.mdx b/website/tutorials/customization/price-scale.mdx index e444f86d78..dabd56ce06 100644 --- a/website/tutorials/customization/price-scale.mdx +++ b/website/tutorials/customization/price-scale.mdx @@ -12,6 +12,14 @@ pagination_prev: customization/price-format pagination_next: customization/time-scale --- +:::info + +**This page is part of an iterative guide (where we build onto code from the previous steps).** + +It is recommend that you follow the guide from the [start](intro). However, if you are only interested in the content on this page then take a look at the [full code](#complete-code) at the bottom of page for context of how this code fits into a working example. + +::: + In this section, we are going to adjust the functionality of the vertical price scale. Currently, the price scale will auto-scale to fit the visible data on the chart. You can observe this behaviour by scrolling the chart to the right such that some of the data points are no longer visible. We will disable this feature such that the price scale can only be manually adjusted by the user (by clicking and dragging on the scale). :::tip diff --git a/website/tutorials/customization/second-series.mdx b/website/tutorials/customization/second-series.mdx index ad0163e85a..1b4de1375d 100644 --- a/website/tutorials/customization/second-series.mdx +++ b/website/tutorials/customization/second-series.mdx @@ -12,6 +12,14 @@ pagination_prev: customization/crosshair pagination_next: customization/data-points --- +:::info + +**This page is part of an iterative guide (where we build onto code from the previous steps).** + +It is recommend that you follow the guide from the [start](intro). However, if you are only interested in the content on this page then take a look at the [full code](#complete-code) at the bottom of page for context of how this code fits into a working example. + +::: + In this section, we will be adding an [area series](/docs/series-types#area) to the chart with a subtle vertical gradient. It's purpose is solely for aesthetic reasons (only to make the chart more visually appealing). However, it will teach us a few key points about the differences between different series types and the visual stacking order. ## Preparing the data for the area series diff --git a/website/tutorials/customization/series.mdx b/website/tutorials/customization/series.mdx index f018927202..9dd1f83f0f 100644 --- a/website/tutorials/customization/series.mdx +++ b/website/tutorials/customization/series.mdx @@ -12,6 +12,14 @@ pagination_prev: customization/chart-colors pagination_next: customization/price-format --- +:::info + +**This page is part of an iterative guide (where we build onto code from the previous steps).** + +It is recommend that you follow the guide from the [start](intro). However, if you are only interested in the content on this page then take a look at the [full code](#complete-code) at the bottom of page for context of how this code fits into a working example. + +::: + In this section, we will be customizing the visual styling of the candlestick series. We can add our custom options to the series by using the `applyOptions` method on the ISeriesApi instance for the candlestick series. In other words, we can call the `applyOptions` method on the `mainSeries` variable (which was returned when we evoked `addCandlestickSeries()` earlier). diff --git a/website/tutorials/customization/time-scale.mdx b/website/tutorials/customization/time-scale.mdx index fc1b64b054..6e745a6039 100644 --- a/website/tutorials/customization/time-scale.mdx +++ b/website/tutorials/customization/time-scale.mdx @@ -12,6 +12,14 @@ pagination_prev: customization/price-scale pagination_next: customization/crosshair --- +:::info + +**This page is part of an iterative guide (where we build onto code from the previous steps).** + +It is recommend that you follow the guide from the [start](intro). However, if you are only interested in the content on this page then take a look at the [full code](#complete-code) at the bottom of page for context of how this code fits into a working example. + +::: + In the previous step, we adjusted the vertical price scale, now let us adjust the horizontal time scale. We previously adjusted the border color of this scale and now we are going to adjust the starting 'zoom' level. We will be adjusting the [`barSpacing`](/docs/api/interfaces/TimeScaleOptions#barspacing) option on the time scale which is used when the chart is first rendered to determine the horizontal 'zoom' level. The property sets 'The space between bars in pixels.', where a larger number will result in wider bars and fewer bars visible on the chart. The default value is `6` and we will be increasing it to `10` which will effectively 'zoom in' for the time scale. From e30fbb3dcfbd02b56ad782dffccf87a66cdc917f Mon Sep 17 00:00:00 2001 From: Mark Silverwood Date: Thu, 18 Aug 2022 00:02:16 +0200 Subject: [PATCH 117/188] fix tests for newer version of puppeteer Minor update for puppeteer (16.1.1) breaks coverage and mem leak tests. --- package.json | 2 +- tests/e2e/coverage/coverage.spec.ts | 91 ++++++++--------------- tests/e2e/memleaks/memleaks-test-cases.ts | 18 ++--- 3 files changed, 37 insertions(+), 74 deletions(-) diff --git a/package.json b/package.json index b3bb1b5117..9026a6bf33 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "npm-run-all": "~4.1.5", "pixelmatch": "~5.3.0", "pngjs": "~6.0.0", - "puppeteer": "~16.1.0", + "puppeteer": "~16.1.1", "rimraf": "~3.0.2", "rollup": "~2.77.2", "rollup-plugin-terser": "~7.0.2", diff --git a/tests/e2e/coverage/coverage.spec.ts b/tests/e2e/coverage/coverage.spec.ts index 22998ed042..e3fb15c67a 100644 --- a/tests/e2e/coverage/coverage.spec.ts +++ b/tests/e2e/coverage/coverage.spec.ts @@ -23,71 +23,29 @@ const testStandalonePathEnvKey = 'TEST_STANDALONE_PATH'; const testStandalonePath: string = process.env[testStandalonePathEnvKey] || ''; -interface MouseWheelEventOptions { - type: 'mouseWheel'; - x: number; - y: number; - deltaX: number; - deltaY: number; -} +async function doMouseScrolls(page: Page, element: ElementHandle): Promise { + const boundingBox = await element.boundingBox(); + if (!boundingBox) { + throw new Error('Unable to get boundingBox for element.'); + } -interface InternalPuppeteerClient { - // see https://github.com/ChromeDevTools/devtools-protocol/blob/20413fc82dea0d45a598715970293b4787296673/json/browser_protocol.json#L7822-L7898 - // see https://github.com/puppeteer/puppeteer/issues/4119 - send(event: 'Input.dispatchMouseEvent', options: MouseWheelEventOptions): Promise; -} + // move mouse to center of element + await page.mouse.move( + boundingBox.x + boundingBox.width / 2, + boundingBox.y + boundingBox.height / 2 + ); -async function doMouseScrolls(element: ElementHandle): Promise { - // eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-member-access - const client: InternalPuppeteerClient = (element as any)._client; + await page.mouse.wheel({ deltaX: 10.0 }); - await client.send('Input.dispatchMouseEvent', { - type: 'mouseWheel', - x: 0, - y: 0, - deltaX: 10.0, - deltaY: 0, - }); + await page.mouse.wheel({ deltaY: 10.0 }); - await client.send('Input.dispatchMouseEvent', { - type: 'mouseWheel', - x: 0, - y: 0, - deltaX: 0, - deltaY: 10.0, - }); + await page.mouse.wheel({ deltaX: -10.0 }); - await client.send('Input.dispatchMouseEvent', { - type: 'mouseWheel', - x: 0, - y: 0, - deltaX: -10.0, - deltaY: 0, - }); + await page.mouse.wheel({ deltaY: -10.0 }); - await client.send('Input.dispatchMouseEvent', { - type: 'mouseWheel', - x: 0, - y: 0, - deltaX: 0, - deltaY: -10.0, - }); + await page.mouse.wheel({ deltaX: 10.0, deltaY: 10.0 }); - await client.send('Input.dispatchMouseEvent', { - type: 'mouseWheel', - x: 0, - y: 0, - deltaX: 10.0, - deltaY: 10.0, - }); - - await client.send('Input.dispatchMouseEvent', { - type: 'mouseWheel', - x: 0, - y: 0, - deltaX: -10.0, - deltaY: -10.0, - }); + await page.mouse.wheel({ deltaX: -10.0, deltaY: -10.0 }); } async function doZoomInZoomOut(page: Page): Promise { @@ -131,6 +89,16 @@ async function doHorizontalDrag(page: Page, element: ElementHandle): Promise { + return page.evaluate( + (ms: number) => new Promise( + (resolve: () => void) => setTimeout(resolve, ms) + ), + delay + ); +} + async function doKineticAnimation(page: Page, element: ElementHandle): Promise { const elBox = await element.boundingBox() as BoundingBox; @@ -141,7 +109,7 @@ async function doKineticAnimation(page: Page, element: ElementHandle): Promise { const timeAxis = (await chartContainer.$$('tr:nth-of-type(2) td:nth-of-type(2) div canvas'))[0]; // mouse scroll - await doMouseScrolls(chartContainer); + await doMouseScrolls(page, chartContainer); // outside click await page.mouse.click(chartBox.x + chartBox.width + 20, chartBox.y + chartBox.height + 50, { button: 'left' }); diff --git a/tests/e2e/memleaks/memleaks-test-cases.ts b/tests/e2e/memleaks/memleaks-test-cases.ts index 4a084f199b..5a7a2a481f 100644 --- a/tests/e2e/memleaks/memleaks-test-cases.ts +++ b/tests/e2e/memleaks/memleaks-test-cases.ts @@ -5,7 +5,7 @@ import * as path from 'path'; import { expect } from 'chai'; import { describe, it } from 'mocha'; -import puppeteer, { Browser, Frame, HTTPResponse, JSHandle, launch as launchPuppeteer } from 'puppeteer'; +import puppeteer, { Browser, HTTPResponse, JSHandle, launch as launchPuppeteer, Page } from 'puppeteer'; import { getTestCases } from './helpers/get-test-cases'; @@ -22,10 +22,9 @@ const testStandalonePathEnvKey = 'TEST_STANDALONE_PATH'; const testStandalonePath: string = process.env[testStandalonePathEnvKey] || ''; -async function getReferencesCount(frame: Frame, prototypeReference: JSHandle): Promise { - const context = await frame.executionContext(); - const activeRefsHandle = await context.queryObjects(prototypeReference); - const activeRefsCount = await (await activeRefsHandle?.getProperty('length'))?.jsonValue(); +async function getReferencesCount(page: Page, prototypeReference: JSHandle): Promise { + const activeRefsHandle = await page.queryObjects(prototypeReference); + const activeRefsCount = await (await activeRefsHandle?.getProperty('length'))?.jsonValue(); await activeRefsHandle.dispose(); @@ -90,12 +89,9 @@ describe('Memleaks tests', () => { return Promise.resolve(CanvasRenderingContext2D.prototype); }; - const frame = page.mainFrame(); - const context = await frame.executionContext(); + const prototype = await page.evaluateHandle(getCanvasPrototype); - const prototype = await context.evaluateHandle(getCanvasPrototype); - - const referencesCountBefore = await getReferencesCount(frame, prototype); + const referencesCountBefore = await getReferencesCount(page, prototype); await page.setContent(pageContent, { waitUntil: 'load' }); @@ -118,7 +114,7 @@ describe('Memleaks tests', () => { // So we have to wait to be sure all is clear await promisleep(100); - const referencesCountAfter = await getReferencesCount(frame, prototype); + const referencesCountAfter = await getReferencesCount(page, prototype); expect(referencesCountAfter).to.be.equal(referencesCountBefore, 'There should not be extra references after removing a chart'); }); From b0eeeb2901679c35017e9102f69f4d1d929f03cc Mon Sep 17 00:00:00 2001 From: Mark Silverwood Date: Tue, 16 Aug 2022 19:16:30 +0200 Subject: [PATCH 118/188] add conditional warning banner for tutorials Added a conditional warning admonition message to tutorials index page. Only displayed if not viewing the docs for the latest version. --- .../VersionWarningAdmonition/index.tsx | 74 +++++++++++++++++++ website/tutorials/index.mdx | 8 +- 2 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 website/src/components/VersionWarningAdmonition/index.tsx diff --git a/website/src/components/VersionWarningAdmonition/index.tsx b/website/src/components/VersionWarningAdmonition/index.tsx new file mode 100644 index 0000000000..34aef49245 --- /dev/null +++ b/website/src/components/VersionWarningAdmonition/index.tsx @@ -0,0 +1,74 @@ +import { type PropVersionMetadata } from '@docusaurus/plugin-content-docs'; +import { useDocsPreferredVersion } from '@docusaurus/theme-common'; +import Admonition from '@theme/Admonition'; +import React from 'react'; + +import versions from '../../../versions.json'; + +interface Props { + /** Message to show if not the latest released version */ + notCurrent?: string; + /** Message to show if the latest released version */ + isCurrent?: string; + type: 'note' | 'tip' | 'danger' | 'info' | 'caution'; + title?: string; + /** Display a preformatted message about the versions at the bottom of the warning */ + displayVersionMessage?: boolean; +} + +// Extend the type returned by useDocsPreferredVersion to include +// information populated by docusaurus plugins +type UseDocsPreferredVersion = Omit< + ReturnType, + 'preferredVersion' +> & { + preferredVersion: (PropVersionMetadata & { name: string }) | null; +}; + +export default function VersionWarningAdmonition({ + notCurrent, + isCurrent, + type, + title, + displayVersionMessage, +}: Props): JSX.Element { + const { preferredVersion, savePreferredVersionName } = + useDocsPreferredVersion() as UseDocsPreferredVersion; + const notCurrentWarning = Boolean(notCurrent && !preferredVersion?.isLast); + const isCurrentWarning = Boolean(isCurrent && preferredVersion?.isLast); + + const currentVersion = versions && versions.length > 0 ? versions[0] : ''; + const docVersion = preferredVersion?.label ?? preferredVersion?.name ?? ''; + + if ((!notCurrentWarning && !isCurrentWarning) || !docVersion) { + return <>; + } + + let message = (notCurrentWarning ? notCurrent : isCurrent) ?? ''; + message = message + .replace(/DOC_VERSION/g, docVersion) + .replace(/CURRENT_VERSION/g, currentVersion); + + return ( +
+ +

{message}

+ {displayVersionMessage && ( +

+ You are currently viewing the documentation for the version tagged:{' '} + {docVersion}.
+ + savePreferredVersionName(currentVersion)} + > + Switch to the latest published version + + {' '} + ({currentVersion}) +

+ )} +
+
+ ); +} diff --git a/website/tutorials/index.mdx b/website/tutorials/index.mdx index f448bf2ccc..3c44c4946e 100644 --- a/website/tutorials/index.mdx +++ b/website/tutorials/index.mdx @@ -9,9 +9,11 @@ import ReactLogo from "@site/src/img/react.svg"; # Tutorials -:::caution -These tutorials are for the latest published version of Lightweight Charts. -::: +import VersionWarningAdmonition from '@site/src/components/VersionWarningAdmonition' + + + + ## Guides From 4959aaa5debc1f69361b12b3a20a74bb961295b2 Mon Sep 17 00:00:00 2001 From: Mark Silverwood Date: Tue, 16 Aug 2022 20:45:09 +0200 Subject: [PATCH 119/188] specify file names for downloads Specify file names for customization tutorial downloads. --- website/tutorials/customization/chart-colors.mdx | 2 +- website/tutorials/customization/creating-a-chart.mdx | 2 +- website/tutorials/customization/crosshair.mdx | 2 +- website/tutorials/customization/data-points.mdx | 2 +- website/tutorials/customization/finishing-touches.mdx | 2 +- website/tutorials/customization/intro.mdx | 2 +- website/tutorials/customization/price-format.mdx | 2 +- website/tutorials/customization/price-scale.mdx | 2 +- website/tutorials/customization/second-series.mdx | 2 +- website/tutorials/customization/series.mdx | 2 +- website/tutorials/customization/time-scale.mdx | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/website/tutorials/customization/chart-colors.mdx b/website/tutorials/customization/chart-colors.mdx index 96f72e7514..e2af081a32 100644 --- a/website/tutorials/customization/chart-colors.mdx +++ b/website/tutorials/customization/chart-colors.mdx @@ -139,7 +139,7 @@ In the next step, we will change the colors for the candlestick series. ## Download -You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. +You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. ## Complete code diff --git a/website/tutorials/customization/creating-a-chart.mdx b/website/tutorials/customization/creating-a-chart.mdx index cd565c5818..8b8c2a17d8 100644 --- a/website/tutorials/customization/creating-a-chart.mdx +++ b/website/tutorials/customization/creating-a-chart.mdx @@ -109,7 +109,7 @@ The rest of the tutorial will focus on how to customise this chart to further ma ## Download -You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. +You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. ## Complete code diff --git a/website/tutorials/customization/crosshair.mdx b/website/tutorials/customization/crosshair.mdx index e4f130bfd2..f8a32f3e71 100644 --- a/website/tutorials/customization/crosshair.mdx +++ b/website/tutorials/customization/crosshair.mdx @@ -79,7 +79,7 @@ In the next step, we will be adding a second series to the chart such that we ca ## Download -You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. +You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. ## Complete code diff --git a/website/tutorials/customization/data-points.mdx b/website/tutorials/customization/data-points.mdx index c014a9f46c..56d03af3fe 100644 --- a/website/tutorials/customization/data-points.mdx +++ b/website/tutorials/customization/data-points.mdx @@ -64,7 +64,7 @@ In the next step, we will add a few finishing touches to the chart. ## Download -You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. +You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. ## Complete code diff --git a/website/tutorials/customization/finishing-touches.mdx b/website/tutorials/customization/finishing-touches.mdx index 3e4679aaa3..d9cd4830d9 100644 --- a/website/tutorials/customization/finishing-touches.mdx +++ b/website/tutorials/customization/finishing-touches.mdx @@ -136,7 +136,7 @@ window.addEventListener('resize', () => { ## Download -You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. +You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. ## Complete code diff --git a/website/tutorials/customization/intro.mdx b/website/tutorials/customization/intro.mdx index 1640950530..4f57d6084c 100644 --- a/website/tutorials/customization/intro.mdx +++ b/website/tutorials/customization/intro.mdx @@ -74,7 +74,7 @@ Provided below is the 'starting point' file for the guide which is a simple HTML You can either: -- +- Download the file and then edit and run the example on your computer, - or [open this JSFiddle](https://jsfiddle.net/msilverwood/391hxf6b/) and then edit and run the example within the browser. diff --git a/website/tutorials/customization/price-format.mdx b/website/tutorials/customization/price-format.mdx index e04df62f7b..dd89ad6052 100644 --- a/website/tutorials/customization/price-format.mdx +++ b/website/tutorials/customization/price-format.mdx @@ -83,7 +83,7 @@ In the next step, we will be making some adjustments to the functionality of the ## Download -You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. +You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. ## Complete code diff --git a/website/tutorials/customization/price-scale.mdx b/website/tutorials/customization/price-scale.mdx index dabd56ce06..3663f3b38c 100644 --- a/website/tutorials/customization/price-scale.mdx +++ b/website/tutorials/customization/price-scale.mdx @@ -74,7 +74,7 @@ In the next step, we will be adjusting the settings on the horizontal time scale ## Download -You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. +You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. ## Complete code diff --git a/website/tutorials/customization/second-series.mdx b/website/tutorials/customization/second-series.mdx index 1b4de1375d..bf08cc72b2 100644 --- a/website/tutorials/customization/second-series.mdx +++ b/website/tutorials/customization/second-series.mdx @@ -99,7 +99,7 @@ In the next step, we will look at how to adjust the colour of individual candles ## Download -You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. +You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. ## Complete code diff --git a/website/tutorials/customization/series.mdx b/website/tutorials/customization/series.mdx index 9dd1f83f0f..b7257ad372 100644 --- a/website/tutorials/customization/series.mdx +++ b/website/tutorials/customization/series.mdx @@ -65,7 +65,7 @@ In the next step, we will set a price formatter so we can customize the formatti ## Download -You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. +You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. ## Complete code diff --git a/website/tutorials/customization/time-scale.mdx b/website/tutorials/customization/time-scale.mdx index 6e745a6039..d294260032 100644 --- a/website/tutorials/customization/time-scale.mdx +++ b/website/tutorials/customization/time-scale.mdx @@ -76,7 +76,7 @@ In the next step, we will be adjusting the visual style and behaviour of the cro ## Download -You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. +You can download the HTML file for example at this stage here in case you've encountered a problem or would like to start the next step from this point. ## Complete code From 1b07b53ee87ca62668305285380f34c4cbfd6997 Mon Sep 17 00:00:00 2001 From: Mark Silverwood Date: Thu, 18 Aug 2022 14:52:04 +0200 Subject: [PATCH 120/188] move window resizing section earlier in tut Move the window resizing section of the tutorial to the first step such that all the charts in the tutorial will be responsive to window resizes. --- .../tutorials/customization/assets/step1.html | 8 +++++++ .../tutorials/customization/assets/step2.html | 8 +++++++ .../tutorials/customization/assets/step3.html | 8 +++++++ .../tutorials/customization/assets/step4.html | 8 +++++++ .../tutorials/customization/assets/step5.html | 8 +++++++ .../tutorials/customization/assets/step6.html | 8 +++++++ .../tutorials/customization/assets/step7.html | 8 +++++++ .../tutorials/customization/assets/step8.html | 8 +++++++ .../tutorials/customization/assets/step9.html | 8 +++++++ .../customization/creating-a-chart.mdx | 23 +++++++++++++++++++ .../customization/finishing-touches.mdx | 20 ---------------- 11 files changed, 95 insertions(+), 20 deletions(-) diff --git a/website/tutorials/customization/assets/step1.html b/website/tutorials/customization/assets/step1.html index 4d6aedbce3..326fa9e76b 100644 --- a/website/tutorials/customization/assets/step1.html +++ b/website/tutorials/customization/assets/step1.html @@ -1087,6 +1087,14 @@ const mainSeries = chart.addCandlestickSeries(); // Set the data for the Main Series mainSeries.setData(candleStickData); + + // Adding a window resize event handler to resize the chart when + // the window size changes. + // Note: for more advanced examples (when the chart doesn't fill the entire window) + // you may need to use ResizeObserver -> https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver + window.addEventListener("resize", () => { + chart.resize(window.innerWidth, window.innerHeight); + }); diff --git a/website/tutorials/customization/assets/step2.html b/website/tutorials/customization/assets/step2.html index 017476b6c3..dfefb69582 100644 --- a/website/tutorials/customization/assets/step2.html +++ b/website/tutorials/customization/assets/step2.html @@ -1109,6 +1109,14 @@ const mainSeries = chart.addCandlestickSeries(); // Set the data for the Main Series mainSeries.setData(candleStickData); + + // Adding a window resize event handler to resize the chart when + // the window size changes. + // Note: for more advanced examples (when the chart doesn't fill the entire window) + // you may need to use ResizeObserver -> https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver + window.addEventListener("resize", () => { + chart.resize(window.innerWidth, window.innerHeight); + }); diff --git a/website/tutorials/customization/assets/step3.html b/website/tutorials/customization/assets/step3.html index 608d1cb4c7..850f929ed9 100644 --- a/website/tutorials/customization/assets/step3.html +++ b/website/tutorials/customization/assets/step3.html @@ -1118,6 +1118,14 @@ downColor: "rgb(225, 50, 85)", borderVisible: false, }); + + // Adding a window resize event handler to resize the chart when + // the window size changes. + // Note: for more advanced examples (when the chart doesn't fill the entire window) + // you may need to use ResizeObserver -> https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver + window.addEventListener("resize", () => { + chart.resize(window.innerWidth, window.innerHeight); + }); diff --git a/website/tutorials/customization/assets/step4.html b/website/tutorials/customization/assets/step4.html index 76e0590c6f..de30e2c4be 100644 --- a/website/tutorials/customization/assets/step4.html +++ b/website/tutorials/customization/assets/step4.html @@ -1133,6 +1133,14 @@ downColor: "rgb(225, 50, 85)", borderVisible: false, }); + + // Adding a window resize event handler to resize the chart when + // the window size changes. + // Note: for more advanced examples (when the chart doesn't fill the entire window) + // you may need to use ResizeObserver -> https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver + window.addEventListener("resize", () => { + chart.resize(window.innerWidth, window.innerHeight); + }); diff --git a/website/tutorials/customization/assets/step5.html b/website/tutorials/customization/assets/step5.html index 36fc745f2b..133f2bed48 100644 --- a/website/tutorials/customization/assets/step5.html +++ b/website/tutorials/customization/assets/step5.html @@ -1142,6 +1142,14 @@ bottom: 0.2, }, }); + + // Adding a window resize event handler to resize the chart when + // the window size changes. + // Note: for more advanced examples (when the chart doesn't fill the entire window) + // you may need to use ResizeObserver -> https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver + window.addEventListener("resize", () => { + chart.resize(window.innerWidth, window.innerHeight); + }); diff --git a/website/tutorials/customization/assets/step6.html b/website/tutorials/customization/assets/step6.html index 300fb68f0b..687f03f7b5 100644 --- a/website/tutorials/customization/assets/step6.html +++ b/website/tutorials/customization/assets/step6.html @@ -1147,6 +1147,14 @@ bottom: 0.2, }, }); + + // Adding a window resize event handler to resize the chart when + // the window size changes. + // Note: for more advanced examples (when the chart doesn't fill the entire window) + // you may need to use ResizeObserver -> https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver + window.addEventListener("resize", () => { + chart.resize(window.innerWidth, window.innerHeight); + }); diff --git a/website/tutorials/customization/assets/step7.html b/website/tutorials/customization/assets/step7.html index 39e6db379b..e7a0dbb811 100644 --- a/website/tutorials/customization/assets/step7.html +++ b/website/tutorials/customization/assets/step7.html @@ -1170,6 +1170,14 @@ bottom: 0.2, }, }); + + // Adding a window resize event handler to resize the chart when + // the window size changes. + // Note: for more advanced examples (when the chart doesn't fill the entire window) + // you may need to use ResizeObserver -> https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver + window.addEventListener("resize", () => { + chart.resize(window.innerWidth, window.innerHeight); + }); diff --git a/website/tutorials/customization/assets/step8.html b/website/tutorials/customization/assets/step8.html index 88bb4b42b3..2d1db5caae 100644 --- a/website/tutorials/customization/assets/step8.html +++ b/website/tutorials/customization/assets/step8.html @@ -1189,6 +1189,14 @@ bottom: 0.2, }, }); + + // Adding a window resize event handler to resize the chart when + // the window size changes. + // Note: for more advanced examples (when the chart doesn't fill the entire window) + // you may need to use ResizeObserver -> https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver + window.addEventListener("resize", () => { + chart.resize(window.innerWidth, window.innerHeight); + }); diff --git a/website/tutorials/customization/assets/step9.html b/website/tutorials/customization/assets/step9.html index c068ebe8b1..d91a6331a5 100644 --- a/website/tutorials/customization/assets/step9.html +++ b/website/tutorials/customization/assets/step9.html @@ -1196,6 +1196,14 @@ bottom: 0.2, }, }); + + // Adding a window resize event handler to resize the chart when + // the window size changes. + // Note: for more advanced examples (when the chart doesn't fill the entire window) + // you may need to use ResizeObserver -> https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver + window.addEventListener("resize", () => { + chart.resize(window.innerWidth, window.innerHeight); + }); diff --git a/website/tutorials/customization/creating-a-chart.mdx b/website/tutorials/customization/creating-a-chart.mdx index 8b8c2a17d8..747af7cf2e 100644 --- a/website/tutorials/customization/creating-a-chart.mdx +++ b/website/tutorials/customization/creating-a-chart.mdx @@ -89,6 +89,29 @@ mainSeries.setData(candleStickData); // highlight-end ``` +## Automatically resizing the chart when the window is resized (Optional) + +This is an optional step which will enable automatically resizing the chart whenever +the browser window is resized. + +:::caution + +This code snippet will only work correctly if you want the chart to completely fill the window / iframe. + +::: + +Adding the following code snippet to the end of the script tag to enable automatic resizing. + +```js +// Adding a window resize event handler to resize the chart when +// the window size changes. +// Note: for more advanced examples (when the chart doesn't fill the entire window) +// you may need to use ResizeObserver -> https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver +window.addEventListener('resize', () => { + chart.resize(window.innerWidth, window.innerHeight); +}); +``` + ## Result At this point we should have a chart like this: diff --git a/website/tutorials/customization/finishing-touches.mdx b/website/tutorials/customization/finishing-touches.mdx index d9cd4830d9..0cb2229bc3 100644 --- a/website/tutorials/customization/finishing-touches.mdx +++ b/website/tutorials/customization/finishing-touches.mdx @@ -102,26 +102,6 @@ and then add the following `
` element within the container element used for
``` -## Automatically resizing the chart when the window is resized - -Adding the following code snippet to the end of the script tag will make the chart resize automatically to fit the browser window whenever the window is resized. - -:::caution - -This will only work correctly if you want the chart to completely fill the window. - -::: - -```js -// Adding a window resize event handler to resize the chart when -// the window size changes. -// Note: for more advanced examples (when the chart doesn't fill the entire window) -// you may need to use ResizeObserver -> https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver -window.addEventListener('resize', () => { - chart.resize(window.innerWidth, window.innerHeight); -}); -``` - ## Result 🎉 Congrats! At this point you should have the final chart which looks like this: From a40e9d20ceb98764e42b2627658dffb38ca7ac68 Mon Sep 17 00:00:00 2001 From: Mark Silverwood Date: Thu, 18 Aug 2022 19:11:40 +0200 Subject: [PATCH 121/188] move iterative guide warning into partial file Moved the iterative guide warning message shared amongst most of the customization tutorial into a separate partial file. --- .../customization/_iterative-guide-warning-partial.mdx | 7 +++++++ website/tutorials/customization/chart-colors.mdx | 8 ++------ website/tutorials/customization/crosshair.mdx | 8 ++------ website/tutorials/customization/data-points.mdx | 8 ++------ website/tutorials/customization/finishing-touches.mdx | 8 ++------ website/tutorials/customization/price-format.mdx | 8 ++------ website/tutorials/customization/price-scale.mdx | 8 ++------ website/tutorials/customization/second-series.mdx | 8 ++------ website/tutorials/customization/series.mdx | 8 ++------ website/tutorials/customization/time-scale.mdx | 8 ++------ 10 files changed, 25 insertions(+), 54 deletions(-) create mode 100644 website/tutorials/customization/_iterative-guide-warning-partial.mdx diff --git a/website/tutorials/customization/_iterative-guide-warning-partial.mdx b/website/tutorials/customization/_iterative-guide-warning-partial.mdx new file mode 100644 index 0000000000..97100bdc7a --- /dev/null +++ b/website/tutorials/customization/_iterative-guide-warning-partial.mdx @@ -0,0 +1,7 @@ +:::info + +**This page is part of an iterative guide (where we build onto code from the previous steps).** + +It is recommend that you follow the guide from the [start](intro). However, if you are only interested in the content on this page then take a look at the [full code](#complete-code) at the bottom of page for context of how this code fits into a working example. + +::: diff --git a/website/tutorials/customization/chart-colors.mdx b/website/tutorials/customization/chart-colors.mdx index e2af081a32..b41af02ac8 100644 --- a/website/tutorials/customization/chart-colors.mdx +++ b/website/tutorials/customization/chart-colors.mdx @@ -12,13 +12,9 @@ pagination_prev: customization/creating-a-chart pagination_next: customization/series --- -:::info +import IterativeGuideWarning from './_iterative-guide-warning-partial.mdx'; -**This page is part of an iterative guide (where we build onto code from the previous steps).** - -It is recommend that you follow the guide from the [start](intro). However, if you are only interested in the content on this page then take a look at the [full code](#complete-code) at the bottom of page for context of how this code fits into a working example. - -::: + In this section, we will be customizing the appearance of the chart 'backdrop'. We will be adjusting the colors to better suit a dark theme. diff --git a/website/tutorials/customization/crosshair.mdx b/website/tutorials/customization/crosshair.mdx index f8a32f3e71..49e944a9fc 100644 --- a/website/tutorials/customization/crosshair.mdx +++ b/website/tutorials/customization/crosshair.mdx @@ -12,13 +12,9 @@ pagination_prev: customization/time-scale pagination_next: customization/second-series --- -:::info +import IterativeGuideWarning from './_iterative-guide-warning-partial.mdx'; -**This page is part of an iterative guide (where we build onto code from the previous steps).** - -It is recommend that you follow the guide from the [start](intro). However, if you are only interested in the content on this page then take a look at the [full code](#complete-code) at the bottom of page for context of how this code fits into a working example. - -::: + In this section, we will be customizing the appearance of the crosshair and it's functionality. diff --git a/website/tutorials/customization/data-points.mdx b/website/tutorials/customization/data-points.mdx index 56d03af3fe..590822c4ad 100644 --- a/website/tutorials/customization/data-points.mdx +++ b/website/tutorials/customization/data-points.mdx @@ -12,13 +12,9 @@ pagination_prev: customization/second-series pagination_next: customization/finishing-touches --- -:::info +import IterativeGuideWarning from './_iterative-guide-warning-partial.mdx'; -**This page is part of an iterative guide (where we build onto code from the previous steps).** - -It is recommend that you follow the guide from the [start](intro). However, if you are only interested in the content on this page then take a look at the [full code](#complete-code) at the bottom of page for context of how this code fits into a working example. - -::: + In this section, we will modify the candlestick data such that we can individually set the color for each candlestick. diff --git a/website/tutorials/customization/finishing-touches.mdx b/website/tutorials/customization/finishing-touches.mdx index 0cb2229bc3..d1d3d61274 100644 --- a/website/tutorials/customization/finishing-touches.mdx +++ b/website/tutorials/customization/finishing-touches.mdx @@ -12,13 +12,9 @@ pagination_prev: customization/data-points pagination_next: customization/conclusion --- -:::info +import IterativeGuideWarning from './_iterative-guide-warning-partial.mdx'; -**This page is part of an iterative guide (where we build onto code from the previous steps).** - -It is recommend that you follow the guide from the [start](intro). However, if you are only interested in the content on this page then take a look at the [full code](#complete-code) at the bottom of page for context of how this code fits into a working example. - -::: + In this section we are going to add a few finishing touches to the chart which are entirely optional. diff --git a/website/tutorials/customization/price-format.mdx b/website/tutorials/customization/price-format.mdx index dd89ad6052..7a0520517e 100644 --- a/website/tutorials/customization/price-format.mdx +++ b/website/tutorials/customization/price-format.mdx @@ -12,13 +12,9 @@ pagination_prev: customization/series pagination_next: customization/price-scale --- -:::info +import IterativeGuideWarning from './_iterative-guide-warning-partial.mdx'; -**This page is part of an iterative guide (where we build onto code from the previous steps).** - -It is recommend that you follow the guide from the [start](intro). However, if you are only interested in the content on this page then take a look at the [full code](#complete-code) at the bottom of page for context of how this code fits into a working example. - -::: + In this section, we will be replacing the default price formatter function with our implementation. Currently, the prices on the chart are been shown with two decimal places and without a currency symbol. Let's implement a formatter which will format the number correctly based on their current locale and present it as the Euro currency. diff --git a/website/tutorials/customization/price-scale.mdx b/website/tutorials/customization/price-scale.mdx index 3663f3b38c..7493905c24 100644 --- a/website/tutorials/customization/price-scale.mdx +++ b/website/tutorials/customization/price-scale.mdx @@ -12,13 +12,9 @@ pagination_prev: customization/price-format pagination_next: customization/time-scale --- -:::info +import IterativeGuideWarning from './_iterative-guide-warning-partial.mdx'; -**This page is part of an iterative guide (where we build onto code from the previous steps).** - -It is recommend that you follow the guide from the [start](intro). However, if you are only interested in the content on this page then take a look at the [full code](#complete-code) at the bottom of page for context of how this code fits into a working example. - -::: + In this section, we are going to adjust the functionality of the vertical price scale. Currently, the price scale will auto-scale to fit the visible data on the chart. You can observe this behaviour by scrolling the chart to the right such that some of the data points are no longer visible. We will disable this feature such that the price scale can only be manually adjusted by the user (by clicking and dragging on the scale). diff --git a/website/tutorials/customization/second-series.mdx b/website/tutorials/customization/second-series.mdx index bf08cc72b2..dd34d45893 100644 --- a/website/tutorials/customization/second-series.mdx +++ b/website/tutorials/customization/second-series.mdx @@ -12,13 +12,9 @@ pagination_prev: customization/crosshair pagination_next: customization/data-points --- -:::info +import IterativeGuideWarning from './_iterative-guide-warning-partial.mdx'; -**This page is part of an iterative guide (where we build onto code from the previous steps).** - -It is recommend that you follow the guide from the [start](intro). However, if you are only interested in the content on this page then take a look at the [full code](#complete-code) at the bottom of page for context of how this code fits into a working example. - -::: + In this section, we will be adding an [area series](/docs/series-types#area) to the chart with a subtle vertical gradient. It's purpose is solely for aesthetic reasons (only to make the chart more visually appealing). However, it will teach us a few key points about the differences between different series types and the visual stacking order. diff --git a/website/tutorials/customization/series.mdx b/website/tutorials/customization/series.mdx index b7257ad372..10b67655be 100644 --- a/website/tutorials/customization/series.mdx +++ b/website/tutorials/customization/series.mdx @@ -12,13 +12,9 @@ pagination_prev: customization/chart-colors pagination_next: customization/price-format --- -:::info +import IterativeGuideWarning from './_iterative-guide-warning-partial.mdx'; -**This page is part of an iterative guide (where we build onto code from the previous steps).** - -It is recommend that you follow the guide from the [start](intro). However, if you are only interested in the content on this page then take a look at the [full code](#complete-code) at the bottom of page for context of how this code fits into a working example. - -::: + In this section, we will be customizing the visual styling of the candlestick series. diff --git a/website/tutorials/customization/time-scale.mdx b/website/tutorials/customization/time-scale.mdx index d294260032..9496d9b215 100644 --- a/website/tutorials/customization/time-scale.mdx +++ b/website/tutorials/customization/time-scale.mdx @@ -12,13 +12,9 @@ pagination_prev: customization/price-scale pagination_next: customization/crosshair --- -:::info +import IterativeGuideWarning from './_iterative-guide-warning-partial.mdx'; -**This page is part of an iterative guide (where we build onto code from the previous steps).** - -It is recommend that you follow the guide from the [start](intro). However, if you are only interested in the content on this page then take a look at the [full code](#complete-code) at the bottom of page for context of how this code fits into a working example. - -::: + In the previous step, we adjusted the vertical price scale, now let us adjust the horizontal time scale. We previously adjusted the border color of this scale and now we are going to adjust the starting 'zoom' level. From f5331ade5ef119c96a7ff98457c2a488a8a581ca Mon Sep 17 00:00:00 2001 From: Mark Silverwood Date: Thu, 18 Aug 2022 19:14:43 +0200 Subject: [PATCH 122/188] further enhanced code block component Added the ability to hide and reveal sections of code within the code block. Useful for code examples with large sections of data. Also enables copied code to contain licence headers if needed. --- website/docusaurus.config.js | 14 +++ .../theme/CodeBlock/hidden-lines-styles.css | 88 +++++++++++++++++++ .../theme/CodeBlock/index.jsx | 16 +++- .../theme/CodeBlock/use-id.ts | 14 +++ website/src/css/custom.css | 22 +++-- 5 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 website/plugins/enhanced-codeblock/theme/CodeBlock/hidden-lines-styles.css create mode 100644 website/plugins/enhanced-codeblock/theme/CodeBlock/use-id.ts diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index f8b7af71b7..dd0fabcb5a 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -311,6 +311,20 @@ async function getConfig() { block: { start: 'highlight-fade-start', end: 'highlight-fade-end' }, line: 'highlight-fade', }, + { + // Hides code lines but can be reveal using toggle and css + className: 'code-block-hide-line', + block: { start: 'hide-start', end: 'hide-end' }, + line: 'hide-line', + }, + { + // Hides code lines and can't be reveal using toggle and css. + // Will still be included in copied code. + // Useful for type comments and header notices. + className: 'code-block-remove-line', + block: { start: 'remove-start', end: 'remove-end' }, + line: 'remove-line', + }, ], }, algolia: { diff --git a/website/plugins/enhanced-codeblock/theme/CodeBlock/hidden-lines-styles.css b/website/plugins/enhanced-codeblock/theme/CodeBlock/hidden-lines-styles.css new file mode 100644 index 0000000000..5df51a7a10 --- /dev/null +++ b/website/plugins/enhanced-codeblock/theme/CodeBlock/hidden-lines-styles.css @@ -0,0 +1,88 @@ +html { + --checkbox-color: var(--color-tv-blue-400); + --checkbox-label-hover-color: var(--color-tv-blue-500); + --checkbox-border-hover-color: var(--color-tv-blue-500); +} + +html[data-theme=dark] { + --checkbox-color: var(--color-tv-blue-600); + --checkbox-label-hover-color: var(--color-tv-blue-200); + --checkbox-border-hover-color: var(--color-tv-blue-200); +} + +input[type='checkbox'] { + vertical-align: middle; + margin-right: 0.4rem; + accent-color: var(--checkbox-color); + margin-bottom: 0.4rem; + appearance: none; + border: 2px solid grey; + border-radius: 4px; + height: 1.5rem; + width: 1.5rem; + cursor: pointer; + overflow: hidden; +} + +input[type='checkbox']:hover { + border-color: var(--checkbox-border-hover-color); +} + +input[type='checkbox']:checked { + background-color: var(--checkbox-color); + border-color: var(--checkbox-color); +} + +input[type='checkbox']::after { + position: relative; + content: ''; + width: 1.5rem; + height: 1.5rem; + display: inline-block; + background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+DQo8c3ZnIHdpZHRoPSIxMiIgaGVpZ2h0PSI5IiB2aWV3Qm94PSIwIDAgMTIgOSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCiAgPHBhdGggZD0iTTQuNTc1IDguOTc3cy0uNDA0LS4wMDctLjUzNi0uMTY1TC4wNTcgNS42NGwuODI5LTEuMjI3TDQuNDcgNy4yNjggMTAuOTIxLjA4NmwuOTIzIDEuMTAzLTYuODYzIDcuNjRjLS4xMzQtLjAwMy0uNDA2LjE0OC0uNDA2LjE0OHoiIGZpbGw9IiNGRkYiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPg0KPC9zdmc+) 40% 40% no-repeat; + transform: scale(1); + transition: transform 0.5s; + opacity: 0; +} + +input[type='checkbox']:checked::after { + transform: scale(1.5); + opacity: 1; + transition: transform 0.5s, opacity 0.2s; +} + +.toggle-label { + cursor: pointer; +} + +.toggle-label:hover { + color: var(--checkbox-label-hover-color); +} + +input.toggle-hidden-lines:checked ~ div.theme-code-block .code-block-hide-line { + display: block; + user-select: all; +} + +input.toggle-hidden-lines:not(:checked) + ~ div.theme-code-block + .token-line:not(.code-block-hide-line) + + .token-line.code-block-hide-line { + visibility: hidden; + display: block; + color: transparent; +} + +input.toggle-hidden-lines:not(:checked) + ~ div.theme-code-block + .token-line:not(.code-block-hide-line) + + .token-line.code-block-hide-line::before { + content: "... hidden lines ..."; + visibility: visible; + position: absolute; + background-color: var(--hidden-lines-warning-bg, rgba(0, 0, 0, 0.1)); + text-align: left; + padding: 0 var(--ifm-pre-padding); + width: 100%; + border-radius: 0.2rem; +} \ No newline at end of file diff --git a/website/plugins/enhanced-codeblock/theme/CodeBlock/index.jsx b/website/plugins/enhanced-codeblock/theme/CodeBlock/index.jsx index e077e9968d..a1a61b11d1 100644 --- a/website/plugins/enhanced-codeblock/theme/CodeBlock/index.jsx +++ b/website/plugins/enhanced-codeblock/theme/CodeBlock/index.jsx @@ -6,8 +6,10 @@ import { useColorMode } from '@docusaurus/theme-common'; import { Chart } from './chart'; import styles from './styles.module.css'; +import './hidden-lines-styles.css'; import { themeColors } from '../../../../theme-colors'; +import { useId } from './use-id'; const variableNames = Object.keys(themeColors.DARK); @@ -23,20 +25,28 @@ export function replaceThemeConstantStrings(originalString, isDarkTheme) { } const EnhancedCodeBlock = props => { - const { chart, replaceThemeConstants, ...rest } = props; + const { chart, replaceThemeConstants, hideableCode, ...rest } = props; let { children } = props; const { colorMode } = useColorMode(); const isDarkTheme = colorMode === 'dark'; + const uniqueId = useId(); if (replaceThemeConstants && typeof children === 'string') { children = replaceThemeConstantStrings(children, isDarkTheme); } - if (chart) { + if (chart || hideableCode) { return ( <> + {hideableCode && <> + + } {children} -  
}>{() => } + {chart &&  }>{() => }} ); } diff --git a/website/plugins/enhanced-codeblock/theme/CodeBlock/use-id.ts b/website/plugins/enhanced-codeblock/theme/CodeBlock/use-id.ts new file mode 100644 index 0000000000..63e9431324 --- /dev/null +++ b/website/plugins/enhanced-codeblock/theme/CodeBlock/use-id.ts @@ -0,0 +1,14 @@ +import { useEffect, useState } from 'react'; + +const randomId = () => `${Math.random().toString(36).slice(2, 11)}`; + +function useClientId(): string { + const [uuid, setUuid] = useState(''); + useEffect(() => setUuid(randomId()), []); + + return uuid; +} + +export function useId(staticId?: string): string { + return typeof staticId === 'string' ? staticId : useClientId(); +} diff --git a/website/src/css/custom.css b/website/src/css/custom.css index b31384cc5a..f87986788a 100644 --- a/website/src/css/custom.css +++ b/website/src/css/custom.css @@ -75,7 +75,8 @@ --navbar-logo-url-desktop-laptop-tablet: url('../../static/img/navbar-logo-desktop-laptop-tablet-light.svg'); --navbar-logo-url-mobile: url('../../static/img/navbar-logo-mobile-light.svg'); - --docusaurus-highlighted-code-line-bg: #2962FF22; /* --color-tv-blue-500 with opacity */ + --docusaurus-highlighted-code-line-bg: #2962ff15; /* --color-tv-blue-500 with opacity */ + --hidden-lines-warning-bg: rgba(0, 0, 0, 0.1); } html[data-theme=dark] { @@ -106,6 +107,9 @@ html[data-theme=dark] { --navbar-logo-url-desktop-laptop-tablet: url('../../static/img/navbar-logo-desktop-laptop-tablet-dark.svg'); --navbar-logo-url-mobile: url('../../static/img/navbar-logo-mobile-dark.svg'); + + --docusaurus-highlighted-code-line-bg: #2962ff20; + --hidden-lines-warning-bg: rgba(0, 0, 0, 0.2); } html[data-theme=light] .DocSearch { @@ -122,17 +126,25 @@ html[data-theme=dark] .DocSearch { display: block; margin: 0 calc(-1 * var(--ifm-pre-padding)); padding: 0 var(--ifm-pre-padding); - border-left: 3px solid #2962FFBB; + border-left: 3px solid #2962ffbb; } .code-block-fade-line { - filter: opacity(0.8) saturate(0.2); display: block; margin: 0 calc(-1 * var(--ifm-pre-padding)); padding: 0 var(--ifm-pre-padding); user-select: none; } +.code-block-remove-line, .code-block-hide-line { + display: none; + user-select: none; +} + +.code-block-hide-line, .code-block-fade-line { + filter: opacity(0.8) saturate(0.2); +} + .navbar { box-shadow: none; border-bottom: 1px solid var(--navbar-border-color); @@ -199,7 +211,7 @@ html[data-theme=dark] .DocSearch { .footer__copyright { padding-top: 24px; - color: var(--footer-copyright-color) + color: var(--footer-copyright-color); } @media (min-width: 767.5px) { @@ -236,6 +248,6 @@ html[data-theme=dark] .DocSearch { .standalone-iframe { height: 250px; - width:100%; + width: 100%; border-radius: var(--ifm-code-border-radius); } From fd59cf227f09348ad6606eb55979d2829e123c1a Mon Sep 17 00:00:00 2001 From: Mark Silverwood Date: Thu, 18 Aug 2022 19:16:58 +0200 Subject: [PATCH 123/188] add partial rgb string to theme-colors Add partial rgb strings to the theme-colors. Useful for when examples need a custom alpha value set. Example: `rgba(${CHART_BACKGROUND_RGB_COLOR}, 0.5)` --- website/.eslintrc.js | 1 + website/theme-colors.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/website/.eslintrc.js b/website/.eslintrc.js index ed0c7debf4..f05e4a42ea 100644 --- a/website/.eslintrc.js +++ b/website/.eslintrc.js @@ -1,6 +1,7 @@ module.exports = { globals: { CHART_BACKGROUND_COLOR: true, + CHART_BACKGROUND_RGB_COLOR: true, LINE_LINE_COLOR: true, AREA_TOP_COLOR: true, AREA_BOTTOM_COLOR: true, diff --git a/website/theme-colors.js b/website/theme-colors.js index f5dd561851..4df0af8360 100644 --- a/website/theme-colors.js +++ b/website/theme-colors.js @@ -1,6 +1,7 @@ export const themeColors = { DARK: { CHART_BACKGROUND_COLOR: 'black', + CHART_BACKGROUND_RGB_COLOR: '0, 0, 0', LINE_LINE_COLOR: '#2962FF', AREA_TOP_COLOR: '#2962FF', AREA_BOTTOM_COLOR: 'rgba(41, 98, 255, 0.28)', @@ -17,6 +18,7 @@ export const themeColors = { }, LIGHT: { CHART_BACKGROUND_COLOR: 'white', + CHART_BACKGROUND_RGB_COLOR: '255, 255, 255', LINE_LINE_COLOR: '#2962FF', AREA_TOP_COLOR: '#2962FF', AREA_BOTTOM_COLOR: 'rgba(41, 98, 255, 0.28)', From 2b23985542d39bb2de5095ac47714ff29e9088db Mon Sep 17 00:00:00 2001 From: Mark Silverwood Date: Thu, 18 Aug 2022 19:24:50 +0200 Subject: [PATCH 124/188] Add 'how to' examples to the tutorials section Adding 'how to' examples to the tutorial section of the documentation site. --- website/sidebars-tutorials.js | 8 + website/tutorials/how_to/.eslintrc.js | 6 + .../tutorials/how_to/_usage-guide-partial.mdx | 16 + .../tutorials/how_to/inverted-price-scale.js | 187 ++++ .../tutorials/how_to/inverted-price-scale.mdx | 54 ++ website/tutorials/how_to/legend-3line.js | 238 +++++ website/tutorials/how_to/legend.js | 226 +++++ website/tutorials/how_to/legends.mdx | 73 ++ website/tutorials/how_to/no-time-scale.js | 43 + website/tutorials/how_to/price-and-volume.js | 360 ++++++++ website/tutorials/how_to/price-and-volume.mdx | 93 ++ website/tutorials/how_to/price-line.js | 441 +++++++++ website/tutorials/how_to/price-line.mdx | 66 ++ website/tutorials/how_to/series-markers.js | 775 ++++++++++++++++ website/tutorials/how_to/series-markers.mdx | 64 ++ website/tutorials/how_to/tooltip-floating.js | 275 ++++++ website/tutorials/how_to/tooltip-magnifier.js | 426 +++++++++ website/tutorials/how_to/tooltip-tracking.js | 266 ++++++ website/tutorials/how_to/tooltips.mdx | 122 +++ website/tutorials/how_to/two-price-scales.js | 859 ++++++++++++++++++ website/tutorials/how_to/two-price-scales.mdx | 78 ++ .../tutorials/how_to/watermark-advanced.js | 57 ++ website/tutorials/how_to/watermark-simple.js | 53 ++ website/tutorials/how_to/watermark.mdx | 106 +++ website/tutorials/index.mdx | 38 +- 25 files changed, 4928 insertions(+), 2 deletions(-) create mode 100644 website/tutorials/how_to/.eslintrc.js create mode 100644 website/tutorials/how_to/_usage-guide-partial.mdx create mode 100644 website/tutorials/how_to/inverted-price-scale.js create mode 100644 website/tutorials/how_to/inverted-price-scale.mdx create mode 100644 website/tutorials/how_to/legend-3line.js create mode 100644 website/tutorials/how_to/legend.js create mode 100644 website/tutorials/how_to/legends.mdx create mode 100644 website/tutorials/how_to/no-time-scale.js create mode 100644 website/tutorials/how_to/price-and-volume.js create mode 100644 website/tutorials/how_to/price-and-volume.mdx create mode 100644 website/tutorials/how_to/price-line.js create mode 100644 website/tutorials/how_to/price-line.mdx create mode 100644 website/tutorials/how_to/series-markers.js create mode 100644 website/tutorials/how_to/series-markers.mdx create mode 100644 website/tutorials/how_to/tooltip-floating.js create mode 100644 website/tutorials/how_to/tooltip-magnifier.js create mode 100644 website/tutorials/how_to/tooltip-tracking.js create mode 100644 website/tutorials/how_to/tooltips.mdx create mode 100644 website/tutorials/how_to/two-price-scales.js create mode 100644 website/tutorials/how_to/two-price-scales.mdx create mode 100644 website/tutorials/how_to/watermark-advanced.js create mode 100644 website/tutorials/how_to/watermark-simple.js create mode 100644 website/tutorials/how_to/watermark.mdx diff --git a/website/sidebars-tutorials.js b/website/sidebars-tutorials.js index e609c78deb..f918ade163 100644 --- a/website/sidebars-tutorials.js +++ b/website/sidebars-tutorials.js @@ -24,6 +24,14 @@ const sidebars = { ] }, ], }, + { + 'How To / Examples': [ + { + type: 'autogenerated', + dirName: 'how_to', + }, + ], + }, ], }; diff --git a/website/tutorials/how_to/.eslintrc.js b/website/tutorials/how_to/.eslintrc.js new file mode 100644 index 0000000000..5cd6c516c1 --- /dev/null +++ b/website/tutorials/how_to/.eslintrc.js @@ -0,0 +1,6 @@ +module.exports = { + globals: { + document: false, + createChart: false, + }, +}; diff --git a/website/tutorials/how_to/_usage-guide-partial.mdx b/website/tutorials/how_to/_usage-guide-partial.mdx new file mode 100644 index 0000000000..96e607a996 --- /dev/null +++ b/website/tutorials/how_to/_usage-guide-partial.mdx @@ -0,0 +1,16 @@ +
+How to use the code sample +The code presented below requires: + +- That `createChart` has already been imported. See [Getting Started](/docs#creating-a-chart) for more information, +- and that there is an html div element on the page with an `id` of `container`. + +Here is an example skeleton setup: [Code Sandbox](https://codesandbox.io/s/lightweight-charts-example-wq1emv?file=/src/index.js). +You can paste the provided code below the `// REPLACE EVERYTHING BELOW HERE` comment. + +:::tip + +Some code may be hidden to improve readability. Toggle the checkbox above the code block to reveal all the code. + +::: +
diff --git a/website/tutorials/how_to/inverted-price-scale.js b/website/tutorials/how_to/inverted-price-scale.js new file mode 100644 index 0000000000..e077441af7 --- /dev/null +++ b/website/tutorials/how_to/inverted-price-scale.js @@ -0,0 +1,187 @@ +// remove-start +// Lightweight Charts Example: Inverted Price Scale +// https://tradingview.github.io/lightweight-charts/tutorials/how_to/inverted-price-scale + +// remove-end +const chartOptions = { + layout: { + textColor: CHART_TEXT_COLOR, + background: { type: 'solid', color: CHART_BACKGROUND_COLOR }, + }, +}; +// remove-line +/** @type {import('lightweight-charts').IChartApi} */ +const chart = createChart(document.getElementById('container'), chartOptions); + +chart.applyOptions({ + rightPriceScale: { + scaleMargins: { + top: 0.1, + bottom: 0.1, + }, + // highlight-start + invertScale: true, + // highlight-end + }, +}); + +const lineSeries = chart.addLineSeries({ color: LINE_LINE_COLOR }); + +const data = [ + { time: '2016-07-18', value: 661.47 }, + // hide-start + { time: '2016-07-25', value: 623.83 }, + { time: '2016-08-01', value: 592.47 }, + { time: '2016-08-08', value: 568.76 }, + { time: '2016-08-15', value: 577.55 }, + { time: '2016-08-22', value: 573.20 }, + { time: '2016-08-29', value: 603.72 }, + { time: '2016-09-05', value: 606.32 }, + { time: '2016-09-12', value: 608.00 }, + { time: '2016-09-19', value: 598.98 }, + { time: '2016-09-26', value: 608.60 }, + { time: '2016-10-03', value: 613.06 }, + { time: '2016-10-10', value: 638.97 }, + { time: '2016-10-17', value: 648.74 }, + { time: '2016-10-24', value: 697.23 }, + { time: '2016-10-31', value: 709.93 }, + { time: '2016-11-07', value: 700.38 }, + { time: '2016-11-14', value: 727.09 }, + { time: '2016-11-21', value: 727.32 }, + { time: '2016-11-28', value: 762.00 }, + { time: '2016-12-05', value: 768.97 }, + { time: '2016-12-12', value: 788.67 }, + { time: '2016-12-19', value: 890.67 }, + { time: '2016-12-26', value: 997.75 }, + { time: '2017-01-02', value: 909.75 }, + { time: '2017-01-09', value: 821.86 }, + { time: '2017-01-16', value: 923.76 }, + { time: '2017-01-23', value: 912.01 }, + { time: '2017-01-30', value: 1011.07 }, + { time: '2017-02-06', value: 1000.73 }, + { time: '2017-02-13', value: 1051.80 }, + { time: '2017-02-20', value: 1179.05 }, + { time: '2017-02-27', value: 1273.00 }, + { time: '2017-03-06', value: 1226.62 }, + { time: '2017-03-13', value: 1017.97 }, + { time: '2017-03-20', value: 960.00 }, + { time: '2017-03-27', value: 1078.01 }, + { time: '2017-04-03', value: 1206.20 }, + { time: '2017-04-10', value: 1162.31 }, + { time: '2017-04-17', value: 1241.99 }, + { time: '2017-04-24', value: 1350.21 }, + { time: '2017-05-01', value: 1554.01 }, + { time: '2017-05-08', value: 1784.00 }, + { time: '2017-05-15', value: 2017.55 }, + { time: '2017-05-22', value: 2178.81 }, + { time: '2017-05-29', value: 2530.27 }, + { time: '2017-06-05', value: 2954.22 }, + { time: '2017-06-12', value: 2516.98 }, + { time: '2017-06-19', value: 2502.03 }, + { time: '2017-06-26', value: 2504.37 }, + { time: '2017-07-03', value: 2502.28 }, + { time: '2017-07-10', value: 1917.63 }, + { time: '2017-07-17', value: 2749.02 }, + { time: '2017-07-24', value: 2742.37 }, + { time: '2017-07-31', value: 3222.75 }, + { time: '2017-08-07', value: 4053.87 }, + { time: '2017-08-14', value: 4058.68 }, + { time: '2017-08-21', value: 4337.68 }, + { time: '2017-08-28', value: 4606.26 }, + { time: '2017-09-04', value: 4226.22 }, + { time: '2017-09-11', value: 3662.99 }, + { time: '2017-09-18', value: 3664.22 }, + { time: '2017-09-25', value: 4377.22 }, + { time: '2017-10-02', value: 4597.98 }, + { time: '2017-10-09', value: 5679.70 }, + { time: '2017-10-16', value: 5969.00 }, + { time: '2017-10-23', value: 6137.37 }, + { time: '2017-10-30', value: 7372.72 }, + { time: '2017-11-06', value: 5870.37 }, + { time: '2017-11-13', value: 8016.58 }, + { time: '2017-11-20', value: 9271.06 }, + { time: '2017-11-27', value: 11250.00 }, + { time: '2017-12-04', value: 14691.00 }, + { time: '2017-12-11', value: 18953.00 }, + { time: '2017-12-18', value: 14157.87 }, + { time: '2017-12-25', value: 13880.00 }, + { time: '2018-01-01', value: 16124.02 }, + { time: '2018-01-08', value: 13647.99 }, + { time: '2018-01-15', value: 11558.87 }, + { time: '2018-01-22', value: 11685.58 }, + { time: '2018-01-29', value: 8191.00 }, + { time: '2018-02-05', value: 8067.00 }, + { time: '2018-02-12', value: 10421.06 }, + { time: '2018-02-19', value: 9590.04 }, + { time: '2018-02-26', value: 11463.27 }, + { time: '2018-03-05', value: 9535.04 }, + { time: '2018-03-12', value: 8188.24 }, + { time: '2018-03-19', value: 8453.90 }, + { time: '2018-03-26', value: 6813.52 }, + { time: '2018-04-02', value: 7027.26 }, + { time: '2018-04-09', value: 8354.22 }, + { time: '2018-04-16', value: 8789.96 }, + { time: '2018-04-23', value: 9393.99 }, + { time: '2018-04-30', value: 9623.54 }, + { time: '2018-05-07', value: 8696.58 }, + { time: '2018-05-14', value: 8518.48 }, + { time: '2018-05-21', value: 7347.39 }, + { time: '2018-05-28', value: 7703.67 }, + { time: '2018-06-04', value: 6781.17 }, + { time: '2018-06-11', value: 6453.41 }, + { time: '2018-06-18', value: 6153.40 }, + { time: '2018-06-25', value: 6349.99 }, + { time: '2018-07-02', value: 6706.60 }, + { time: '2018-07-09', value: 6349.30 }, + { time: '2018-07-16', value: 7396.60 }, + { time: '2018-07-23', value: 8216.74 }, + { time: '2018-07-30', value: 7032.61 }, + { time: '2018-08-06', value: 6310.82 }, + { time: '2018-08-13', value: 6481.99 }, + { time: '2018-08-20', value: 6700.46 }, + { time: '2018-08-27', value: 7290.31 }, + { time: '2018-09-03', value: 6236.04 }, + { time: '2018-09-10', value: 6499.98 }, + { time: '2018-09-17', value: 6702.22 }, + { time: '2018-09-24', value: 6597.81 }, + { time: '2018-10-01', value: 6577.63 }, + { time: '2018-10-08', value: 6183.00 }, + { time: '2018-10-15', value: 6413.38 }, + { time: '2018-10-22', value: 6405.57 }, + { time: '2018-10-29', value: 6421.76 }, + { time: '2018-11-05', value: 6357.54 }, + { time: '2018-11-12', value: 5559.26 }, + { time: '2018-11-19', value: 3938.89 }, + { time: '2018-11-26', value: 4102.05 }, + { time: '2018-12-03', value: 3529.75 }, + { time: '2018-12-10', value: 3193.78 }, + { time: '2018-12-17', value: 3943.83 }, + { time: '2018-12-24', value: 3835.79 }, + { time: '2018-12-31', value: 4040.71 }, + { time: '2019-01-07', value: 3515.95 }, + { time: '2019-01-14', value: 3536.72 }, + { time: '2019-01-21', value: 3533.23 }, + { time: '2019-01-28', value: 3414.82 }, + { time: '2019-02-04', value: 3650.37 }, + { time: '2019-02-11', value: 3625.60 }, + { time: '2019-02-18', value: 3730.68 }, + { time: '2019-02-25', value: 3789.52 }, + { time: '2019-03-04', value: 3897.92 }, + { time: '2019-03-11', value: 3965.50 }, + { time: '2019-03-18', value: 3969.99 }, + { time: '2019-03-25', value: 4096.08 }, + { time: '2019-04-01', value: 5190.85 }, + { time: '2019-04-08', value: 5162.72 }, + { time: '2019-04-15', value: 5295.65 }, + { time: '2019-04-22', value: 5160.98 }, + { time: '2019-04-29', value: 5709.32 }, + { time: '2019-05-06', value: 6974.35 }, + { time: '2019-05-13', value: 8200.00 }, + { time: '2019-05-20', value: 8733.26 }, + { time: '2019-05-27', value: 8702.43 }, + // hide-end +]; + +lineSeries.setData(data); + +chart.timeScale().fitContent(); diff --git a/website/tutorials/how_to/inverted-price-scale.mdx b/website/tutorials/how_to/inverted-price-scale.mdx new file mode 100644 index 0000000000..d4af973445 --- /dev/null +++ b/website/tutorials/how_to/inverted-price-scale.mdx @@ -0,0 +1,54 @@ +--- +title: Inverted Price Scale +sidebar_label: Inverted Price Scale +description: How to invert a price scale. +pagination_prev: null +pagination_next: null +keywords: + - price scale + - Inverted + - example +--- + +This example shows how to invert a price scale. Usually, the price scale will +map the range of numbers from small to large along the vertical axis from bottom +to top. Inverting the price scale will change this such that the values map from +top to bottom. + +## How to + +Set the [`invertScale`](/docs/api/interfaces/PriceScaleOptions#invertscale) property +on the [priceScale options](/docs/api/interfaces/PriceScaleOptions) to `true`. + +```js +chart.applyOptions({ + rightPriceScale: { + invertScale: true, + }, +}); + +// or (for a specific price scale) +const priceScale = chart.priceScale(); +priceScale.applyOptions({ + invertScale: true, +}); +``` + +You can see a full [working example](#full-example) below. + +## Resources +- [invertScale](/docs/api/interfaces/PriceScaleOptions#invertscale) +- [Price Scales](/docs/price-scale) - General introduction to Price Scales. + +## Full example + +import UsageGuidePartial from "./_usage-guide-partial.mdx"; + + + +import CodeBlock from "@theme/CodeBlock"; +import code from "!!raw-loader!./inverted-price-scale.js"; + + + {code} + diff --git a/website/tutorials/how_to/legend-3line.js b/website/tutorials/how_to/legend-3line.js new file mode 100644 index 0000000000..71c88a4b7b --- /dev/null +++ b/website/tutorials/how_to/legend-3line.js @@ -0,0 +1,238 @@ +// remove-start +// Lightweight Charts Example: Legend 3 Lines +// https://tradingview.github.io/lightweight-charts/tutorials/how_to/legends + +// remove-end +const chartOptions = { + layout: { + textColor: CHART_TEXT_COLOR, + background: { type: 'solid', color: CHART_BACKGROUND_COLOR }, + }, +}; +// remove-line +/** @type {import('lightweight-charts').IChartApi} */ +const chart = createChart(document.getElementById('container'), chartOptions); + +chart.applyOptions({ + rightPriceScale: { + scaleMargins: { + top: 0.4, // leave some space for the legend + bottom: 0.15, + }, + }, + crosshair: { + // hide the horizontal crosshair line + horzLine: { + visible: false, + labelVisible: false, + }, + }, + // hide the grid lines + grid: { + vertLines: { + visible: false, + }, + horzLines: { + visible: false, + }, + }, +}); + +const areaSeries = chart.addAreaSeries({ + topColor: AREA_TOP_COLOR, + bottomColor: AREA_BOTTOM_COLOR, + lineColor: LINE_LINE_COLOR, + lineWidth: 2, + crossHairMarkerVisible: false, +}); + +const data = [ + { time: '2018-10-19', value: 26.19 }, + // hide-start + { time: '2018-10-22', value: 25.87 }, + { time: '2018-10-23', value: 25.83 }, + { time: '2018-10-24', value: 25.78 }, + { time: '2018-10-25', value: 25.82 }, + { time: '2018-10-26', value: 25.81 }, + { time: '2018-10-29', value: 25.82 }, + { time: '2018-10-30', value: 25.71 }, + { time: '2018-10-31', value: 25.82 }, + { time: '2018-11-01', value: 25.72 }, + { time: '2018-11-02', value: 25.74 }, + { time: '2018-11-05', value: 25.81 }, + { time: '2018-11-06', value: 25.75 }, + { time: '2018-11-07', value: 25.73 }, + { time: '2018-11-08', value: 25.75 }, + { time: '2018-11-09', value: 25.75 }, + { time: '2018-11-12', value: 25.76 }, + { time: '2018-11-13', value: 25.8 }, + { time: '2018-11-14', value: 25.77 }, + { time: '2018-11-15', value: 25.75 }, + { time: '2018-11-16', value: 25.75 }, + { time: '2018-11-19', value: 25.75 }, + { time: '2018-11-20', value: 25.72 }, + { time: '2018-11-21', value: 25.78 }, + { time: '2018-11-23', value: 25.72 }, + { time: '2018-11-26', value: 25.78 }, + { time: '2018-11-27', value: 25.85 }, + { time: '2018-11-28', value: 25.85 }, + { time: '2018-11-29', value: 25.55 }, + { time: '2018-11-30', value: 25.41 }, + { time: '2018-12-03', value: 25.41 }, + { time: '2018-12-04', value: 25.42 }, + { time: '2018-12-06', value: 25.33 }, + { time: '2018-12-07', value: 25.39 }, + { time: '2018-12-10', value: 25.32 }, + { time: '2018-12-11', value: 25.48 }, + { time: '2018-12-12', value: 25.39 }, + { time: '2018-12-13', value: 25.45 }, + { time: '2018-12-14', value: 25.52 }, + { time: '2018-12-17', value: 25.38 }, + { time: '2018-12-18', value: 25.36 }, + { time: '2018-12-19', value: 25.65 }, + { time: '2018-12-20', value: 25.7 }, + { time: '2018-12-21', value: 25.66 }, + { time: '2018-12-24', value: 25.66 }, + { time: '2018-12-26', value: 25.65 }, + { time: '2018-12-27', value: 25.66 }, + { time: '2018-12-28', value: 25.68 }, + { time: '2018-12-31', value: 25.77 }, + { time: '2019-01-02', value: 25.72 }, + { time: '2019-01-03', value: 25.69 }, + { time: '2019-01-04', value: 25.71 }, + { time: '2019-01-07', value: 25.72 }, + { time: '2019-01-08', value: 25.72 }, + { time: '2019-01-09', value: 25.66 }, + { time: '2019-01-10', value: 25.85 }, + { time: '2019-01-11', value: 25.92 }, + { time: '2019-01-14', value: 25.94 }, + { time: '2019-01-15', value: 25.95 }, + { time: '2019-01-16', value: 26.0 }, + { time: '2019-01-17', value: 25.99 }, + { time: '2019-01-18', value: 25.6 }, + { time: '2019-01-22', value: 25.81 }, + { time: '2019-01-23', value: 25.7 }, + { time: '2019-01-24', value: 25.74 }, + { time: '2019-01-25', value: 25.8 }, + { time: '2019-01-28', value: 25.83 }, + { time: '2019-01-29', value: 25.7 }, + { time: '2019-01-30', value: 25.78 }, + { time: '2019-01-31', value: 25.35 }, + { time: '2019-02-01', value: 25.6 }, + { time: '2019-02-04', value: 25.65 }, + { time: '2019-02-05', value: 25.73 }, + { time: '2019-02-06', value: 25.71 }, + { time: '2019-02-07', value: 25.71 }, + { time: '2019-02-08', value: 25.72 }, + { time: '2019-02-11', value: 25.76 }, + { time: '2019-02-12', value: 25.84 }, + { time: '2019-02-13', value: 25.85 }, + { time: '2019-02-14', value: 25.87 }, + { time: '2019-02-15', value: 25.89 }, + { time: '2019-02-19', value: 25.9 }, + { time: '2019-02-20', value: 25.92 }, + { time: '2019-02-21', value: 25.96 }, + { time: '2019-02-22', value: 26.0 }, + { time: '2019-02-25', value: 25.93 }, + { time: '2019-02-26', value: 25.92 }, + { time: '2019-02-27', value: 25.67 }, + { time: '2019-02-28', value: 25.79 }, + { time: '2019-03-01', value: 25.86 }, + { time: '2019-03-04', value: 25.94 }, + { time: '2019-03-05', value: 26.02 }, + { time: '2019-03-06', value: 25.95 }, + { time: '2019-03-07', value: 25.89 }, + { time: '2019-03-08', value: 25.94 }, + { time: '2019-03-11', value: 25.91 }, + { time: '2019-03-12', value: 25.92 }, + { time: '2019-03-13', value: 26.0 }, + { time: '2019-03-14', value: 26.05 }, + { time: '2019-03-15', value: 26.11 }, + { time: '2019-03-18', value: 26.1 }, + { time: '2019-03-19', value: 25.98 }, + { time: '2019-03-20', value: 26.11 }, + { time: '2019-03-21', value: 26.12 }, + { time: '2019-03-22', value: 25.88 }, + { time: '2019-03-25', value: 25.85 }, + { time: '2019-03-26', value: 25.72 }, + { time: '2019-03-27', value: 25.73 }, + { time: '2019-03-28', value: 25.8 }, + { time: '2019-03-29', value: 25.77 }, + { time: '2019-04-01', value: 26.06 }, + { time: '2019-04-02', value: 25.93 }, + { time: '2019-04-03', value: 25.95 }, + { time: '2019-04-04', value: 26.06 }, + { time: '2019-04-05', value: 26.16 }, + { time: '2019-04-08', value: 26.12 }, + { time: '2019-04-09', value: 26.07 }, + { time: '2019-04-10', value: 26.13 }, + { time: '2019-04-11', value: 26.04 }, + { time: '2019-04-12', value: 26.04 }, + { time: '2019-04-15', value: 26.05 }, + { time: '2019-04-16', value: 26.01 }, + { time: '2019-04-17', value: 26.09 }, + { time: '2019-04-18', value: 26.0 }, + { time: '2019-04-22', value: 26.0 }, + { time: '2019-04-23', value: 26.06 }, + { time: '2019-04-24', value: 26.0 }, + { time: '2019-04-25', value: 25.81 }, + { time: '2019-04-26', value: 25.88 }, + { time: '2019-04-29', value: 25.91 }, + { time: '2019-04-30', value: 25.9 }, + { time: '2019-05-01', value: 26.02 }, + { time: '2019-05-02', value: 25.97 }, + { time: '2019-05-03', value: 26.02 }, + { time: '2019-05-06', value: 26.03 }, + { time: '2019-05-07', value: 26.04 }, + { time: '2019-05-08', value: 26.05 }, + { time: '2019-05-09', value: 26.05 }, + { time: '2019-05-10', value: 26.08 }, + { time: '2019-05-13', value: 26.05 }, + { time: '2019-05-14', value: 26.01 }, + { time: '2019-05-15', value: 26.03 }, + { time: '2019-05-16', value: 26.14 }, + { time: '2019-05-17', value: 26.09 }, + { time: '2019-05-20', value: 26.01 }, + { time: '2019-05-21', value: 26.12 }, + { time: '2019-05-22', value: 26.15 }, + { time: '2019-05-23', value: 26.18 }, + { time: '2019-05-24', value: 26.16 }, + { time: '2019-05-28', value: 26.23 }, + // hide-end +]; + +areaSeries.setData(data); + +const symbolName = 'AEROSPACE'; + +const container = document.getElementById('container'); + +const legend = document.createElement('div'); +legend.style = `position: absolute; left: 12px; top: 12px; z-index: 1; font-size: 14px; font-family: sans-serif; line-height: 18px; font-weight: 300;`; +legend.style.color = CHART_TEXT_COLOR; +container.appendChild(legend); + +const getLastBar = () => data[data.length - 1]; +const buildDateString = time => `${time.year} - ${time.month} - ${time.day}`; +const formatPrice = price => (Math.round(price * 100) / 100).toFixed(2); +const setTooltipHtml = (name, date, price) => { + legend.innerHTML = `
${name}
${price}
${date}
`; +}; + +const updateLegend = param => { + const validCrosshairPoint = !( + param === undefined || param.time === undefined || param.point.x < 0 || param.point.y < 0 + ); + const bar = validCrosshairPoint ? param : getLastBar(); + const time = bar.time; + const date = buildDateString(time); + const price = validCrosshairPoint ? param.seriesPrices.get(areaSeries) : bar.value; + const formattedPrice = formatPrice(price); + setTooltipHtml(symbolName, date, formattedPrice); +}; + +chart.subscribeCrosshairMove(updateLegend); + +updateLegend(undefined); + +chart.timeScale().fitContent(); diff --git a/website/tutorials/how_to/legend.js b/website/tutorials/how_to/legend.js new file mode 100644 index 0000000000..67c5caebe3 --- /dev/null +++ b/website/tutorials/how_to/legend.js @@ -0,0 +1,226 @@ +// remove-start +// Lightweight Charts Example: Legend +// https://tradingview.github.io/lightweight-charts/tutorials/how_to/legends + +// remove-end +const chartOptions = { + layout: { + textColor: CHART_TEXT_COLOR, + background: { type: 'solid', color: CHART_BACKGROUND_COLOR }, + }, +}; +// remove-line +/** @type {import('lightweight-charts').IChartApi} */ +const chart = createChart(document.getElementById('container'), chartOptions); + +chart.applyOptions({ + rightPriceScale: { + scaleMargins: { + top: 0.3, // leave some space for the legend + bottom: 0.25, + }, + }, + crosshair: { + // hide the horizontal crosshair line + horzLine: { + visible: false, + labelVisible: false, + }, + }, + // hide the grid lines + grid: { + vertLines: { + visible: false, + }, + horzLines: { + visible: false, + }, + }, +}); + +const areaSeries = chart.addAreaSeries({ + topColor: AREA_TOP_COLOR, + bottomColor: AREA_BOTTOM_COLOR, + lineColor: LINE_LINE_COLOR, + lineWidth: 2, + crossHairMarkerVisible: false, +}); + +areaSeries.setData([ + { time: '2018-10-19', value: 26.19 }, + // hide-start + { time: '2018-10-22', value: 25.87 }, + { time: '2018-10-23', value: 25.83 }, + { time: '2018-10-24', value: 25.78 }, + { time: '2018-10-25', value: 25.82 }, + { time: '2018-10-26', value: 25.81 }, + { time: '2018-10-29', value: 25.82 }, + { time: '2018-10-30', value: 25.71 }, + { time: '2018-10-31', value: 25.82 }, + { time: '2018-11-01', value: 25.72 }, + { time: '2018-11-02', value: 25.74 }, + { time: '2018-11-05', value: 25.81 }, + { time: '2018-11-06', value: 25.75 }, + { time: '2018-11-07', value: 25.73 }, + { time: '2018-11-08', value: 25.75 }, + { time: '2018-11-09', value: 25.75 }, + { time: '2018-11-12', value: 25.76 }, + { time: '2018-11-13', value: 25.8 }, + { time: '2018-11-14', value: 25.77 }, + { time: '2018-11-15', value: 25.75 }, + { time: '2018-11-16', value: 25.75 }, + { time: '2018-11-19', value: 25.75 }, + { time: '2018-11-20', value: 25.72 }, + { time: '2018-11-21', value: 25.78 }, + { time: '2018-11-23', value: 25.72 }, + { time: '2018-11-26', value: 25.78 }, + { time: '2018-11-27', value: 25.85 }, + { time: '2018-11-28', value: 25.85 }, + { time: '2018-11-29', value: 25.55 }, + { time: '2018-11-30', value: 25.41 }, + { time: '2018-12-03', value: 25.41 }, + { time: '2018-12-04', value: 25.42 }, + { time: '2018-12-06', value: 25.33 }, + { time: '2018-12-07', value: 25.39 }, + { time: '2018-12-10', value: 25.32 }, + { time: '2018-12-11', value: 25.48 }, + { time: '2018-12-12', value: 25.39 }, + { time: '2018-12-13', value: 25.45 }, + { time: '2018-12-14', value: 25.52 }, + { time: '2018-12-17', value: 25.38 }, + { time: '2018-12-18', value: 25.36 }, + { time: '2018-12-19', value: 25.65 }, + { time: '2018-12-20', value: 25.7 }, + { time: '2018-12-21', value: 25.66 }, + { time: '2018-12-24', value: 25.66 }, + { time: '2018-12-26', value: 25.65 }, + { time: '2018-12-27', value: 25.66 }, + { time: '2018-12-28', value: 25.68 }, + { time: '2018-12-31', value: 25.77 }, + { time: '2019-01-02', value: 25.72 }, + { time: '2019-01-03', value: 25.69 }, + { time: '2019-01-04', value: 25.71 }, + { time: '2019-01-07', value: 25.72 }, + { time: '2019-01-08', value: 25.72 }, + { time: '2019-01-09', value: 25.66 }, + { time: '2019-01-10', value: 25.85 }, + { time: '2019-01-11', value: 25.92 }, + { time: '2019-01-14', value: 25.94 }, + { time: '2019-01-15', value: 25.95 }, + { time: '2019-01-16', value: 26.0 }, + { time: '2019-01-17', value: 25.99 }, + { time: '2019-01-18', value: 25.6 }, + { time: '2019-01-22', value: 25.81 }, + { time: '2019-01-23', value: 25.7 }, + { time: '2019-01-24', value: 25.74 }, + { time: '2019-01-25', value: 25.8 }, + { time: '2019-01-28', value: 25.83 }, + { time: '2019-01-29', value: 25.7 }, + { time: '2019-01-30', value: 25.78 }, + { time: '2019-01-31', value: 25.35 }, + { time: '2019-02-01', value: 25.6 }, + { time: '2019-02-04', value: 25.65 }, + { time: '2019-02-05', value: 25.73 }, + { time: '2019-02-06', value: 25.71 }, + { time: '2019-02-07', value: 25.71 }, + { time: '2019-02-08', value: 25.72 }, + { time: '2019-02-11', value: 25.76 }, + { time: '2019-02-12', value: 25.84 }, + { time: '2019-02-13', value: 25.85 }, + { time: '2019-02-14', value: 25.87 }, + { time: '2019-02-15', value: 25.89 }, + { time: '2019-02-19', value: 25.9 }, + { time: '2019-02-20', value: 25.92 }, + { time: '2019-02-21', value: 25.96 }, + { time: '2019-02-22', value: 26.0 }, + { time: '2019-02-25', value: 25.93 }, + { time: '2019-02-26', value: 25.92 }, + { time: '2019-02-27', value: 25.67 }, + { time: '2019-02-28', value: 25.79 }, + { time: '2019-03-01', value: 25.86 }, + { time: '2019-03-04', value: 25.94 }, + { time: '2019-03-05', value: 26.02 }, + { time: '2019-03-06', value: 25.95 }, + { time: '2019-03-07', value: 25.89 }, + { time: '2019-03-08', value: 25.94 }, + { time: '2019-03-11', value: 25.91 }, + { time: '2019-03-12', value: 25.92 }, + { time: '2019-03-13', value: 26.0 }, + { time: '2019-03-14', value: 26.05 }, + { time: '2019-03-15', value: 26.11 }, + { time: '2019-03-18', value: 26.1 }, + { time: '2019-03-19', value: 25.98 }, + { time: '2019-03-20', value: 26.11 }, + { time: '2019-03-21', value: 26.12 }, + { time: '2019-03-22', value: 25.88 }, + { time: '2019-03-25', value: 25.85 }, + { time: '2019-03-26', value: 25.72 }, + { time: '2019-03-27', value: 25.73 }, + { time: '2019-03-28', value: 25.8 }, + { time: '2019-03-29', value: 25.77 }, + { time: '2019-04-01', value: 26.06 }, + { time: '2019-04-02', value: 25.93 }, + { time: '2019-04-03', value: 25.95 }, + { time: '2019-04-04', value: 26.06 }, + { time: '2019-04-05', value: 26.16 }, + { time: '2019-04-08', value: 26.12 }, + { time: '2019-04-09', value: 26.07 }, + { time: '2019-04-10', value: 26.13 }, + { time: '2019-04-11', value: 26.04 }, + { time: '2019-04-12', value: 26.04 }, + { time: '2019-04-15', value: 26.05 }, + { time: '2019-04-16', value: 26.01 }, + { time: '2019-04-17', value: 26.09 }, + { time: '2019-04-18', value: 26.0 }, + { time: '2019-04-22', value: 26.0 }, + { time: '2019-04-23', value: 26.06 }, + { time: '2019-04-24', value: 26.0 }, + { time: '2019-04-25', value: 25.81 }, + { time: '2019-04-26', value: 25.88 }, + { time: '2019-04-29', value: 25.91 }, + { time: '2019-04-30', value: 25.9 }, + { time: '2019-05-01', value: 26.02 }, + { time: '2019-05-02', value: 25.97 }, + { time: '2019-05-03', value: 26.02 }, + { time: '2019-05-06', value: 26.03 }, + { time: '2019-05-07', value: 26.04 }, + { time: '2019-05-08', value: 26.05 }, + { time: '2019-05-09', value: 26.05 }, + { time: '2019-05-10', value: 26.08 }, + { time: '2019-05-13', value: 26.05 }, + { time: '2019-05-14', value: 26.01 }, + { time: '2019-05-15', value: 26.03 }, + { time: '2019-05-16', value: 26.14 }, + { time: '2019-05-17', value: 26.09 }, + { time: '2019-05-20', value: 26.01 }, + { time: '2019-05-21', value: 26.12 }, + { time: '2019-05-22', value: 26.15 }, + { time: '2019-05-23', value: 26.18 }, + { time: '2019-05-24', value: 26.16 }, + { time: '2019-05-28', value: 26.23 }, + // hide-end +]); + +const symbolName = 'ETC USD 7D VWAP'; + +const container = document.getElementById('container'); + +const legend = document.createElement('div'); +legend.style = `position: absolute; left: 12px; top: 12px; z-index: 1; font-size: 14px; font-family: sans-serif; line-height: 18px; font-weight: 300;`; +container.appendChild(legend); + +const firstRow = document.createElement('div'); +firstRow.innerHTML = symbolName; +firstRow.style.color = CHART_TEXT_COLOR; +legend.appendChild(firstRow); + +chart.subscribeCrosshairMove(param => { + let priceFormatted = ''; + if (param.time) { + const price = param.seriesPrices.get(areaSeries); + priceFormatted = price.toFixed(2); + } + firstRow.innerHTML = `${symbolName} ${priceFormatted}`; +}); + +chart.timeScale().fitContent(); diff --git a/website/tutorials/how_to/legends.mdx b/website/tutorials/how_to/legends.mdx new file mode 100644 index 0000000000..7056027d42 --- /dev/null +++ b/website/tutorials/how_to/legends.mdx @@ -0,0 +1,73 @@ +--- +title: Legends +sidebar_label: Legends +description: Examples on how to add a legend to your chart. +pagination_prev: null +pagination_next: null +keywords: + - example + - legend +--- + +Lightweight charts doesn't include a built-in legend feature, however it is something which can be added +to your chart by following the examples presented below. + +## How to + +In order to add a legend to the chart we need to create and position an `html` into the desired position above +the chart. We can then subscribe to the crosshairMove events ([subscribeCrosshairMove](/docs/api/interfaces/IChartApi#subscribecrosshairmove)) provided by the [`IChartApi`](/docs/api/interfaces/IChartApi) instance, and manually +update the content within our `html` legend element. + +```js +chart.subscribeCrosshairMove(param => { + let priceFormatted = ''; + if (param.time) { + const price = param.seriesPrices.get(areaSeries); + priceFormatted = price.toFixed(2); + } + // legend is a html element which has already been created + legend.innerHTML = `${symbolName} ${priceFormatted}`; +}); +``` + +The process of creating the legend html element and positioning can be seen within the examples below. +Essentially, we create a new div element within the container div (holding the chart) and then position +and style it using `css`. + +You can see full [working examples](#examples) below. + +## Resources + +- [subscribeCrosshairMove](/docs/api/interfaces/IChartApi#subscribecrosshairmove) +- [MouseEventParams Interface](/docs/api/interfaces/MouseEventParams) +- [MouseEventhandler](/docs/api#mouseeventhandler) + +Below are a few external resources related to creating and styling html elements: + +- [createElement](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement) +- [innerHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML) +- [style property](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) + +## Examples + +import UsageGuidePartial from "./_usage-guide-partial.mdx"; + + + +import CodeBlock from "@theme/CodeBlock"; + +### Simple Legend Example + +import codeLegend from "!!raw-loader!./legend.js"; + + + {codeLegend} + + +### 3 Line Legend Example + +import code3Line from "!!raw-loader!./legend-3line.js"; + + + {code3Line} + diff --git a/website/tutorials/how_to/no-time-scale.js b/website/tutorials/how_to/no-time-scale.js new file mode 100644 index 0000000000..0d97724901 --- /dev/null +++ b/website/tutorials/how_to/no-time-scale.js @@ -0,0 +1,43 @@ +// remove-start +// Lightweight Charts Example: No Time Scale +// https://tradingview.github.io/lightweight-charts/tutorials/how_to/example + +// remove-end +const chartOptions = { + layout: { + textColor: CHART_TEXT_COLOR, + background: { type: 'solid', color: CHART_BACKGROUND_COLOR }, + }, +}; +// remove-line +/** @type {import('lightweight-charts').IChartApi} */ +const chart = createChart(document.getElementById('container'), chartOptions); + +// highlight-start +chart.applyOptions({ + timeScale: { + visible: false, + }, +}); +// highlight-end + +const lineSeries = chart.addLineSeries({ color: LINE_LINE_COLOR }); + +const data = [ + { value: 0, time: 1642425322 }, + // hide-start + { value: 8, time: 1642511722 }, + { value: 10, time: 1642598122 }, + { value: 20, time: 1642684522 }, + { value: 3, time: 1642770922 }, + { value: 43, time: 1642857322 }, + { value: 41, time: 1642943722 }, + { value: 43, time: 1643030122 }, + { value: 56, time: 1643116522 }, + { value: 46, time: 1643202922 }, + // hide-end +]; + +lineSeries.setData(data); + +chart.timeScale().fitContent(); diff --git a/website/tutorials/how_to/price-and-volume.js b/website/tutorials/how_to/price-and-volume.js new file mode 100644 index 0000000000..2e3d065a03 --- /dev/null +++ b/website/tutorials/how_to/price-and-volume.js @@ -0,0 +1,360 @@ +// remove-start +// Lightweight Charts Example: Price and Volume +// https://tradingview.github.io/lightweight-charts/tutorials/how_to/price-and-volume + +// remove-end +const chartOptions = { + layout: { + textColor: CHART_TEXT_COLOR, + background: { type: 'solid', color: CHART_BACKGROUND_COLOR }, + }, + // highlight-start + rightPriceScale: { + // positioning the price scale for the area series + scaleMargins: { + top: 0.1, + bottom: 0.4, + }, + borderVisible: false, + }, + // highlight-end +}; +// remove-line +/** @type {import('lightweight-charts').IChartApi} */ +const chart = createChart(document.getElementById('container'), chartOptions); + +const areaSeries = chart.addAreaSeries({ + topColor: AREA_TOP_COLOR, + bottomColor: AREA_BOTTOM_COLOR, + lineColor: LINE_LINE_COLOR, + lineWidth: 2, +}); + +const volumeSeries = chart.addHistogramSeries({ + color: BAR_UP_COLOR, + // highlight-start + priceFormat: { + type: 'volume', + }, + priceScaleId: '', // set as an overlay by setting a blank priceScaleId + // set the positioning of the volume series + scaleMargins: { + top: 0.7, // highest point of the series will be 70% away from the top + bottom: 0, + }, + // highlight-end +}); + +areaSeries.setData([ + { time: '2018-10-19', value: 54.90 }, + // hide-start + { time: '2018-10-22', value: 54.98 }, + { time: '2018-10-23', value: 57.21 }, + { time: '2018-10-24', value: 57.42 }, + { time: '2018-10-25', value: 56.43 }, + { time: '2018-10-26', value: 55.51 }, + { time: '2018-10-29', value: 56.48 }, + { time: '2018-10-30', value: 58.18 }, + { time: '2018-10-31', value: 57.09 }, + { time: '2018-11-01', value: 56.05 }, + { time: '2018-11-02', value: 56.63 }, + { time: '2018-11-05', value: 57.21 }, + { time: '2018-11-06', value: 57.21 }, + { time: '2018-11-07', value: 57.65 }, + { time: '2018-11-08', value: 58.27 }, + { time: '2018-11-09', value: 58.46 }, + { time: '2018-11-12', value: 58.72 }, + { time: '2018-11-13', value: 58.66 }, + { time: '2018-11-14', value: 58.94 }, + { time: '2018-11-15', value: 59.08 }, + { time: '2018-11-16', value: 60.21 }, + { time: '2018-11-19', value: 60.62 }, + { time: '2018-11-20', value: 59.46 }, + { time: '2018-11-21', value: 59.16 }, + { time: '2018-11-23', value: 58.64 }, + { time: '2018-11-26', value: 59.17 }, + { time: '2018-11-27', value: 60.65 }, + { time: '2018-11-28', value: 60.06 }, + { time: '2018-11-29', value: 59.45 }, + { time: '2018-11-30', value: 60.30 }, + { time: '2018-12-03', value: 58.16 }, + { time: '2018-12-04', value: 58.09 }, + { time: '2018-12-06', value: 58.08 }, + { time: '2018-12-07', value: 57.68 }, + { time: '2018-12-10', value: 58.27 }, + { time: '2018-12-11', value: 58.85 }, + { time: '2018-12-12', value: 57.25 }, + { time: '2018-12-13', value: 57.09 }, + { time: '2018-12-14', value: 57.08 }, + { time: '2018-12-17', value: 55.95 }, + { time: '2018-12-18', value: 55.65 }, + { time: '2018-12-19', value: 55.86 }, + { time: '2018-12-20', value: 55.07 }, + { time: '2018-12-21', value: 54.92 }, + { time: '2018-12-24', value: 53.05 }, + { time: '2018-12-26', value: 54.44 }, + { time: '2018-12-27', value: 55.15 }, + { time: '2018-12-28', value: 55.27 }, + { time: '2018-12-31', value: 56.22 }, + { time: '2019-01-02', value: 56.02 }, + { time: '2019-01-03', value: 56.22 }, + { time: '2019-01-04', value: 56.36 }, + { time: '2019-01-07', value: 56.72 }, + { time: '2019-01-08', value: 58.38 }, + { time: '2019-01-09', value: 57.05 }, + { time: '2019-01-10', value: 57.60 }, + { time: '2019-01-11', value: 58.02 }, + { time: '2019-01-14', value: 58.03 }, + { time: '2019-01-15', value: 58.10 }, + { time: '2019-01-16', value: 57.08 }, + { time: '2019-01-17', value: 56.83 }, + { time: '2019-01-18', value: 57.09 }, + { time: '2019-01-22', value: 56.99 }, + { time: '2019-01-23', value: 57.76 }, + { time: '2019-01-24', value: 57.07 }, + { time: '2019-01-25', value: 56.40 }, + { time: '2019-01-28', value: 55.07 }, + { time: '2019-01-29', value: 53.28 }, + { time: '2019-01-30', value: 54.00 }, + { time: '2019-01-31', value: 55.06 }, + { time: '2019-02-01', value: 54.55 }, + { time: '2019-02-04', value: 54.04 }, + { time: '2019-02-05', value: 54.14 }, + { time: '2019-02-06', value: 53.79 }, + { time: '2019-02-07', value: 53.57 }, + { time: '2019-02-08', value: 53.95 }, + { time: '2019-02-11', value: 54.05 }, + { time: '2019-02-12', value: 54.42 }, + { time: '2019-02-13', value: 54.48 }, + { time: '2019-02-14', value: 54.03 }, + { time: '2019-02-15', value: 55.16 }, + { time: '2019-02-19', value: 55.44 }, + { time: '2019-02-20', value: 55.76 }, + { time: '2019-02-21', value: 56.15 }, + { time: '2019-02-22', value: 56.92 }, + { time: '2019-02-25', value: 56.78 }, + { time: '2019-02-26', value: 56.64 }, + { time: '2019-02-27', value: 56.72 }, + { time: '2019-02-28', value: 56.92 }, + { time: '2019-03-01', value: 56.96 }, + { time: '2019-03-04', value: 56.24 }, + { time: '2019-03-05', value: 56.08 }, + { time: '2019-03-06', value: 55.68 }, + { time: '2019-03-07', value: 56.30 }, + { time: '2019-03-08', value: 56.53 }, + { time: '2019-03-11', value: 57.58 }, + { time: '2019-03-12', value: 57.43 }, + { time: '2019-03-13', value: 57.66 }, + { time: '2019-03-14', value: 57.95 }, + { time: '2019-03-15', value: 58.39 }, + { time: '2019-03-18', value: 58.07 }, + { time: '2019-03-19', value: 57.50 }, + { time: '2019-03-20', value: 57.67 }, + { time: '2019-03-21', value: 58.29 }, + { time: '2019-03-22', value: 59.76 }, + { time: '2019-03-25', value: 60.08 }, + { time: '2019-03-26', value: 60.63 }, + { time: '2019-03-27', value: 60.88 }, + { time: '2019-03-28', value: 59.08 }, + { time: '2019-03-29', value: 59.13 }, + { time: '2019-04-01', value: 59.09 }, + { time: '2019-04-02', value: 58.53 }, + { time: '2019-04-03', value: 58.87 }, + { time: '2019-04-04', value: 58.99 }, + { time: '2019-04-05', value: 59.09 }, + { time: '2019-04-08', value: 59.13 }, + { time: '2019-04-09', value: 58.40 }, + { time: '2019-04-10', value: 58.61 }, + { time: '2019-04-11', value: 58.56 }, + { time: '2019-04-12', value: 58.74 }, + { time: '2019-04-15', value: 58.71 }, + { time: '2019-04-16', value: 58.79 }, + { time: '2019-04-17', value: 57.78 }, + { time: '2019-04-18', value: 58.04 }, + { time: '2019-04-22', value: 58.37 }, + { time: '2019-04-23', value: 57.15 }, + { time: '2019-04-24', value: 57.08 }, + { time: '2019-04-25', value: 55.85 }, + { time: '2019-04-26', value: 56.58 }, + { time: '2019-04-29', value: 56.84 }, + { time: '2019-04-30', value: 57.19 }, + { time: '2019-05-01', value: 56.52 }, + { time: '2019-05-02', value: 56.99 }, + { time: '2019-05-03', value: 57.24 }, + { time: '2019-05-06', value: 56.91 }, + { time: '2019-05-07', value: 56.63 }, + { time: '2019-05-08', value: 56.38 }, + { time: '2019-05-09', value: 56.48 }, + { time: '2019-05-10', value: 56.91 }, + { time: '2019-05-13', value: 56.75 }, + { time: '2019-05-14', value: 56.55 }, + { time: '2019-05-15', value: 56.81 }, + { time: '2019-05-16', value: 57.38 }, + { time: '2019-05-17', value: 58.09 }, + { time: '2019-05-20', value: 59.01 }, + { time: '2019-05-21', value: 59.50 }, + { time: '2019-05-22', value: 59.25 }, + { time: '2019-05-23', value: 58.87 }, + { time: '2019-05-24', value: 59.32 }, + { time: '2019-05-28', value: 59.57 }, + // hide-end +]); + +// setting the data for the volume series. +// note: we are defining each bars color as part of the data +volumeSeries.setData([ + { time: '2018-10-19', value: 19103293.00, color: BAR_UP_COLOR }, + // hide-start + { time: '2018-10-22', value: 21737523.00, color: BAR_UP_COLOR }, + { time: '2018-10-23', value: 29328713.00, color: BAR_UP_COLOR }, + { time: '2018-10-24', value: 37435638.00, color: BAR_UP_COLOR }, + { time: '2018-10-25', value: 25269995.00, color: BAR_DOWN_COLOR }, + { time: '2018-10-26', value: 24973311.00, color: BAR_DOWN_COLOR }, + { time: '2018-10-29', value: 22103692.00, color: BAR_UP_COLOR }, + { time: '2018-10-30', value: 25231199.00, color: BAR_UP_COLOR }, + { time: '2018-10-31', value: 24214427.00, color: BAR_DOWN_COLOR }, + { time: '2018-11-01', value: 22533201.00, color: BAR_DOWN_COLOR }, + { time: '2018-11-02', value: 14734412.00, color: BAR_UP_COLOR }, + { time: '2018-11-05', value: 12733842.00, color: BAR_UP_COLOR }, + { time: '2018-11-06', value: 12371207.00, color: BAR_UP_COLOR }, + { time: '2018-11-07', value: 14891287.00, color: BAR_UP_COLOR }, + { time: '2018-11-08', value: 12482392.00, color: BAR_UP_COLOR }, + { time: '2018-11-09', value: 17365762.00, color: BAR_UP_COLOR }, + { time: '2018-11-12', value: 13236769.00, color: BAR_UP_COLOR }, + { time: '2018-11-13', value: 13047907.00, color: BAR_DOWN_COLOR }, + { time: '2018-11-14', value: 18288710.00, color: BAR_UP_COLOR }, + { time: '2018-11-15', value: 17147123.00, color: BAR_UP_COLOR }, + { time: '2018-11-16', value: 19470986.00, color: BAR_UP_COLOR }, + { time: '2018-11-19', value: 18405731.00, color: BAR_UP_COLOR }, + { time: '2018-11-20', value: 22028957.00, color: BAR_DOWN_COLOR }, + { time: '2018-11-21', value: 18482233.00, color: BAR_DOWN_COLOR }, + { time: '2018-11-23', value: 7009050.00, color: BAR_DOWN_COLOR }, + { time: '2018-11-26', value: 12308876.00, color: BAR_UP_COLOR }, + { time: '2018-11-27', value: 14118867.00, color: BAR_UP_COLOR }, + { time: '2018-11-28', value: 18662989.00, color: BAR_DOWN_COLOR }, + { time: '2018-11-29', value: 14763658.00, color: BAR_DOWN_COLOR }, + { time: '2018-11-30', value: 31142818.00, color: BAR_UP_COLOR }, + { time: '2018-12-03', value: 27795428.00, color: BAR_DOWN_COLOR }, + { time: '2018-12-04', value: 21727411.00, color: BAR_DOWN_COLOR }, + { time: '2018-12-06', value: 26880429.00, color: BAR_DOWN_COLOR }, + { time: '2018-12-07', value: 16948126.00, color: BAR_DOWN_COLOR }, + { time: '2018-12-10', value: 16603356.00, color: BAR_UP_COLOR }, + { time: '2018-12-11', value: 14991438.00, color: BAR_UP_COLOR }, + { time: '2018-12-12', value: 18892182.00, color: BAR_DOWN_COLOR }, + { time: '2018-12-13', value: 15454706.00, color: BAR_DOWN_COLOR }, + { time: '2018-12-14', value: 13960870.00, color: BAR_DOWN_COLOR }, + { time: '2018-12-17', value: 18902523.00, color: BAR_DOWN_COLOR }, + { time: '2018-12-18', value: 18895777.00, color: BAR_DOWN_COLOR }, + { time: '2018-12-19', value: 20968473.00, color: BAR_UP_COLOR }, + { time: '2018-12-20', value: 26897008.00, color: BAR_DOWN_COLOR }, + { time: '2018-12-21', value: 55413082.00, color: BAR_DOWN_COLOR }, + { time: '2018-12-24', value: 15077207.00, color: BAR_DOWN_COLOR }, + { time: '2018-12-26', value: 17970539.00, color: BAR_UP_COLOR }, + { time: '2018-12-27', value: 17530977.00, color: BAR_UP_COLOR }, + { time: '2018-12-28', value: 14771641.00, color: BAR_UP_COLOR }, + { time: '2018-12-31', value: 15331758.00, color: BAR_UP_COLOR }, + { time: '2019-01-02', value: 13969691.00, color: BAR_DOWN_COLOR }, + { time: '2019-01-03', value: 19245411.00, color: BAR_UP_COLOR }, + { time: '2019-01-04', value: 17035848.00, color: BAR_UP_COLOR }, + { time: '2019-01-07', value: 16348982.00, color: BAR_UP_COLOR }, + { time: '2019-01-08', value: 21425008.00, color: BAR_UP_COLOR }, + { time: '2019-01-09', value: 18136000.00, color: BAR_DOWN_COLOR }, + { time: '2019-01-10', value: 14259910.00, color: BAR_UP_COLOR }, + { time: '2019-01-11', value: 15801548.00, color: BAR_UP_COLOR }, + { time: '2019-01-14', value: 11342293.00, color: BAR_UP_COLOR }, + { time: '2019-01-15', value: 10074386.00, color: BAR_UP_COLOR }, + { time: '2019-01-16', value: 13411691.00, color: BAR_DOWN_COLOR }, + { time: '2019-01-17', value: 15223854.00, color: BAR_DOWN_COLOR }, + { time: '2019-01-18', value: 16802516.00, color: BAR_UP_COLOR }, + { time: '2019-01-22', value: 18284771.00, color: BAR_DOWN_COLOR }, + { time: '2019-01-23', value: 15109007.00, color: BAR_UP_COLOR }, + { time: '2019-01-24', value: 12494109.00, color: BAR_DOWN_COLOR }, + { time: '2019-01-25', value: 17806822.00, color: BAR_DOWN_COLOR }, + { time: '2019-01-28', value: 25955718.00, color: BAR_DOWN_COLOR }, + { time: '2019-01-29', value: 33789235.00, color: BAR_DOWN_COLOR }, + { time: '2019-01-30', value: 27260036.00, color: BAR_UP_COLOR }, + { time: '2019-01-31', value: 28585447.00, color: BAR_UP_COLOR }, + { time: '2019-02-01', value: 13778392.00, color: BAR_DOWN_COLOR }, + { time: '2019-02-04', value: 15818901.00, color: BAR_DOWN_COLOR }, + { time: '2019-02-05', value: 14124794.00, color: BAR_UP_COLOR }, + { time: '2019-02-06', value: 11391442.00, color: BAR_DOWN_COLOR }, + { time: '2019-02-07', value: 12436168.00, color: BAR_DOWN_COLOR }, + { time: '2019-02-08', value: 12011657.00, color: BAR_UP_COLOR }, + { time: '2019-02-11', value: 9802798.00, color: BAR_UP_COLOR }, + { time: '2019-02-12', value: 11227550.00, color: BAR_UP_COLOR }, + { time: '2019-02-13', value: 11884803.00, color: BAR_UP_COLOR }, + { time: '2019-02-14', value: 11190094.00, color: BAR_DOWN_COLOR }, + { time: '2019-02-15', value: 15719416.00, color: BAR_UP_COLOR }, + { time: '2019-02-19', value: 12272877.00, color: BAR_UP_COLOR }, + { time: '2019-02-20', value: 11379006.00, color: BAR_UP_COLOR }, + { time: '2019-02-21', value: 14680547.00, color: BAR_UP_COLOR }, + { time: '2019-02-22', value: 12534431.00, color: BAR_UP_COLOR }, + { time: '2019-02-25', value: 15051182.00, color: BAR_DOWN_COLOR }, + { time: '2019-02-26', value: 12005571.00, color: BAR_DOWN_COLOR }, + { time: '2019-02-27', value: 8962776.00, color: BAR_UP_COLOR }, + { time: '2019-02-28', value: 15742971.00, color: BAR_UP_COLOR }, + { time: '2019-03-01', value: 10942737.00, color: BAR_UP_COLOR }, + { time: '2019-03-04', value: 13674737.00, color: BAR_DOWN_COLOR }, + { time: '2019-03-05', value: 15749545.00, color: BAR_DOWN_COLOR }, + { time: '2019-03-06', value: 13935530.00, color: BAR_DOWN_COLOR }, + { time: '2019-03-07', value: 12644171.00, color: BAR_UP_COLOR }, + { time: '2019-03-08', value: 10646710.00, color: BAR_UP_COLOR }, + { time: '2019-03-11', value: 13627431.00, color: BAR_UP_COLOR }, + { time: '2019-03-12', value: 12812980.00, color: BAR_DOWN_COLOR }, + { time: '2019-03-13', value: 14168350.00, color: BAR_UP_COLOR }, + { time: '2019-03-14', value: 12148349.00, color: BAR_UP_COLOR }, + { time: '2019-03-15', value: 23715337.00, color: BAR_UP_COLOR }, + { time: '2019-03-18', value: 12168133.00, color: BAR_DOWN_COLOR }, + { time: '2019-03-19', value: 13462686.00, color: BAR_DOWN_COLOR }, + { time: '2019-03-20', value: 11903104.00, color: BAR_UP_COLOR }, + { time: '2019-03-21', value: 10920129.00, color: BAR_UP_COLOR }, + { time: '2019-03-22', value: 25125385.00, color: BAR_UP_COLOR }, + { time: '2019-03-25', value: 15463411.00, color: BAR_UP_COLOR }, + { time: '2019-03-26', value: 12316901.00, color: BAR_UP_COLOR }, + { time: '2019-03-27', value: 13290298.00, color: BAR_UP_COLOR }, + { time: '2019-03-28', value: 20547060.00, color: BAR_DOWN_COLOR }, + { time: '2019-03-29', value: 17283871.00, color: BAR_UP_COLOR }, + { time: '2019-04-01', value: 16331140.00, color: BAR_DOWN_COLOR }, + { time: '2019-04-02', value: 11408146.00, color: BAR_DOWN_COLOR }, + { time: '2019-04-03', value: 15491724.00, color: BAR_UP_COLOR }, + { time: '2019-04-04', value: 8776028.00, color: BAR_UP_COLOR }, + { time: '2019-04-05', value: 11497780.00, color: BAR_UP_COLOR }, + { time: '2019-04-08', value: 11680538.00, color: BAR_UP_COLOR }, + { time: '2019-04-09', value: 10414416.00, color: BAR_DOWN_COLOR }, + { time: '2019-04-10', value: 8782061.00, color: BAR_UP_COLOR }, + { time: '2019-04-11', value: 9219930.00, color: BAR_DOWN_COLOR }, + { time: '2019-04-12', value: 10847504.00, color: BAR_UP_COLOR }, + { time: '2019-04-15', value: 7741472.00, color: BAR_DOWN_COLOR }, + { time: '2019-04-16', value: 10239261.00, color: BAR_UP_COLOR }, + { time: '2019-04-17', value: 15498037.00, color: BAR_DOWN_COLOR }, + { time: '2019-04-18', value: 13189013.00, color: BAR_UP_COLOR }, + { time: '2019-04-22', value: 11950365.00, color: BAR_UP_COLOR }, + { time: '2019-04-23', value: 23488682.00, color: BAR_DOWN_COLOR }, + { time: '2019-04-24', value: 13227084.00, color: BAR_DOWN_COLOR }, + { time: '2019-04-25', value: 17425466.00, color: BAR_DOWN_COLOR }, + { time: '2019-04-26', value: 16329727.00, color: BAR_UP_COLOR }, + { time: '2019-04-29', value: 13984965.00, color: BAR_UP_COLOR }, + { time: '2019-04-30', value: 15469002.00, color: BAR_UP_COLOR }, + { time: '2019-05-01', value: 11627436.00, color: BAR_DOWN_COLOR }, + { time: '2019-05-02', value: 14435436.00, color: BAR_UP_COLOR }, + { time: '2019-05-03', value: 9388228.00, color: BAR_UP_COLOR }, + { time: '2019-05-06', value: 10066145.00, color: BAR_DOWN_COLOR }, + { time: '2019-05-07', value: 12963827.00, color: BAR_DOWN_COLOR }, + { time: '2019-05-08', value: 12086743.00, color: BAR_DOWN_COLOR }, + { time: '2019-05-09', value: 14835326.00, color: BAR_UP_COLOR }, + { time: '2019-05-10', value: 10707335.00, color: BAR_UP_COLOR }, + { time: '2019-05-13', value: 13759350.00, color: BAR_DOWN_COLOR }, + { time: '2019-05-14', value: 12776175.00, color: BAR_DOWN_COLOR }, + { time: '2019-05-15', value: 10806379.00, color: BAR_UP_COLOR }, + { time: '2019-05-16', value: 11695064.00, color: BAR_UP_COLOR }, + { time: '2019-05-17', value: 14436662.00, color: BAR_UP_COLOR }, + { time: '2019-05-20', value: 20910590.00, color: BAR_UP_COLOR }, + { time: '2019-05-21', value: 14016315.00, color: BAR_UP_COLOR }, + { time: '2019-05-22', value: 11487448.00, color: BAR_DOWN_COLOR }, + { time: '2019-05-23', value: 11707083.00, color: BAR_DOWN_COLOR }, + { time: '2019-05-24', value: 8755506.00, color: BAR_UP_COLOR }, + { time: '2019-05-28', value: 3097125.00, color: BAR_UP_COLOR }, + // hide-end +]); + +chart.timeScale().fitContent(); diff --git a/website/tutorials/how_to/price-and-volume.mdx b/website/tutorials/how_to/price-and-volume.mdx new file mode 100644 index 0000000000..5428dd6201 --- /dev/null +++ b/website/tutorials/how_to/price-and-volume.mdx @@ -0,0 +1,93 @@ +--- +title: Price and volume on a single chart +sidebar_label: Price and Volume +description: An example of how to include both price and volume series on a single chart. +pagination_prev: null +pagination_next: null +keywords: + - example +--- + +This example shows how to include a volume study on your chart. + +## How to add a volume histogram + +An additional series can be added to a chart as an 'overlay' by setting the series' +[`priceScaleId`](/docs/api/interfaces/SeriesOptionsCommon#pricescaleid) to `''`. +An overlay doesn't make use of either the left or right price scale, and it's positioning +is controlled by setting the [`scaleMargins`](/docs/api/interfaces/PriceScaleOptions#scalemargins) +property on the series options. + +```js +const volumeSeries = chart.addHistogramSeries({ + priceFormat: { + type: 'volume', + }, + priceScaleId: '', // set as an overlay by setting a blank priceScaleId + // set the positioning of the volume series + scaleMargins: { + top: 0.7, // highest point of the series will be 70% away from the top + bottom: 0, + }, +}); +``` + +We are using the [Histogram](/docs/series-types#histogram) series type to draw the volume bars. +We can set the `priceFormat` option to `'volume'` to have the values display correctly within +the crosshair line label. + +We adjust the position of the overlay series to the bottom 30% of the chart by +setting the [`scaleMargins`](/docs/api/interfaces/PriceScaleOptions#scalemargins) properties as follows: + +```js +series.applyOptions({ + scaleMargins: { + top: 0.7, // highest point of the series will be 70% away from the top + bottom: 0, // lowest point will be at the very bottom. + }, +}); +``` + +Similarly, we can set the position of the main series using the same approach. By setting +the `bottom` margin value to `0.4` we can ensure that the two series don't overlap each other. + +```js +mainSeries.applyOptions({ + scaleMargins: { + top: 0.1, // highest point of the series will be 10% away from the top + bottom: 0.4, // lowest point will be 40% away from the bottom + }, +}); +``` + +We can control the color of the histogram bars by directly specifying color inside +the data set. + +```js +histogramSeries.setData([ + { time: '2018-10-19', value: 19103293.0, color: 'green' }, + { time: '2018-10-20', value: 20345000.0, color: 'red' }, +]); +``` + +You can see a full [working example](#full-example) below. + +## Resources + +- [OverlayPriceScale Options](/docs/api#overlaypricescaleoptions) +- [Histogram Series Type](/docs/series-types#histogram) +- [PriceFormat Types](/docs/api/interfaces/PriceFormatBuiltIn#type) +- [Scale Margins](/docs/api/interfaces/PriceScaleOptions#scalemargins) + +## Full example + +import UsageGuidePartial from "./_usage-guide-partial.mdx"; + + + +import CodeBlock from "@theme/CodeBlock"; +import code from "!!raw-loader!./price-and-volume.js"; + + + {code} + diff --git a/website/tutorials/how_to/price-line.js b/website/tutorials/how_to/price-line.js new file mode 100644 index 0000000000..0058aea743 --- /dev/null +++ b/website/tutorials/how_to/price-line.js @@ -0,0 +1,441 @@ +// remove-start +// Lightweight Charts Example: Price Lines +// https://tradingview.github.io/lightweight-charts/tutorials/how_to/price-line + +// remove-end +const chartOptions = { + layout: { + textColor: CHART_TEXT_COLOR, + background: { type: 'solid', color: CHART_BACKGROUND_COLOR }, + }, +}; +// remove-line +/** @type {import('lightweight-charts').IChartApi} */ +const chart = createChart(document.getElementById('container'), chartOptions); + +const series = chart.addLineSeries({ + color: LINE_LINE_COLOR, + lineWidth: 2, + // highlight-start + // disabling built-in price lines + lastValueVisible: false, + priceLineVisible: false, + // highlight-end +}); + +const data = [ + { time: { year: 2018, month: 1, day: 1 }, value: 27.58405298746434 }, + // hide-start + { time: { year: 2018, month: 1, day: 2 }, value: 31.74088841431117 }, + { time: { year: 2018, month: 1, day: 3 }, value: 35.892978753808926 }, + { time: { year: 2018, month: 1, day: 4 }, value: 39.63642029045179 }, + { time: { year: 2018, month: 1, day: 5 }, value: 40.79167357702531 }, + { time: { year: 2018, month: 1, day: 6 }, value: 47.691740220947764 }, + { time: { year: 2018, month: 1, day: 7 }, value: 49.377161099825415 }, + { time: { year: 2018, month: 1, day: 8 }, value: 52.47379203136591 }, + { time: { year: 2018, month: 1, day: 9 }, value: 50.40209743179448 }, + { time: { year: 2018, month: 1, day: 10 }, value: 61.47316837848548 }, + { time: { year: 2018, month: 1, day: 11 }, value: 58.22831552141069 }, + { time: { year: 2018, month: 1, day: 12 }, value: 59.36868132891698 }, + { time: { year: 2018, month: 1, day: 13 }, value: 62.10845687168416 }, + { time: { year: 2018, month: 1, day: 14 }, value: 51.259701958506724 }, + { time: { year: 2018, month: 1, day: 15 }, value: 56.247578870411644 }, + { time: { year: 2018, month: 1, day: 16 }, value: 55.483307642385164 }, + { time: { year: 2018, month: 1, day: 17 }, value: 55.85295564734231 }, + { time: { year: 2018, month: 1, day: 18 }, value: 48.3138216778343 }, + { time: { year: 2018, month: 1, day: 19 }, value: 53.071901176203866 }, + { time: { year: 2018, month: 1, day: 20 }, value: 50.873781097281885 }, + { time: { year: 2018, month: 1, day: 21 }, value: 49.7840315054249 }, + { time: { year: 2018, month: 1, day: 22 }, value: 52.34956807336156 }, + { time: { year: 2018, month: 1, day: 23 }, value: 53.79112543285674 }, + { time: { year: 2018, month: 1, day: 24 }, value: 53.984887985424805 }, + { time: { year: 2018, month: 1, day: 25 }, value: 58.56902893497121 }, + { time: { year: 2018, month: 1, day: 26 }, value: 54.76191372282466 }, + { time: { year: 2018, month: 1, day: 27 }, value: 63.38042554684846 }, + { time: { year: 2018, month: 1, day: 28 }, value: 55.452618512103065 }, + { time: { year: 2018, month: 1, day: 29 }, value: 65.60820758942769 }, + { time: { year: 2018, month: 1, day: 30 }, value: 56.82795136583009 }, + { time: { year: 2018, month: 1, day: 31 }, value: 70.3148022984224 }, + { time: { year: 2018, month: 2, day: 1 }, value: 65.86230944167264 }, + { time: { year: 2018, month: 2, day: 2 }, value: 72.05467846676524 }, + { time: { year: 2018, month: 2, day: 3 }, value: 72.99238887850564 }, + { time: { year: 2018, month: 2, day: 4 }, value: 67.03373730222785 }, + { time: { year: 2018, month: 2, day: 5 }, value: 69.97670934736414 }, + { time: { year: 2018, month: 2, day: 6 }, value: 73.08910595492105 }, + { time: { year: 2018, month: 2, day: 7 }, value: 81.43976528732057 }, + { time: { year: 2018, month: 2, day: 8 }, value: 73.62230936920984 }, + { time: { year: 2018, month: 2, day: 9 }, value: 82.15522801870938 }, + { time: { year: 2018, month: 2, day: 10 }, value: 77.99384538574678 }, + { time: { year: 2018, month: 2, day: 11 }, value: 85.62489628897463 }, + { time: { year: 2018, month: 2, day: 12 }, value: 86.93090666568217 }, + { time: { year: 2018, month: 2, day: 13 }, value: 75.99689788850394 }, + { time: { year: 2018, month: 2, day: 14 }, value: 88.46418548355727 }, + { time: { year: 2018, month: 2, day: 15 }, value: 86.20760396539865 }, + { time: { year: 2018, month: 2, day: 16 }, value: 81.88757639758437 }, + { time: { year: 2018, month: 2, day: 17 }, value: 79.58151786389108 }, + { time: { year: 2018, month: 2, day: 18 }, value: 80.96845249711073 }, + { time: { year: 2018, month: 2, day: 19 }, value: 73.54901807055447 }, + { time: { year: 2018, month: 2, day: 20 }, value: 75.65626118347262 }, + { time: { year: 2018, month: 2, day: 21 }, value: 78.41307347680399 }, + { time: { year: 2018, month: 2, day: 22 }, value: 74.60352602043042 }, + { time: { year: 2018, month: 2, day: 23 }, value: 72.28241570381236 }, + { time: { year: 2018, month: 2, day: 24 }, value: 72.24427397962566 }, + { time: { year: 2018, month: 2, day: 25 }, value: 64.80996965592134 }, + { time: { year: 2018, month: 2, day: 26 }, value: 67.37511361319652 }, + { time: { year: 2018, month: 2, day: 27 }, value: 65.5449131917524 }, + { time: { year: 2018, month: 2, day: 28 }, value: 65.4802711362433 }, + { time: { year: 2018, month: 3, day: 1 }, value: 62.207767815581086 }, + { time: { year: 2018, month: 3, day: 2 }, value: 59.78884720470812 }, + { time: { year: 2018, month: 3, day: 3 }, value: 67.51846586137782 }, + { time: { year: 2018, month: 3, day: 4 }, value: 68.752834400291 }, + { time: { year: 2018, month: 3, day: 5 }, value: 66.63416073573323 }, + { time: { year: 2018, month: 3, day: 6 }, value: 65.58601621691751 }, + { time: { year: 2018, month: 3, day: 7 }, value: 57.33498792621458 }, + { time: { year: 2018, month: 3, day: 8 }, value: 56.93436946311955 }, + { time: { year: 2018, month: 3, day: 9 }, value: 58.31144672902557 }, + { time: { year: 2018, month: 3, day: 10 }, value: 59.96407207657268 }, + { time: { year: 2018, month: 3, day: 11 }, value: 55.7861486424976 }, + { time: { year: 2018, month: 3, day: 12 }, value: 52.91803500214551 }, + { time: { year: 2018, month: 3, day: 13 }, value: 54.491591573038306 }, + { time: { year: 2018, month: 3, day: 14 }, value: 51.924409342593385 }, + { time: { year: 2018, month: 3, day: 15 }, value: 41.90263950118436 }, + { time: { year: 2018, month: 3, day: 16 }, value: 40.514436076485694 }, + { time: { year: 2018, month: 3, day: 17 }, value: 41.065887666854486 }, + { time: { year: 2018, month: 3, day: 18 }, value: 40.44445534031683 }, + { time: { year: 2018, month: 3, day: 19 }, value: 42.13922977216152 }, + { time: { year: 2018, month: 3, day: 20 }, value: 42.317162952084495 }, + { time: { year: 2018, month: 3, day: 21 }, value: 39.02881877743751 }, + { time: { year: 2018, month: 3, day: 22 }, value: 39.81917993955704 }, + { time: { year: 2018, month: 3, day: 23 }, value: 36.753197056053374 }, + { time: { year: 2018, month: 3, day: 24 }, value: 37.02203306330588 }, + { time: { year: 2018, month: 3, day: 25 }, value: 36.36014042161194 }, + { time: { year: 2018, month: 3, day: 26 }, value: 33.56275879100148 }, + { time: { year: 2018, month: 3, day: 27 }, value: 34.39112540787079 }, + { time: { year: 2018, month: 3, day: 28 }, value: 30.57170225544929 }, + { time: { year: 2018, month: 3, day: 29 }, value: 33.56826040802756 }, + { time: { year: 2018, month: 3, day: 30 }, value: 32.89895543218274 }, + { time: { year: 2018, month: 3, day: 31 }, value: 31.015658561825738 }, + { time: { year: 2018, month: 4, day: 1 }, value: 33.189179815787455 }, + { time: { year: 2018, month: 4, day: 2 }, value: 29.530756945582162 }, + { time: { year: 2018, month: 4, day: 3 }, value: 29.250978140719916 }, + { time: { year: 2018, month: 4, day: 4 }, value: 27.89635178919736 }, + { time: { year: 2018, month: 4, day: 5 }, value: 26.995427160624686 }, + { time: { year: 2018, month: 4, day: 6 }, value: 25.89631885043023 }, + { time: { year: 2018, month: 4, day: 7 }, value: 28.71812492475548 }, + { time: { year: 2018, month: 4, day: 8 }, value: 23.56476085365468 }, + { time: { year: 2018, month: 4, day: 9 }, value: 23.8550787876547 }, + { time: { year: 2018, month: 4, day: 10 }, value: 23.27046572075657 }, + { time: { year: 2018, month: 4, day: 11 }, value: 25.73099630369951 }, + { time: { year: 2018, month: 4, day: 12 }, value: 23.398760738394646 }, + { time: { year: 2018, month: 4, day: 13 }, value: 22.97970501784193 }, + { time: { year: 2018, month: 4, day: 14 }, value: 22.145587244500526 }, + { time: { year: 2018, month: 4, day: 15 }, value: 20.622578956226192 }, + { time: { year: 2018, month: 4, day: 16 }, value: 21.99297423796017 }, + { time: { year: 2018, month: 4, day: 17 }, value: 20.756081302371527 }, + { time: { year: 2018, month: 4, day: 18 }, value: 18.1983338834302 }, + { time: { year: 2018, month: 4, day: 19 }, value: 16.419404563645198 }, + { time: { year: 2018, month: 4, day: 20 }, value: 18.38192363882247 }, + { time: { year: 2018, month: 4, day: 21 }, value: 17.41452255210322 }, + { time: { year: 2018, month: 4, day: 22 }, value: 17.39866711593896 }, + { time: { year: 2018, month: 4, day: 23 }, value: 14.859371316920733 }, + { time: { year: 2018, month: 4, day: 24 }, value: 14.49488592297959 }, + { time: { year: 2018, month: 4, day: 25 }, value: 14.183858665721397 }, + { time: { year: 2018, month: 4, day: 26 }, value: 12.754187935636056 }, + { time: { year: 2018, month: 4, day: 27 }, value: 14.467536059608742 }, + { time: { year: 2018, month: 4, day: 28 }, value: 14.72962730689032 }, + { time: { year: 2018, month: 4, day: 29 }, value: 16.591675981296518 }, + { time: { year: 2018, month: 4, day: 30 }, value: 17.560385679284135 }, + { time: { year: 2018, month: 5, day: 1 }, value: 22.386250317504064 }, + { time: { year: 2018, month: 5, day: 2 }, value: 23.697029442697385 }, + { time: { year: 2018, month: 5, day: 3 }, value: 23.453148128592442 }, + { time: { year: 2018, month: 5, day: 4 }, value: 23.164700105036882 }, + { time: { year: 2018, month: 5, day: 5 }, value: 23.919034678006323 }, + { time: { year: 2018, month: 5, day: 6 }, value: 25.18353870528509 }, + { time: { year: 2018, month: 5, day: 7 }, value: 26.982824465076394 }, + { time: { year: 2018, month: 5, day: 8 }, value: 29.122512185000712 }, + { time: { year: 2018, month: 5, day: 9 }, value: 29.60160406576696 }, + { time: { year: 2018, month: 5, day: 10 }, value: 30.845749384528016 }, + { time: { year: 2018, month: 5, day: 11 }, value: 33.03296938636202 }, + { time: { year: 2018, month: 5, day: 12 }, value: 33.784707082446104 }, + { time: { year: 2018, month: 5, day: 13 }, value: 36.08545410406137 }, + { time: { year: 2018, month: 5, day: 14 }, value: 36.80597815439238 }, + { time: { year: 2018, month: 5, day: 15 }, value: 34.56062188644443 }, + { time: { year: 2018, month: 5, day: 16 }, value: 36.52666657165473 }, + { time: { year: 2018, month: 5, day: 17 }, value: 34.76705735297833 }, + { time: { year: 2018, month: 5, day: 18 }, value: 39.01598033743525 }, + { time: { year: 2018, month: 5, day: 19 }, value: 37.60979560604685 }, + { time: { year: 2018, month: 5, day: 20 }, value: 42.403895121650436 }, + { time: { year: 2018, month: 5, day: 21 }, value: 45.55998014298455 }, + { time: { year: 2018, month: 5, day: 22 }, value: 39.76704752577289 }, + { time: { year: 2018, month: 5, day: 23 }, value: 41.83196178430985 }, + { time: { year: 2018, month: 5, day: 24 }, value: 46.99399105885453 }, + { time: { year: 2018, month: 5, day: 25 }, value: 48.553566318637984 }, + { time: { year: 2018, month: 5, day: 26 }, value: 48.918166891087594 }, + { time: { year: 2018, month: 5, day: 27 }, value: 42.95391588314584 }, + { time: { year: 2018, month: 5, day: 28 }, value: 51.267649950057546 }, + { time: { year: 2018, month: 5, day: 29 }, value: 44.86104374986037 }, + { time: { year: 2018, month: 5, day: 30 }, value: 46.7183564731453 }, + { time: { year: 2018, month: 5, day: 31 }, value: 52.753001379260766 }, + { time: { year: 2018, month: 6, day: 1 }, value: 56.04835638442964 }, + { time: { year: 2018, month: 6, day: 2 }, value: 54.82119793390652 }, + { time: { year: 2018, month: 6, day: 3 }, value: 57.718938021257685 }, + { time: { year: 2018, month: 6, day: 4 }, value: 53.9914459224653 }, + { time: { year: 2018, month: 6, day: 5 }, value: 59.33050624063286 }, + { time: { year: 2018, month: 6, day: 6 }, value: 50.79074268713266 }, + { time: { year: 2018, month: 6, day: 7 }, value: 53.15657316197036 }, + { time: { year: 2018, month: 6, day: 8 }, value: 59.43675134021954 }, + { time: { year: 2018, month: 6, day: 9 }, value: 63.28437595760727 }, + { time: { year: 2018, month: 6, day: 10 }, value: 58.07099287435428 }, + { time: { year: 2018, month: 6, day: 11 }, value: 56.68728978119396 }, + { time: { year: 2018, month: 6, day: 12 }, value: 57.05079700873323 }, + { time: { year: 2018, month: 6, day: 13 }, value: 65.65087785161663 }, + { time: { year: 2018, month: 6, day: 14 }, value: 59.20877581396441 }, + { time: { year: 2018, month: 6, day: 15 }, value: 64.30200099280857 }, + { time: { year: 2018, month: 6, day: 16 }, value: 68.60774992100288 }, + { time: { year: 2018, month: 6, day: 17 }, value: 67.16628680993453 }, + { time: { year: 2018, month: 6, day: 18 }, value: 62.89644591741811 }, + { time: { year: 2018, month: 6, day: 19 }, value: 61.42888198118138 }, + { time: { year: 2018, month: 6, day: 20 }, value: 64.89813095653885 }, + { time: { year: 2018, month: 6, day: 21 }, value: 72.72701573552945 }, + { time: { year: 2018, month: 6, day: 22 }, value: 67.85006296129148 }, + { time: { year: 2018, month: 6, day: 23 }, value: 74.8567814889 }, + { time: { year: 2018, month: 6, day: 24 }, value: 66.24228046316296 }, + { time: { year: 2018, month: 6, day: 25 }, value: 68.09192660329269 }, + { time: { year: 2018, month: 6, day: 26 }, value: 75.30075953672075 }, + { time: { year: 2018, month: 6, day: 27 }, value: 68.7478054620306 }, + { time: { year: 2018, month: 6, day: 28 }, value: 76.2285023801364 }, + { time: { year: 2018, month: 6, day: 29 }, value: 82.945167109736 }, + { time: { year: 2018, month: 6, day: 30 }, value: 76.91811779365663 }, + { time: { year: 2018, month: 7, day: 1 }, value: 73.4904875247808 }, + { time: { year: 2018, month: 7, day: 2 }, value: 78.37882382739707 }, + { time: { year: 2018, month: 7, day: 3 }, value: 77.00224598364632 }, + { time: { year: 2018, month: 7, day: 4 }, value: 74.96345662377028 }, + { time: { year: 2018, month: 7, day: 5 }, value: 85.54303380001907 }, + { time: { year: 2018, month: 7, day: 6 }, value: 74.23916926437794 }, + { time: { year: 2018, month: 7, day: 7 }, value: 86.38109732705232 }, + { time: { year: 2018, month: 7, day: 8 }, value: 88.36203657839357 }, + { time: { year: 2018, month: 7, day: 9 }, value: 81.63303116061893 }, + { time: { year: 2018, month: 7, day: 10 }, value: 78.05188105610182 }, + { time: { year: 2018, month: 7, day: 11 }, value: 93.73294498110195 }, + { time: { year: 2018, month: 7, day: 12 }, value: 86.3226018109303 }, + { time: { year: 2018, month: 7, day: 13 }, value: 83.18571530136985 }, + { time: { year: 2018, month: 7, day: 14 }, value: 92.45097319531271 }, + { time: { year: 2018, month: 7, day: 15 }, value: 82.61971871486392 }, + { time: { year: 2018, month: 7, day: 16 }, value: 84.39935112744469 }, + { time: { year: 2018, month: 7, day: 17 }, value: 86.84420907417798 }, + { time: { year: 2018, month: 7, day: 18 }, value: 81.50766784637338 }, + { time: { year: 2018, month: 7, day: 19 }, value: 88.74841709228694 }, + { time: { year: 2018, month: 7, day: 20 }, value: 85.84190975050336 }, + { time: { year: 2018, month: 7, day: 21 }, value: 86.9594938103096 }, + { time: { year: 2018, month: 7, day: 22 }, value: 83.72572564120199 }, + { time: { year: 2018, month: 7, day: 23 }, value: 83.42754326770913 }, + { time: { year: 2018, month: 7, day: 24 }, value: 80.40755818165856 }, + { time: { year: 2018, month: 7, day: 25 }, value: 81.40515523172276 }, + { time: { year: 2018, month: 7, day: 26 }, value: 88.6671912474792 }, + { time: { year: 2018, month: 7, day: 27 }, value: 83.98197434091429 }, + { time: { year: 2018, month: 7, day: 28 }, value: 79.93747419607003 }, + { time: { year: 2018, month: 7, day: 29 }, value: 88.74666581089701 }, + { time: { year: 2018, month: 7, day: 30 }, value: 88.4887222568271 }, + { time: { year: 2018, month: 7, day: 31 }, value: 91.70282162754224 }, + { time: { year: 2018, month: 8, day: 1 }, value: 81.82327312829118 }, + { time: { year: 2018, month: 8, day: 2 }, value: 89.56625546634567 }, + { time: { year: 2018, month: 8, day: 3 }, value: 83.60742160062637 }, + { time: { year: 2018, month: 8, day: 4 }, value: 92.16271144958857 }, + { time: { year: 2018, month: 8, day: 5 }, value: 92.93451790057534 }, + { time: { year: 2018, month: 8, day: 6 }, value: 97.10629615852636 }, + { time: { year: 2018, month: 8, day: 7 }, value: 93.18989484413193 }, + { time: { year: 2018, month: 8, day: 8 }, value: 99.52744238602173 }, + { time: { year: 2018, month: 8, day: 9 }, value: 90.84973685659028 }, + { time: { year: 2018, month: 8, day: 10 }, value: 98.37517814040118 }, + { time: { year: 2018, month: 8, day: 11 }, value: 90.13525425607658 }, + { time: { year: 2018, month: 8, day: 12 }, value: 95.55125798221395 }, + { time: { year: 2018, month: 8, day: 13 }, value: 82.77346455876308 }, + { time: { year: 2018, month: 8, day: 14 }, value: 83.24854499361042 }, + { time: { year: 2018, month: 8, day: 15 }, value: 95.41999231944423 }, + { time: { year: 2018, month: 8, day: 16 }, value: 93.80228527345439 }, + { time: { year: 2018, month: 8, day: 17 }, value: 95.6453232668047 }, + { time: { year: 2018, month: 8, day: 18 }, value: 85.15427147925779 }, + { time: { year: 2018, month: 8, day: 19 }, value: 84.96447951638784 }, + { time: { year: 2018, month: 8, day: 20 }, value: 95.92739640648459 }, + { time: { year: 2018, month: 8, day: 21 }, value: 96.44143870862634 }, + { time: { year: 2018, month: 8, day: 22 }, value: 101.23323393804354 }, + { time: { year: 2018, month: 8, day: 23 }, value: 92.12092707164649 }, + { time: { year: 2018, month: 8, day: 24 }, value: 101.74117610271144 }, + { time: { year: 2018, month: 8, day: 25 }, value: 96.38311956379351 }, + { time: { year: 2018, month: 8, day: 26 }, value: 98.18485692799554 }, + { time: { year: 2018, month: 8, day: 27 }, value: 102.60080903938159 }, + { time: { year: 2018, month: 8, day: 28 }, value: 97.48394132428021 }, + { time: { year: 2018, month: 8, day: 29 }, value: 101.41501247525068 }, + { time: { year: 2018, month: 8, day: 30 }, value: 94.9501205245301 }, + { time: { year: 2018, month: 8, day: 31 }, value: 89.11429564465982 }, + { time: { year: 2018, month: 9, day: 1 }, value: 104.1842141132897 }, + { time: { year: 2018, month: 9, day: 2 }, value: 97.36185743859737 }, + { time: { year: 2018, month: 9, day: 3 }, value: 97.34376526297034 }, + { time: { year: 2018, month: 9, day: 4 }, value: 103.73609636859413 }, + { time: { year: 2018, month: 9, day: 5 }, value: 86.89420264148593 }, + { time: { year: 2018, month: 9, day: 6 }, value: 90.99445484839778 }, + { time: { year: 2018, month: 9, day: 7 }, value: 92.0197876339847 }, + { time: { year: 2018, month: 9, day: 8 }, value: 87.35412911434453 }, + { time: { year: 2018, month: 9, day: 9 }, value: 97.40224787139312 }, + { time: { year: 2018, month: 9, day: 10 }, value: 98.14732375673768 }, + { time: { year: 2018, month: 9, day: 11 }, value: 101.5147062059064 }, + { time: { year: 2018, month: 9, day: 12 }, value: 93.11950437404803 }, + { time: { year: 2018, month: 9, day: 13 }, value: 98.41765784798642 }, + { time: { year: 2018, month: 9, day: 14 }, value: 89.08499357950659 }, + { time: { year: 2018, month: 9, day: 15 }, value: 96.29213559344964 }, + { time: { year: 2018, month: 9, day: 16 }, value: 103.57528341068684 }, + { time: { year: 2018, month: 9, day: 17 }, value: 98.94258099235431 }, + { time: { year: 2018, month: 9, day: 18 }, value: 92.43383394007822 }, + { time: { year: 2018, month: 9, day: 19 }, value: 105.39681697822706 }, + { time: { year: 2018, month: 9, day: 20 }, value: 100.52663985092036 }, + { time: { year: 2018, month: 9, day: 21 }, value: 98.84754340440189 }, + { time: { year: 2018, month: 9, day: 22 }, value: 93.78502517034752 }, + { time: { year: 2018, month: 9, day: 23 }, value: 95.51435402626828 }, + { time: { year: 2018, month: 9, day: 24 }, value: 91.94633821567461 }, + { time: { year: 2018, month: 9, day: 25 }, value: 98.18484857755837 }, + { time: { year: 2018, month: 9, day: 26 }, value: 102.51694320185617 }, + { time: { year: 2018, month: 9, day: 27 }, value: 97.40549024974396 }, + { time: { year: 2018, month: 9, day: 28 }, value: 103.49718807374374 }, + { time: { year: 2018, month: 9, day: 29 }, value: 108.24441490057781 }, + { time: { year: 2018, month: 9, day: 30 }, value: 103.24675412744978 }, + { time: { year: 2018, month: 10, day: 1 }, value: 97.05089568637521 }, + { time: { year: 2018, month: 10, day: 2 }, value: 91.85875309835458 }, + { time: { year: 2018, month: 10, day: 3 }, value: 72.24590653541385 }, + { time: { year: 2018, month: 10, day: 4 }, value: 70.73707674373722 }, + { time: { year: 2018, month: 10, day: 5 }, value: 61.2106378263602 }, + { time: { year: 2018, month: 10, day: 6 }, value: 62.35889407826063 }, + { time: { year: 2018, month: 10, day: 7 }, value: 56.311930696659 }, + { time: { year: 2018, month: 10, day: 8 }, value: 51.462743547904374 }, + { time: { year: 2018, month: 10, day: 9 }, value: 47.99928302521288 }, + { time: { year: 2018, month: 10, day: 10 }, value: 42.735011616511976 }, + { time: { year: 2018, month: 10, day: 11 }, value: 35.82291867003256 }, + { time: { year: 2018, month: 10, day: 12 }, value: 28.706141122035454 }, + { time: { year: 2018, month: 10, day: 13 }, value: 24.289344698634036 }, + { time: { year: 2018, month: 10, day: 14 }, value: 22.56513000253429 }, + { time: { year: 2018, month: 10, day: 15 }, value: 18.862530899060324 }, + { time: { year: 2018, month: 10, day: 16 }, value: 18.164416367748263 }, + { time: { year: 2018, month: 10, day: 17 }, value: 16.25993836760582 }, + { time: { year: 2018, month: 10, day: 18 }, value: 15.849033589978859 }, + { time: { year: 2018, month: 10, day: 19 }, value: 12.581184324950792 }, + { time: { year: 2018, month: 10, day: 20 }, value: 12.46960767886934 }, + { time: { year: 2018, month: 10, day: 21 }, value: 13.203284995561251 }, + { time: { year: 2018, month: 10, day: 22 }, value: 12.751819244602629 }, + { time: { year: 2018, month: 10, day: 23 }, value: 13.815126496529205 }, + { time: { year: 2018, month: 10, day: 24 }, value: 14.44156419046133 }, + { time: { year: 2018, month: 10, day: 25 }, value: 12.030505290672643 }, + { time: { year: 2018, month: 10, day: 26 }, value: 13.426535837756518 }, + { time: { year: 2018, month: 10, day: 27 }, value: 13.141003741491893 }, + { time: { year: 2018, month: 10, day: 28 }, value: 12.216411608284261 }, + { time: { year: 2018, month: 10, day: 29 }, value: 12.437867813477077 }, + { time: { year: 2018, month: 10, day: 30 }, value: 12.228521667932771 }, + { time: { year: 2018, month: 10, day: 31 }, value: 13.587126038913974 }, + { time: { year: 2018, month: 11, day: 1 }, value: 12.016792589137491 }, + { time: { year: 2018, month: 11, day: 2 }, value: 13.01948020515645 }, + { time: { year: 2018, month: 11, day: 3 }, value: 12.70475528902004 }, + { time: { year: 2018, month: 11, day: 4 }, value: 13.018454073452016 }, + { time: { year: 2018, month: 11, day: 5 }, value: 12.505487262036313 }, + { time: { year: 2018, month: 11, day: 6 }, value: 13.467522897553604 }, + { time: { year: 2018, month: 11, day: 7 }, value: 13.748796553616549 }, + { time: { year: 2018, month: 11, day: 8 }, value: 11.974873751121669 }, + { time: { year: 2018, month: 11, day: 9 }, value: 11.8316362912944 }, + { time: { year: 2018, month: 11, day: 10 }, value: 13.864291857325023 }, + { time: { year: 2018, month: 11, day: 11 }, value: 12.071675684436165 }, + { time: { year: 2018, month: 11, day: 12 }, value: 12.314581956701367 }, + { time: { year: 2018, month: 11, day: 13 }, value: 13.223445358310986 }, + { time: { year: 2018, month: 11, day: 14 }, value: 12.346191421746546 }, + { time: { year: 2018, month: 11, day: 15 }, value: 12.0232072259563 }, + { time: { year: 2018, month: 11, day: 16 }, value: 13.367039701380252 }, + { time: { year: 2018, month: 11, day: 17 }, value: 12.232635114201212 }, + { time: { year: 2018, month: 11, day: 18 }, value: 13.348220671014012 }, + { time: { year: 2018, month: 11, day: 19 }, value: 13.508314659502604 }, + { time: { year: 2018, month: 11, day: 20 }, value: 12.630893498655155 }, + { time: { year: 2018, month: 11, day: 21 }, value: 12.632952849963768 }, + { time: { year: 2018, month: 11, day: 22 }, value: 11.645073051089117 }, + { time: { year: 2018, month: 11, day: 23 }, value: 13.845637677048611 }, + { time: { year: 2018, month: 11, day: 24 }, value: 12.567519871595463 }, + { time: { year: 2018, month: 11, day: 25 }, value: 13.927270152127294 }, + { time: { year: 2018, month: 11, day: 26 }, value: 12.179362670863737 }, + { time: { year: 2018, month: 11, day: 27 }, value: 12.471835488646303 }, + { time: { year: 2018, month: 11, day: 28 }, value: 11.71761488042106 }, + { time: { year: 2018, month: 11, day: 29 }, value: 12.181312973409113 }, + { time: { year: 2018, month: 11, day: 30 }, value: 12.662272671374286 }, + { time: { year: 2018, month: 12, day: 1 }, value: 13.859774226603497 }, + { time: { year: 2018, month: 12, day: 2 }, value: 11.643237368800426 }, + { time: { year: 2018, month: 12, day: 3 }, value: 11.646083130428282 }, + { time: { year: 2018, month: 12, day: 4 }, value: 13.3486102643562 }, + { time: { year: 2018, month: 12, day: 5 }, value: 13.421817450001853 }, + { time: { year: 2018, month: 12, day: 6 }, value: 13.734844672048157 }, + { time: { year: 2018, month: 12, day: 7 }, value: 11.808371821628475 }, + { time: { year: 2018, month: 12, day: 8 }, value: 13.906976670383472 }, + { time: { year: 2018, month: 12, day: 9 }, value: 13.161627469585584 }, + { time: { year: 2018, month: 12, day: 10 }, value: 13.642368164712535 }, + { time: { year: 2018, month: 12, day: 11 }, value: 12.755167209621261 }, + { time: { year: 2018, month: 12, day: 12 }, value: 12.13947468588139 }, + { time: { year: 2018, month: 12, day: 13 }, value: 13.68979129854326 }, + { time: { year: 2018, month: 12, day: 14 }, value: 11.812050924695251 }, + { time: { year: 2018, month: 12, day: 15 }, value: 11.71992287051065 }, + { time: { year: 2018, month: 12, day: 16 }, value: 13.003823991463284 }, + { time: { year: 2018, month: 12, day: 17 }, value: 13.124946877570311 }, + { time: { year: 2018, month: 12, day: 18 }, value: 11.844736927132542 }, + { time: { year: 2018, month: 12, day: 19 }, value: 11.770961792864846 }, + { time: { year: 2018, month: 12, day: 20 }, value: 10.926179837275598 }, + { time: { year: 2018, month: 12, day: 21 }, value: 11.155771630851676 }, + { time: { year: 2018, month: 12, day: 22 }, value: 11.63008791780728 }, + { time: { year: 2018, month: 12, day: 23 }, value: 10.216268005840389 }, + { time: { year: 2018, month: 12, day: 24 }, value: 13.611694182717626 }, + { time: { year: 2018, month: 12, day: 25 }, value: 17.47706580052263 }, + { time: { year: 2018, month: 12, day: 26 }, value: 20.900697260373697 }, + { time: { year: 2018, month: 12, day: 27 }, value: 20.44940301651778 }, + { time: { year: 2018, month: 12, day: 28 }, value: 23.47128311932538 }, + { time: { year: 2018, month: 12, day: 29 }, value: 28.63506505828928 }, + { time: { year: 2018, month: 12, day: 30 }, value: 29.567577074788517 }, + // hide-end +]; +series.setData(data); + +let minimumPrice = data[0].value; +let maximumPrice = minimumPrice; +for (let i = 1; i < data.length; i++) { + const price = data[i].value; + if (price > maximumPrice) { + maximumPrice = price; + } + if (price < minimumPrice) { + minimumPrice = price; + } +} +const avgPrice = (maximumPrice + minimumPrice) / 2; + +// highlight-start +const lineWidth = 2; +const minPriceLine = { + price: minimumPrice, + color: BAR_DOWN_COLOR, + lineWidth: lineWidth, + lineStyle: 2, // LineStyle.Dashed + axisLabelVisible: true, + title: 'min price', +}; +const avgPriceLine = { + price: avgPrice, + color: CHART_TEXT_COLOR, + lineWidth: lineWidth, + lineStyle: 1, // LineStyle.Dotted + axisLabelVisible: true, + title: 'ave price', +}; +const maxPriceLine = { + price: maximumPrice, + color: BAR_UP_COLOR, + lineWidth: lineWidth, + lineStyle: 2, // LineStyle.Dashed + axisLabelVisible: true, + title: 'max price', +}; + +series.createPriceLine(minPriceLine); +series.createPriceLine(avgPriceLine); +series.createPriceLine(maxPriceLine); +// highlight-end + +chart.timeScale().fitContent(); diff --git a/website/tutorials/how_to/price-line.mdx b/website/tutorials/how_to/price-line.mdx new file mode 100644 index 0000000000..61b303f14d --- /dev/null +++ b/website/tutorials/how_to/price-line.mdx @@ -0,0 +1,66 @@ +--- +title: Add Price Line +sidebar_label: Price Lines +description: An example of how to add price lines to a chart. +pagination_prev: null +pagination_next: null +keywords: + - price line +--- + +A price line is a horizontal line drawn across the width of the chart at a specific price value. +This example shows how to add price lines to your chart. + +## Short answer + +A price line can be added to a chart by using the +[`createPriceLine`](/docs/api/interfaces/ISeriesApi#createpriceline) method on an existing series +([ISeriesApi](/docs/api/interfaces/ISeriesApi)) instance. + +```js +const myPriceLine = { + price: 1234, + color: BAR_UP_COLOR, + lineWidth: lineWidth, + lineStyle: 2, // LineStyle.Dashed + axisLabelVisible: true, + title: 'my label', +}; + +series.createPriceLine(myPriceLine); +``` + +You can see a full [working example](#full-example) below. + +## Tips + +You may wish to disable the default price line drawn by Lightweight Charts +for the last value in the series (and it's label). You can do this by adjusting the +series options as follows: + +```js +series.applyOptions({ + lastValueVisible: false, + priceLineVisible: false, +}); +``` + +## Resources + +You can view the related APIs here: +- [createPriceLine](/docs/api/interfaces/ISeriesApi#createpriceline) - Method to create a new Price Line. +- [PriceLineOptions](/docs/api/interfaces/PriceLineOptions) - Price Line options. +- [LineStyle](/docs/api/enums/LineStyle) - Available line styles. + +## Full example + +import UsageGuidePartial from "./_usage-guide-partial.mdx"; + + + +import CodeBlock from "@theme/CodeBlock"; +import code from "!!raw-loader!./price-line.js"; + + + {code} + diff --git a/website/tutorials/how_to/series-markers.js b/website/tutorials/how_to/series-markers.js new file mode 100644 index 0000000000..f8910bdc9f --- /dev/null +++ b/website/tutorials/how_to/series-markers.js @@ -0,0 +1,775 @@ +// remove-start +// Lightweight Charts Example: Series Markers +// https://tradingview.github.io/lightweight-charts/tutorials/how_to/series-markers + +// remove-end +const chartOptions = { + layout: { + textColor: CHART_TEXT_COLOR, + background: { type: 'solid', color: CHART_BACKGROUND_COLOR }, + }, +}; +// remove-line +/** @type {import('lightweight-charts').IChartApi} */ +const chart = createChart(document.getElementById('container'), chartOptions); + +const series = chart.addCandlestickSeries({ + upColor: BAR_UP_COLOR, downColor: BAR_DOWN_COLOR, borderVisible: false, + wickUpColor: BAR_UP_COLOR, wickDownColor: BAR_DOWN_COLOR, +}); + +const data = [ + { + time: { year: 2018, month: 9, day: 22 }, + open: 29.630237296336794, + high: 35.36950035097501, + low: 26.21522501353531, + close: 30.734997177569916, + }, + // hide-start + { + time: { year: 2018, month: 9, day: 23 }, + open: 32.267626500691215, + high: 34.452661663723774, + low: 26.096868360824704, + close: 29.573918833457004, + }, + { + time: { year: 2018, month: 9, day: 24 }, + open: 27.33996760497746, + high: 35.8060364835531, + low: 27.33996760497746, + close: 33.06283432964511, + }, + { + time: { year: 2018, month: 9, day: 25 }, + open: 31.1654368745013, + high: 31.97284477478497, + low: 26.766743287285593, + close: 27.364979322283386, + }, + { + time: { year: 2018, month: 9, day: 26 }, + open: 29.5901452337888, + high: 32.147919593347474, + low: 27.53289219709677, + close: 29.202912415085272, + }, + { + time: { year: 2018, month: 9, day: 27 }, + open: 27.561741523265923, + high: 35.11649043301526, + low: 25.20035866163233, + close: 31.14520649627546, + }, + { + time: { year: 2018, month: 9, day: 28 }, + open: 31.925975006823798, + high: 31.925975006823798, + low: 28.998500720406675, + close: 29.87723790403876, + }, + { + time: { year: 2018, month: 9, day: 29 }, + open: 30.826956088992475, + high: 34.79463130873015, + low: 25.291546123273097, + close: 28.994812708315987, + }, + { + time: { year: 2018, month: 9, day: 30 }, + open: 31.202920145287838, + high: 33.19178819590413, + low: 23.94419012923956, + close: 31.47253745770869, + }, + { + time: { year: 2018, month: 10, day: 1 }, + open: 26.927794164758666, + high: 34.6744456778885, + low: 26.927794164758666, + close: 31.091122539737423, + }, + { + time: { year: 2018, month: 10, day: 2 }, + open: 26.452041173938298, + high: 34.527917622572154, + low: 26.452041173938298, + close: 27.65703395829094, + }, + { + time: { year: 2018, month: 10, day: 3 }, + open: 27.74629982387605, + high: 29.300441707649835, + low: 23.761300216231263, + close: 29.182874625005628, + }, + { + time: { year: 2018, month: 10, day: 4 }, + open: 30.41599722290526, + high: 31.942643078777103, + low: 27.09925359459428, + close: 30.918477883682872, + }, + { + time: { year: 2018, month: 10, day: 5 }, + open: 25.76549797105683, + high: 33.4650523853759, + low: 25.76549797105683, + close: 28.15984801386293, + }, + { + time: { year: 2018, month: 10, day: 6 }, + open: 27.543404135965382, + high: 30.7227783000902, + low: 25.749951838020884, + close: 29.150903848724184, + }, + { + time: { year: 2018, month: 10, day: 7 }, + open: 29.34759861812077, + high: 31.08503530472835, + low: 23.395022079647823, + close: 25.00923131079722, + }, + { + time: { year: 2018, month: 10, day: 8 }, + open: 27.00266154335036, + high: 29.51599687178633, + low: 23.46749249241176, + close: 28.702932483799707, + }, + { + time: { year: 2018, month: 10, day: 9 }, + open: 25.569958099853594, + high: 27.669071502065417, + low: 25.569958099853594, + close: 25.626920473922613, + }, + { + time: { year: 2018, month: 10, day: 10 }, + open: 24.886919828178304, + high: 27.167620185117006, + low: 23.71595991386752, + close: 23.71595991386752, + }, + { + time: { year: 2018, month: 10, day: 11 }, + open: 26.14124249813686, + high: 29.5638477987916, + low: 20.82341105699825, + close: 25.563138238511257, + }, + { + time: { year: 2018, month: 10, day: 12 }, + open: 22.26412127509447, + high: 27.637685003390743, + low: 20.838507431464958, + close: 22.450517792778047, + }, + { + time: { year: 2018, month: 10, day: 13 }, + open: 25.75099239090953, + high: 28.12000626118839, + low: 21.929748303510852, + close: 22.63015682488669, + }, + { + time: { year: 2018, month: 10, day: 14 }, + open: 25.428132591291497, + high: 25.999229490809693, + low: 22.266121337091555, + close: 23.51047528528147, + }, + { + time: { year: 2018, month: 10, day: 15 }, + open: 25.07416967939059, + high: 25.50535192500713, + low: 21.96666570325133, + close: 21.96666570325133, + }, + { + time: { year: 2018, month: 10, day: 16 }, + open: 24.957206161449307, + high: 26.679727314857256, + low: 20.196753994637245, + close: 21.523347810451863, + }, + { + time: { year: 2018, month: 10, day: 17 }, + open: 23.705184745772733, + high: 26.754094837621004, + low: 18.724184302695104, + close: 20.160857555541725, + }, + { + time: { year: 2018, month: 10, day: 18 }, + open: 21.95610851644136, + high: 22.914889536420105, + low: 19.567733140100472, + close: 22.914889536420105, + }, + { + time: { year: 2018, month: 10, day: 19 }, + open: 23.216357873687972, + high: 25.44815512734246, + low: 19.54787451276509, + close: 20.76851802225937, + }, + { + time: { year: 2018, month: 10, day: 20 }, + open: 19.6289025950405, + high: 24.290702755740412, + low: 19.041541929894358, + close: 22.48608548162324, + }, + { + time: { year: 2018, month: 10, day: 21 }, + open: 23.599000037544915, + high: 26.839019853462844, + low: 20.884129956680898, + close: 22.01878871761756, + }, + { + time: { year: 2018, month: 10, day: 22 }, + open: 24.618502768742008, + high: 28.00099352255492, + low: 23.061935629399088, + close: 23.061935629399088, + }, + { + time: { year: 2018, month: 10, day: 23 }, + open: 23.840701995876866, + high: 28.494382608429564, + low: 23.840701995876866, + close: 25.321841131665526, + }, + { + time: { year: 2018, month: 10, day: 24 }, + open: 27.764925733189372, + high: 31.05550601484776, + low: 22.810929726970702, + close: 30.02406259204889, + }, + { + time: { year: 2018, month: 10, day: 25 }, + open: 29.703149280184604, + high: 34.0185175501095, + low: 26.82967654698301, + close: 32.06834171351323, + }, + { + time: { year: 2018, month: 10, day: 26 }, + open: 29.0251492427822, + high: 36.89478162439007, + low: 28.3502671011196, + close: 32.822663125409356, + }, + { + time: { year: 2018, month: 10, day: 27 }, + open: 35.040777462643284, + high: 35.12524316379231, + low: 26.805156020579663, + close: 34.23626219571325, + }, + { + time: { year: 2018, month: 10, day: 28 }, + open: 31.21349419519032, + high: 35.73068910379853, + low: 31.064101813812698, + close: 34.75020857236565, + }, + { + time: { year: 2018, month: 10, day: 29 }, + open: 32.34914826794689, + high: 42.381605482695505, + low: 30.176750284055878, + close: 39.24138147444552, + }, + { + time: { year: 2018, month: 10, day: 30 }, + open: 38.84583808993371, + high: 41.75165839362154, + low: 33.37106955991806, + close: 35.93904098275507, + }, + { + time: { year: 2018, month: 10, day: 31 }, + open: 37.070183005323564, + high: 44.84460203857022, + low: 35.23671284121251, + close: 36.329972003600034, + }, + { + time: { year: 2018, month: 11, day: 1 }, + open: 43.31997309164893, + high: 48.43216497187469, + low: 38.30881963355285, + close: 41.554948540677586, + }, + { + time: { year: 2018, month: 11, day: 2 }, + open: 41.33946811092929, + high: 46.65347243834853, + low: 37.472215586661335, + close: 39.26832265482503, + }, + { + time: { year: 2018, month: 11, day: 3 }, + open: 44.76468593661226, + high: 44.76468593661226, + low: 40.039672147314235, + close: 43.42106786288436, + }, + { + time: { year: 2018, month: 11, day: 4 }, + open: 49.13160326887013, + high: 49.13160326887013, + low: 40.93648693038296, + close: 42.17817698294767, + }, + { + time: { year: 2018, month: 11, day: 5 }, + open: 50.46706012970579, + high: 54.38104598422352, + low: 38.159930155343616, + close: 47.5899156640143, + }, + { + time: { year: 2018, month: 11, day: 6 }, + open: 48.25899506613569, + high: 48.25899506613569, + low: 45.63208604138365, + close: 45.63208604138365, + }, + { + time: { year: 2018, month: 11, day: 7 }, + open: 52.45484210527629, + high: 57.55979771849961, + low: 45.23447676016779, + close: 46.01127464234881, + }, + { + time: { year: 2018, month: 11, day: 8 }, + open: 53.228216675179624, + high: 54.07804814570622, + low: 40.61161433961706, + close: 47.689867390699014, + }, + { + time: { year: 2018, month: 11, day: 9 }, + open: 46.193099316212816, + high: 56.190537353078824, + low: 45.01246323828753, + close: 49.14012661656766, + }, + { + time: { year: 2018, month: 11, day: 10 }, + open: 50.409245396927986, + high: 52.3082002787041, + low: 41.764144138886394, + close: 52.3082002787041, + }, + { + time: { year: 2018, month: 11, day: 11 }, + open: 48.58146178816203, + high: 52.653922195022126, + low: 47.34031788474959, + close: 47.34031788474959, + }, + { + time: { year: 2018, month: 11, day: 12 }, + open: 46.80040325283692, + high: 56.709349494076804, + low: 45.81605691554122, + close: 45.81605691554122, + }, + { + time: { year: 2018, month: 11, day: 13 }, + open: 46.042722425788355, + high: 58.476056411825695, + low: 46.042722425788355, + close: 51.2300776481609, + }, + { + time: { year: 2018, month: 11, day: 14 }, + open: 53.909068487588385, + high: 60.240990154306715, + low: 45.230741063278664, + close: 51.34529637385427, + }, + { + time: { year: 2018, month: 11, day: 15 }, + open: 53.739609857086606, + high: 53.739609857086606, + low: 44.38017019990068, + close: 47.595960698697894, + }, + { + time: { year: 2018, month: 11, day: 16 }, + open: 52.52688238296145, + high: 60.9220040817774, + low: 44.27700764117003, + close: 55.27309771985698, + }, + { + time: { year: 2018, month: 11, day: 17 }, + open: 54.46100795908005, + high: 57.57937841117058, + low: 49.50543170388487, + close: 49.50543170388487, + }, + { + time: { year: 2018, month: 11, day: 18 }, + open: 51.12284024600029, + high: 57.646718858433026, + low: 48.73280269653226, + close: 51.35457902694444, + }, + { + time: { year: 2018, month: 11, day: 19 }, + open: 53.536130807863266, + high: 53.536130807863266, + low: 51.29649965636722, + close: 52.99088526565045, + }, + { + time: { year: 2018, month: 11, day: 20 }, + open: 50.92761950009885, + high: 57.70671943558014, + low: 46.45030483558741, + close: 52.229112575743066, + }, + { + time: { year: 2018, month: 11, day: 21 }, + open: 49.30035068900293, + high: 58.67691694734525, + low: 44.63563165197862, + close: 58.67691694734525, + }, + { + time: { year: 2018, month: 11, day: 22 }, + open: 54.230476484061036, + high: 59.03831193868438, + low: 50.77849134047791, + close: 59.03831193868438, + }, + { + time: { year: 2018, month: 11, day: 23 }, + open: 57.282420985156854, + high: 60.4869735007396, + low: 44.14116488798797, + close: 57.93461310007337, + }, + { + time: { year: 2018, month: 11, day: 24 }, + open: 54.86833150125539, + high: 64.25102812467448, + low: 52.36616043331222, + close: 52.36616043331222, + }, + { + time: { year: 2018, month: 11, day: 25 }, + open: 51.689239380620386, + high: 64.29747922654688, + low: 50.71498529572432, + close: 60.518206306602394, + }, + { + time: { year: 2018, month: 11, day: 26 }, + open: 55.74863310659164, + high: 60.816819055612584, + low: 46.11238607935206, + close: 59.23044859881929, + }, + { + time: { year: 2018, month: 11, day: 27 }, + open: 52.57406222528308, + high: 64.2058753841427, + low: 48.163404012323305, + close: 60.593847809696896, + }, + { + time: { year: 2018, month: 11, day: 28 }, + open: 57.50710740029724, + high: 60.12123058977347, + low: 49.61839271711267, + close: 53.29152711098895, + }, + { + time: { year: 2018, month: 11, day: 29 }, + open: 57.33581828303538, + high: 58.92432332528284, + low: 53.27790061455899, + close: 57.02787118731709, + }, + { + time: { year: 2018, month: 11, day: 30 }, + open: 57.527445314328595, + high: 67.63249690962569, + low: 49.603261485289146, + close: 54.589123556483656, + }, + { + time: { year: 2018, month: 12, day: 1 }, + open: 59.98835793934424, + high: 65.51917884840141, + low: 52.32535994476165, + close: 62.127135611086565, + }, + { + time: { year: 2018, month: 12, day: 2 }, + open: 52.509550731662536, + high: 58.49971806419494, + low: 52.509550731662536, + close: 54.759948868082255, + }, + { + time: { year: 2018, month: 12, day: 3 }, + open: 58.08470541982317, + high: 62.74987556918568, + low: 47.85627992158991, + close: 58.690428071336406, + }, + { + time: { year: 2018, month: 12, day: 4 }, + open: 58.28482939034761, + high: 69.16675825892361, + low: 57.41588944088662, + close: 57.74515245619454, + }, + { + time: { year: 2018, month: 12, day: 5 }, + open: 60.004299871302464, + high: 65.82447121605708, + low: 53.13330527599658, + close: 57.64488004774012, + }, + { + time: { year: 2018, month: 12, day: 6 }, + open: 61.92746155137417, + high: 64.36944842979646, + low: 49.470442234694225, + close: 59.94404434023895, + }, + { + time: { year: 2018, month: 12, day: 7 }, + open: 63.72235832229121, + high: 66.33649390307095, + low: 49.91822946887207, + close: 63.56396375320479, + }, + { + time: { year: 2018, month: 12, day: 8 }, + open: 56.64594047326664, + high: 65.3730920902599, + low: 52.604389283975664, + close: 60.71684658387917, + }, + { + time: { year: 2018, month: 12, day: 9 }, + open: 58.89798885700999, + high: 68.04578543284373, + low: 58.89798885700999, + close: 63.36111469854223, + }, + { + time: { year: 2018, month: 12, day: 10 }, + open: 58.869685789579826, + high: 70.99828637845869, + low: 52.36901833289119, + close: 63.15473262144694, + }, + { + time: { year: 2018, month: 12, day: 11 }, + open: 57.61362492091653, + high: 66.41975632948531, + low: 50.827182111530895, + close: 61.770769489947064, + }, + { + time: { year: 2018, month: 12, day: 12 }, + open: 57.869332957269656, + high: 66.28374056429257, + low: 57.05028878520954, + close: 63.87762958979595, + }, + { + time: { year: 2018, month: 12, day: 13 }, + open: 68.14347595614306, + high: 73.46304446829079, + low: 50.83319311788897, + close: 66.9144140431443, + }, + { + time: { year: 2018, month: 12, day: 14 }, + open: 56.95907344942102, + high: 68.81432823196859, + low: 56.95907344942102, + close: 60.69722290026252, + }, + { + time: { year: 2018, month: 12, day: 15 }, + open: 69.14662166493828, + high: 69.14662166493828, + low: 58.59143795311565, + close: 66.25235616866007, + }, + { + time: { year: 2018, month: 12, day: 16 }, + open: 64.0373004661208, + high: 72.91321850066319, + low: 52.079104978168345, + close: 65.92678310822487, + }, + { + time: { year: 2018, month: 12, day: 17 }, + open: 68.81814300123497, + high: 69.51927964796873, + low: 62.70935477415118, + close: 65.64565364397754, + }, + { + time: { year: 2018, month: 12, day: 18 }, + open: 63.47554821643351, + high: 73.6284398311906, + low: 58.996882824636856, + close: 58.996882824636856, + }, + { + time: { year: 2018, month: 12, day: 19 }, + open: 69.97765183896102, + high: 69.97765183896102, + low: 58.73355952507237, + close: 58.73355952507237, + }, + { + time: { year: 2018, month: 12, day: 20 }, + open: 63.22638756186111, + high: 65.67137242291682, + low: 59.9542779777421, + close: 61.20003065016431, + }, + { + time: { year: 2018, month: 12, day: 21 }, + open: 59.690029086102506, + high: 78.08665559197297, + low: 54.862707942292275, + close: 70.58935191024504, + }, + { + time: { year: 2018, month: 12, day: 22 }, + open: 66.29092355620301, + high: 71.82667261213395, + low: 65.28001993201676, + close: 71.82667261213395, + }, + { + time: { year: 2018, month: 12, day: 23 }, + open: 60.92645998120027, + high: 74.21283998861118, + low: 57.331119016099116, + close: 60.36728842356329, + }, + { + time: { year: 2018, month: 12, day: 24 }, + open: 60.211957192084036, + high: 72.37883919241614, + low: 60.211957192084036, + close: 72.37883919241614, + }, + { + time: { year: 2018, month: 12, day: 25 }, + open: 64.80282266865653, + high: 71.00204457933133, + low: 54.58446926152339, + close: 69.9468262738086, + }, + { + time: { year: 2018, month: 12, day: 26 }, + open: 66.28091239894763, + high: 81.00843300529249, + low: 54.56212171317677, + close: 69.58528111643206, + }, + { + time: { year: 2018, month: 12, day: 27 }, + open: 66.38479296949795, + high: 79.97207476893692, + low: 59.738742243860464, + close: 73.77893045661807, + }, + { + time: { year: 2018, month: 12, day: 28 }, + open: 73.80105714462456, + high: 73.80105714462456, + low: 59.95172576316864, + close: 73.49823170047799, + }, + { + time: { year: 2018, month: 12, day: 29 }, + open: 75.65816205696441, + high: 75.65816205696441, + low: 63.710206287837266, + close: 63.710206287837266, + }, + { + time: { year: 2018, month: 12, day: 30 }, + open: 70.43199072631421, + high: 80.48229715762909, + low: 62.65542750589909, + close: 63.42588929424237, + }, + { + time: { year: 2018, month: 12, day: 31 }, + open: 74.18101512382138, + high: 79.0918171034821, + low: 57.80109358134577, + close: 72.91361896511863, + }, + // hide-end +]; +series.setData(data); + +// determining the dates for the 'buy' and 'sell' markers added below. +const datesForMarkers = [data[data.length - 39], data[data.length - 19]]; +let indexOfMinPrice = 0; +for (let i = 1; i < datesForMarkers.length; i++) { + if (datesForMarkers[i].high < datesForMarkers[indexOfMinPrice].high) { + indexOfMinPrice = i; + } +} + +// highlight-start +const markers = [ + { + time: data[data.length - 48].time, + position: 'aboveBar', + color: '#f68410', + shape: 'circle', + text: 'D', + }, +]; +for (let i = 0; i < datesForMarkers.length; i++) { + if (i !== indexOfMinPrice) { + markers.push({ + time: datesForMarkers[i].time, + position: 'aboveBar', + color: '#e91e63', + shape: 'arrowDown', + text: 'Sell @ ' + Math.floor(datesForMarkers[i].high + 2), + }); + } else { + markers.push({ + time: datesForMarkers[i].time, + position: 'belowBar', + color: '#2196F3', + shape: 'arrowUp', + text: 'Buy @ ' + Math.floor(datesForMarkers[i].low - 2), + }); + } +} +series.setMarkers(markers); +// highlight-end + +chart.timeScale().fitContent(); diff --git a/website/tutorials/how_to/series-markers.mdx b/website/tutorials/how_to/series-markers.mdx new file mode 100644 index 0000000000..96c3830aad --- /dev/null +++ b/website/tutorials/how_to/series-markers.mdx @@ -0,0 +1,64 @@ +--- +title: Add Series Markers +sidebar_label: Series Markers +description: An example of how to add markers to a series on the chart. +pagination_prev: null +pagination_next: null +keywords: + - series + - markers +--- + +A series marker is an annotation which can be drawn on the chart at a specific point. +It can be used to draw attention to specific events within the data set. +This example shows how to add series markers to your chart. + +## Short answer + +You can add markers to a series by passing an array of [`seriesMarker`](/docs/api/interfaces/SeriesMarker) +objects to the [`setMarkers`](/docs/api/interfaces/ISeriesApi#setmarkers) method on +an [`ISeriesApi`](/docs/api/interfaces/ISeriesApi) instance. + +```js +const markers = [ + { + time: { year: 2018, month: 12, day: 23 }, + position: 'aboveBar', + color: '#f68410', + shape: 'circle', + text: 'A', + }, +]; +series.setMarkers(markers); +``` + +You can see a full [working example](#full-example) below. + +## Further information + +A series marker is an annotation which can be attached to a specific data point within a series. +We don't need to specify a vertical price value but rather only the `time` property since the +marker will determine it's vertical position from the data points values (such as `high` and +`low` in the case of candlestick data) and the specified `position` property ([SeriesMarkerPosition](/docs/api#seriesmarkerposition)). + +## Resources + +You can view the related APIs here: +- [SeriesMarker](/docs/api/interfaces/SeriesMarker) - Series Marker interface. +- [SeriesMarkerPosition](/docs/api#seriesmarkerposition) - Positions that can be set for the marker. +- [SeriesMarkerShape](/docs/api#seriesmarkershape) - Shapes that can be set for the marker. +- [setMarkers](/docs/api/interfaces/ISeriesApi#setmarkers) - Method for adding markers to a series. +- [Time Types](/docs/api#time) - Different time formats available to use. + +## Full example + +import UsageGuidePartial from "./_usage-guide-partial.mdx"; + + + +import CodeBlock from "@theme/CodeBlock"; +import code from "!!raw-loader!./series-markers.js"; + + + {code} + diff --git a/website/tutorials/how_to/tooltip-floating.js b/website/tutorials/how_to/tooltip-floating.js new file mode 100644 index 0000000000..9156b6dd13 --- /dev/null +++ b/website/tutorials/how_to/tooltip-floating.js @@ -0,0 +1,275 @@ +// remove-start +// Lightweight Charts Example: Floating Tooltip +// https://tradingview.github.io/lightweight-charts/tutorials/how_to/tooltips + +// remove-end +const chartOptions = { + layout: { + textColor: CHART_TEXT_COLOR, + background: { type: 'solid', color: CHART_BACKGROUND_COLOR }, + }, +}; +// remove-line +/** @type {import('lightweight-charts').IChartApi} */ +const chart = createChart(document.getElementById('container'), chartOptions); + +chart.applyOptions({ + rightPriceScale: { + scaleMargins: { + top: 0.3, // leave some space for the legend + bottom: 0.25, + }, + }, + crosshair: { + // hide the horizontal crosshair line + horzLine: { + visible: false, + labelVisible: false, + }, + // hide the vertical crosshair label + vertLine: { + labelVisible: false, + }, + }, + // hide the grid lines + grid: { + vertLines: { + visible: false, + }, + horzLines: { + visible: false, + }, + }, +}); + +const series = chart.addAreaSeries({ + topColor: AREA_TOP_COLOR, + bottomColor: AREA_BOTTOM_COLOR, + lineColor: LINE_LINE_COLOR, + lineWidth: 2, + crossHairMarkerVisible: false, +}); + +series.setData([ + { time: '2018-10-19', value: 26.19 }, + // hide-start + { time: '2018-10-22', value: 25.87 }, + { time: '2018-10-23', value: 25.83 }, + { time: '2018-10-24', value: 25.78 }, + { time: '2018-10-25', value: 25.82 }, + { time: '2018-10-26', value: 25.81 }, + { time: '2018-10-29', value: 25.82 }, + { time: '2018-10-30', value: 25.71 }, + { time: '2018-10-31', value: 25.82 }, + { time: '2018-11-01', value: 25.72 }, + { time: '2018-11-02', value: 25.74 }, + { time: '2018-11-05', value: 25.81 }, + { time: '2018-11-06', value: 25.75 }, + { time: '2018-11-07', value: 25.73 }, + { time: '2018-11-08', value: 25.75 }, + { time: '2018-11-09', value: 25.75 }, + { time: '2018-11-12', value: 25.76 }, + { time: '2018-11-13', value: 25.8 }, + { time: '2018-11-14', value: 25.77 }, + { time: '2018-11-15', value: 25.75 }, + { time: '2018-11-16', value: 25.75 }, + { time: '2018-11-19', value: 25.75 }, + { time: '2018-11-20', value: 25.72 }, + { time: '2018-11-21', value: 25.78 }, + { time: '2018-11-23', value: 25.72 }, + { time: '2018-11-26', value: 25.78 }, + { time: '2018-11-27', value: 25.85 }, + { time: '2018-11-28', value: 25.85 }, + { time: '2018-11-29', value: 25.55 }, + { time: '2018-11-30', value: 25.41 }, + { time: '2018-12-03', value: 25.41 }, + { time: '2018-12-04', value: 25.42 }, + { time: '2018-12-06', value: 25.33 }, + { time: '2018-12-07', value: 25.39 }, + { time: '2018-12-10', value: 25.32 }, + { time: '2018-12-11', value: 25.48 }, + { time: '2018-12-12', value: 25.39 }, + { time: '2018-12-13', value: 25.45 }, + { time: '2018-12-14', value: 25.52 }, + { time: '2018-12-17', value: 25.38 }, + { time: '2018-12-18', value: 25.36 }, + { time: '2018-12-19', value: 25.65 }, + { time: '2018-12-20', value: 25.7 }, + { time: '2018-12-21', value: 25.66 }, + { time: '2018-12-24', value: 25.66 }, + { time: '2018-12-26', value: 25.65 }, + { time: '2018-12-27', value: 25.66 }, + { time: '2018-12-28', value: 25.68 }, + { time: '2018-12-31', value: 25.77 }, + { time: '2019-01-02', value: 25.72 }, + { time: '2019-01-03', value: 25.69 }, + { time: '2019-01-04', value: 25.71 }, + { time: '2019-01-07', value: 25.72 }, + { time: '2019-01-08', value: 25.72 }, + { time: '2019-01-09', value: 25.66 }, + { time: '2019-01-10', value: 25.85 }, + { time: '2019-01-11', value: 25.92 }, + { time: '2019-01-14', value: 25.94 }, + { time: '2019-01-15', value: 25.95 }, + { time: '2019-01-16', value: 26.0 }, + { time: '2019-01-17', value: 25.99 }, + { time: '2019-01-18', value: 25.6 }, + { time: '2019-01-22', value: 25.81 }, + { time: '2019-01-23', value: 25.7 }, + { time: '2019-01-24', value: 25.74 }, + { time: '2019-01-25', value: 25.8 }, + { time: '2019-01-28', value: 25.83 }, + { time: '2019-01-29', value: 25.7 }, + { time: '2019-01-30', value: 25.78 }, + { time: '2019-01-31', value: 25.35 }, + { time: '2019-02-01', value: 25.6 }, + { time: '2019-02-04', value: 25.65 }, + { time: '2019-02-05', value: 25.73 }, + { time: '2019-02-06', value: 25.71 }, + { time: '2019-02-07', value: 25.71 }, + { time: '2019-02-08', value: 25.72 }, + { time: '2019-02-11', value: 25.76 }, + { time: '2019-02-12', value: 25.84 }, + { time: '2019-02-13', value: 25.85 }, + { time: '2019-02-14', value: 25.87 }, + { time: '2019-02-15', value: 25.89 }, + { time: '2019-02-19', value: 25.9 }, + { time: '2019-02-20', value: 25.92 }, + { time: '2019-02-21', value: 25.96 }, + { time: '2019-02-22', value: 26.0 }, + { time: '2019-02-25', value: 25.93 }, + { time: '2019-02-26', value: 25.92 }, + { time: '2019-02-27', value: 25.67 }, + { time: '2019-02-28', value: 25.79 }, + { time: '2019-03-01', value: 25.86 }, + { time: '2019-03-04', value: 25.94 }, + { time: '2019-03-05', value: 26.02 }, + { time: '2019-03-06', value: 25.95 }, + { time: '2019-03-07', value: 25.89 }, + { time: '2019-03-08', value: 25.94 }, + { time: '2019-03-11', value: 25.91 }, + { time: '2019-03-12', value: 25.92 }, + { time: '2019-03-13', value: 26.0 }, + { time: '2019-03-14', value: 26.05 }, + { time: '2019-03-15', value: 26.11 }, + { time: '2019-03-18', value: 26.1 }, + { time: '2019-03-19', value: 25.98 }, + { time: '2019-03-20', value: 26.11 }, + { time: '2019-03-21', value: 26.12 }, + { time: '2019-03-22', value: 25.88 }, + { time: '2019-03-25', value: 25.85 }, + { time: '2019-03-26', value: 25.72 }, + { time: '2019-03-27', value: 25.73 }, + { time: '2019-03-28', value: 25.8 }, + { time: '2019-03-29', value: 25.77 }, + { time: '2019-04-01', value: 26.06 }, + { time: '2019-04-02', value: 25.93 }, + { time: '2019-04-03', value: 25.95 }, + { time: '2019-04-04', value: 26.06 }, + { time: '2019-04-05', value: 26.16 }, + { time: '2019-04-08', value: 26.12 }, + { time: '2019-04-09', value: 26.07 }, + { time: '2019-04-10', value: 26.13 }, + { time: '2019-04-11', value: 26.04 }, + { time: '2019-04-12', value: 26.04 }, + { time: '2019-04-15', value: 26.05 }, + { time: '2019-04-16', value: 26.01 }, + { time: '2019-04-17', value: 26.09 }, + { time: '2019-04-18', value: 26.0 }, + { time: '2019-04-22', value: 26.0 }, + { time: '2019-04-23', value: 26.06 }, + { time: '2019-04-24', value: 26.0 }, + { time: '2019-04-25', value: 25.81 }, + { time: '2019-04-26', value: 25.88 }, + { time: '2019-04-29', value: 25.91 }, + { time: '2019-04-30', value: 25.9 }, + { time: '2019-05-01', value: 26.02 }, + { time: '2019-05-02', value: 25.97 }, + { time: '2019-05-03', value: 26.02 }, + { time: '2019-05-06', value: 26.03 }, + { time: '2019-05-07', value: 26.04 }, + { time: '2019-05-08', value: 26.05 }, + { time: '2019-05-09', value: 26.05 }, + { time: '2019-05-10', value: 26.08 }, + { time: '2019-05-13', value: 26.05 }, + { time: '2019-05-14', value: 26.01 }, + { time: '2019-05-15', value: 26.03 }, + { time: '2019-05-16', value: 26.14 }, + { time: '2019-05-17', value: 26.09 }, + { time: '2019-05-20', value: 26.01 }, + { time: '2019-05-21', value: 26.12 }, + { time: '2019-05-22', value: 26.15 }, + { time: '2019-05-23', value: 26.18 }, + { time: '2019-05-24', value: 26.16 }, + { time: '2019-05-28', value: 26.23 }, + // hide-end +]); + +// const symbolName = 'ETC USD 7D VWAP'; + +const container = document.getElementById('container'); + +function dateToString(date) { + return `${date.year} - ${date.month} - ${date.day}`; +} + +const toolTipWidth = 80; +const toolTipHeight = 80; +const toolTipMargin = 15; + +// Create and style the tooltip html element +const toolTip = document.createElement('div'); +toolTip.style = `width: 96px; height: 80px; position: absolute; display: none; padding: 8px; box-sizing: border-box; font-size: 12px; text-align: left; z-index: 1000; top: 12px; left: 12px; pointer-events: none; border: 1px solid; border-radius: 2px;font-family: 'Trebuchet MS', Roboto, Ubuntu, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;`; +toolTip.style.background = CHART_BACKGROUND_COLOR; +toolTip.style.color = CHART_TEXT_COLOR; +toolTip.style.borderColor = LINE_LINE_COLOR; +container.appendChild(toolTip); + +// update tooltip +chart.subscribeCrosshairMove(param => { + if ( + param.point === undefined || + !param.time || + param.point.x < 0 || + param.point.x > container.clientWidth || + param.point.y < 0 || + param.point.y > container.clientHeight + ) { + toolTip.style.display = 'none'; + } else { + const dateStr = dateToString(param.time); + toolTip.style.display = 'block'; + const price = param.seriesPrices.get(series); + toolTip.innerHTML = `
Apple Inc.
+ ${Math.round(100 * price) / 100} +
+ ${dateStr} +
`; + + // highlight-start + const coordinate = series.priceToCoordinate(price); + let shiftedCoordinate = param.point.x - 50; + if (coordinate === null) { + return; + } + shiftedCoordinate = Math.max( + 0, + Math.min(container.clientWidth - toolTipWidth, shiftedCoordinate) + ); + const coordinateY = + coordinate - toolTipHeight - toolTipMargin > 0 + ? coordinate - toolTipHeight - toolTipMargin + : Math.max( + 0, + Math.min( + container.clientHeight - toolTipHeight - toolTipMargin, + coordinate + toolTipMargin + ) + ); + toolTip.style.left = shiftedCoordinate + 'px'; + toolTip.style.top = coordinateY + 'px'; + // highlight-end + } +}); + +chart.timeScale().fitContent(); diff --git a/website/tutorials/how_to/tooltip-magnifier.js b/website/tutorials/how_to/tooltip-magnifier.js new file mode 100644 index 0000000000..25c2e5ecd1 --- /dev/null +++ b/website/tutorials/how_to/tooltip-magnifier.js @@ -0,0 +1,426 @@ +// remove-start +// Lightweight Charts Example: Magnifier Tooltip +// https://tradingview.github.io/lightweight-charts/tutorials/how_to/tooltips + +// remove-end +const chartOptions = { + layout: { + textColor: CHART_TEXT_COLOR, + background: { type: 'solid', color: CHART_BACKGROUND_COLOR }, + }, +}; +// remove-line +/** @type {import('lightweight-charts').IChartApi} */ +const chart = createChart(document.getElementById('container'), chartOptions); + +chart.applyOptions({ + leftPriceScale: { + scaleMargins: { + top: 0.3, // leave some space for the legend + bottom: 0.25, + }, + visible: true, + borderVisible: false, + }, + rightPriceScale: { + visible: false, + }, + timeScale: { + borderVisible: false, + }, + crosshair: { + horzLine: { + visible: false, + labelVisible: false, + }, + vertLine: { + visible: true, + style: 0, + width: 2, + color: 'rgba(32, 38, 46, 0.1)', + labelVisible: false, + }, + }, + // hide the grid lines + grid: { + vertLines: { + visible: false, + }, + horzLines: { + visible: false, + }, + }, +}); + +const series = chart.addAreaSeries({ + topColor: BASELINE_BOTTOM_FILL_COLOR1, + bottomColor: BASELINE_BOTTOM_FILL_COLOR2, + lineColor: BASELINE_BOTTOM_LINE_COLOR, + lineWidth: 2, + crossHairMarkerVisible: false, + priceLineVisible: false, + lastValueVisible: false, +}); + +series.setData([ + { time: '2018-03-28', value: 154 }, + // hide-start + { time: '2018-03-29', value: 154.2 }, + { time: '2018-03-30', value: 155.60001 }, + { time: '2018-04-02', value: 156.25 }, + { time: '2018-04-03', value: 156.25 }, + { time: '2018-04-04', value: 156.05 }, + { time: '2018-04-05', value: 158.05 }, + { time: '2018-04-06', value: 157.3 }, + { time: '2018-04-09', value: 144 }, + { time: '2018-04-10', value: 144.8 }, + { time: '2018-04-11', value: 143.5 }, + { time: '2018-04-12', value: 147.05 }, + { time: '2018-04-13', value: 144.85001 }, + { time: '2018-04-16', value: 145.39999 }, + { time: '2018-04-17', value: 147.3 }, + { time: '2018-04-18', value: 153.55 }, + { time: '2018-04-19', value: 150.95 }, + { time: '2018-04-20', value: 149.39999 }, + { time: '2018-04-23', value: 148.5 }, + { time: '2018-04-24', value: 146.60001 }, + { time: '2018-04-25', value: 144.45 }, + { time: '2018-04-26', value: 145.60001 }, + { time: '2018-04-27', value: 144.3 }, + { time: '2018-04-30', value: 144 }, + { time: '2018-05-02', value: 147.3 }, + { time: '2018-05-03', value: 144.2 }, + { time: '2018-05-04', value: 141.64999 }, + { time: '2018-05-07', value: 141.89999 }, + { time: '2018-05-08', value: 140.39999 }, + { time: '2018-05-10', value: 139.8 }, + { time: '2018-05-11', value: 137.5 }, + { time: '2018-05-14', value: 136.14999 }, + { time: '2018-05-15', value: 138 }, + { time: '2018-05-16', value: 137.95 }, + { time: '2018-05-17', value: 137.95 }, + { time: '2018-05-18', value: 136.75 }, + { time: '2018-05-21', value: 136.2 }, + { time: '2018-05-22', value: 135 }, + { time: '2018-05-23', value: 132.55 }, + { time: '2018-05-24', value: 132.7 }, + { time: '2018-05-25', value: 132.2 }, + { time: '2018-05-28', value: 131.55 }, + { time: '2018-05-29', value: 131.85001 }, + { time: '2018-05-30', value: 139.85001 }, + { time: '2018-05-31', value: 140.8 }, + { time: '2018-06-01', value: 139.64999 }, + { time: '2018-06-04', value: 137.10001 }, + { time: '2018-06-05', value: 139.25 }, + { time: '2018-06-06', value: 141.3 }, + { time: '2018-06-07', value: 145 }, + { time: '2018-06-08', value: 143.89999 }, + { time: '2018-06-11', value: 142.7 }, + { time: '2018-06-13', value: 144.05 }, + { time: '2018-06-14', value: 143.55 }, + { time: '2018-06-15', value: 140.5 }, + { time: '2018-06-18', value: 139.64999 }, + { time: '2018-06-19', value: 138 }, + { time: '2018-06-20', value: 141 }, + { time: '2018-06-21', value: 140.55 }, + { time: '2018-06-22', value: 140.55 }, + { time: '2018-06-25', value: 140.75 }, + { time: '2018-06-26', value: 140.64999 }, + { time: '2018-06-27', value: 141.14999 }, + { time: '2018-06-28', value: 139.8 }, + { time: '2018-06-29', value: 139.8 }, + { time: '2018-07-02', value: 140.14999 }, + { time: '2018-07-03', value: 143.05 }, + { time: '2018-07-04', value: 140 }, + { time: '2018-07-05', value: 129.2 }, + { time: '2018-07-06', value: 129.55 }, + { time: '2018-07-09', value: 128.3 }, + { time: '2018-07-10', value: 126.55 }, + { time: '2018-07-11', value: 124.3 }, + { time: '2018-07-12', value: 124.8 }, + { time: '2018-07-13', value: 123.25 }, + { time: '2018-07-16', value: 123 }, + { time: '2018-07-17', value: 124.25 }, + { time: '2018-07-18', value: 123 }, + { time: '2018-07-19', value: 121 }, + { time: '2018-07-20', value: 121.45 }, + { time: '2018-07-23', value: 123.8 }, + { time: '2018-07-24', value: 122.15 }, + { time: '2018-07-25', value: 121.3 }, + { time: '2018-07-26', value: 122.7 }, + { time: '2018-07-27', value: 122.2 }, + { time: '2018-07-30', value: 121.7 }, + { time: '2018-07-31', value: 122.95 }, + { time: '2018-08-01', value: 121 }, + { time: '2018-08-02', value: 116 }, + { time: '2018-08-03', value: 118.1 }, + { time: '2018-08-06', value: 114.3 }, + { time: '2018-08-07', value: 114.25 }, + { time: '2018-08-08', value: 111.85 }, + { time: '2018-08-09', value: 109.7 }, + { time: '2018-08-10', value: 105 }, + { time: '2018-08-13', value: 105.75 }, + { time: '2018-08-14', value: 107.25 }, + { time: '2018-08-15', value: 107.4 }, + { time: '2018-08-16', value: 110.5 }, + { time: '2018-08-17', value: 109 }, + { time: '2018-08-20', value: 108.95 }, + { time: '2018-08-21', value: 108.35 }, + { time: '2018-08-22', value: 108.6 }, + { time: '2018-08-23', value: 105.65 }, + { time: '2018-08-24', value: 104.45 }, + { time: '2018-08-27', value: 106.2 }, + { time: '2018-08-28', value: 106.55 }, + { time: '2018-08-29', value: 111.8 }, + { time: '2018-08-30', value: 115.5 }, + { time: '2018-08-31', value: 115.5 }, + { time: '2018-09-03', value: 114.55 }, + { time: '2018-09-04', value: 112.75 }, + { time: '2018-09-05', value: 111.5 }, + { time: '2018-09-06', value: 108.1 }, + { time: '2018-09-07', value: 108.55 }, + { time: '2018-09-10', value: 106.5 }, + { time: '2018-09-11', value: 105.1 }, + { time: '2018-09-12', value: 107.2 }, + { time: '2018-09-13', value: 107.1 }, + { time: '2018-09-14', value: 105.75 }, + { time: '2018-09-17', value: 106.05 }, + { time: '2018-09-18', value: 109.15 }, + { time: '2018-09-19', value: 111.4 }, + { time: '2018-09-20', value: 111 }, + { time: '2018-09-21', value: 111 }, + { time: '2018-09-24', value: 108.95 }, + { time: '2018-09-25', value: 106.65 }, + { time: '2018-09-26', value: 106.7 }, + { time: '2018-09-27', value: 107.05 }, + { time: '2018-09-28', value: 106.55 }, + { time: '2018-10-01', value: 106.2 }, + { time: '2018-10-02', value: 106.4 }, + { time: '2018-10-03', value: 107.1 }, + { time: '2018-10-04', value: 106.4 }, + { time: '2018-10-05', value: 104.65 }, + { time: '2018-10-08', value: 102.65 }, + { time: '2018-10-09', value: 102.1 }, + { time: '2018-10-10', value: 102.15 }, + { time: '2018-10-11', value: 100.9 }, + { time: '2018-10-12', value: 102 }, + { time: '2018-10-15', value: 100.15 }, + { time: '2018-10-16', value: 99 }, + { time: '2018-10-17', value: 98 }, + { time: '2018-10-18', value: 96 }, + { time: '2018-10-19', value: 95.5 }, + { time: '2018-10-22', value: 92.400002 }, + { time: '2018-10-23', value: 92.300003 }, + { time: '2018-10-24', value: 92.900002 }, + { time: '2018-10-25', value: 91.849998 }, + { time: '2018-10-26', value: 91.599998 }, + { time: '2018-10-29', value: 94.050003 }, + { time: '2018-10-30', value: 98.25 }, + { time: '2018-10-31', value: 97.25 }, + { time: '2018-11-01', value: 96.879997 }, + { time: '2018-11-02', value: 101.78 }, + { time: '2018-11-06', value: 102.4 }, + { time: '2018-11-07', value: 100.6 }, + { time: '2018-11-08', value: 98.5 }, + { time: '2018-11-09', value: 95.139999 }, + { time: '2018-11-12', value: 96.900002 }, + { time: '2018-11-13', value: 97.400002 }, + { time: '2018-11-14', value: 103.3 }, + { time: '2018-11-15', value: 102.74 }, + { time: '2018-11-16', value: 101.2 }, + { time: '2018-11-19', value: 98.720001 }, + { time: '2018-11-20', value: 102.2 }, + { time: '2018-11-21', value: 108.8 }, + { time: '2018-11-22', value: 109.9 }, + { time: '2018-11-23', value: 113.74 }, + { time: '2018-11-26', value: 110.9 }, + { time: '2018-11-27', value: 113.28 }, + { time: '2018-11-28', value: 113.6 }, + { time: '2018-11-29', value: 113.14 }, + { time: '2018-11-30', value: 114.4 }, + { time: '2018-12-03', value: 111.84 }, + { time: '2018-12-04', value: 106.7 }, + { time: '2018-12-05', value: 107.8 }, + { time: '2018-12-06', value: 106.44 }, + { time: '2018-12-07', value: 103.7 }, + { time: '2018-12-10', value: 101.02 }, + { time: '2018-12-11', value: 102.72 }, + { time: '2018-12-12', value: 101.8 }, + { time: '2018-12-13', value: 102 }, + { time: '2018-12-14', value: 102.1 }, + { time: '2018-12-17', value: 101.04 }, + { time: '2018-12-18', value: 101.6 }, + { time: '2018-12-19', value: 102 }, + { time: '2018-12-20', value: 102.02 }, + { time: '2018-12-21', value: 102.2 }, + { time: '2018-12-24', value: 102.1 }, + { time: '2018-12-25', value: 100.8 }, + { time: '2018-12-26', value: 101.02 }, + { time: '2018-12-27', value: 100.5 }, + { time: '2018-12-28', value: 101 }, + { time: '2019-01-03', value: 101.5 }, + { time: '2019-01-04', value: 101.1 }, + { time: '2019-01-08', value: 101.1 }, + { time: '2019-01-09', value: 102.06 }, + { time: '2019-01-10', value: 105.14 }, + { time: '2019-01-11', value: 104.66 }, + { time: '2019-01-14', value: 106.22 }, + { time: '2019-01-15', value: 106.98 }, + { time: '2019-01-16', value: 108.4 }, + { time: '2019-01-17', value: 109.06 }, + { time: '2019-01-18', value: 107 }, + { time: '2019-01-21', value: 105.8 }, + { time: '2019-01-22', value: 105.24 }, + { time: '2019-01-23', value: 105.4 }, + { time: '2019-01-24', value: 105.38 }, + { time: '2019-01-25', value: 105.7 }, + { time: '2019-01-28', value: 105.8 }, + { time: '2019-01-29', value: 106.1 }, + { time: '2019-01-30', value: 108.6 }, + { time: '2019-01-31', value: 107.92 }, + { time: '2019-02-01', value: 105.22 }, + { time: '2019-02-04', value: 102.44 }, + { time: '2019-02-05', value: 102.26 }, + { time: '2019-02-06', value: 102 }, + { time: '2019-02-07', value: 101.62 }, + { time: '2019-02-08', value: 101.9 }, + { time: '2019-02-11', value: 101.78 }, + { time: '2019-02-12', value: 102.7 }, + { time: '2019-02-13', value: 100.14 }, + { time: '2019-02-14', value: 101.36 }, + { time: '2019-02-15', value: 101.62 }, + { time: '2019-02-18', value: 100.74 }, + { time: '2019-02-19', value: 100 }, + { time: '2019-02-20', value: 100.04 }, + { time: '2019-02-21', value: 100 }, + { time: '2019-02-22', value: 100.12 }, + { time: '2019-02-25', value: 100.04 }, + { time: '2019-02-26', value: 98.980003 }, + { time: '2019-02-27', value: 97.699997 }, + { time: '2019-02-28', value: 97.099998 }, + { time: '2019-03-01', value: 96.760002 }, + { time: '2019-03-04', value: 98.360001 }, + { time: '2019-03-05', value: 96.260002 }, + { time: '2019-03-06', value: 98.120003 }, + { time: '2019-03-07', value: 99.68 }, + { time: '2019-03-11', value: 102.1 }, + { time: '2019-03-12', value: 100.22 }, + { time: '2019-03-13', value: 100.5 }, + { time: '2019-03-14', value: 99.660004 }, + { time: '2019-03-15', value: 100.08 }, + { time: '2019-03-18', value: 99.919998 }, + { time: '2019-03-19', value: 99.459999 }, + { time: '2019-03-20', value: 98.220001 }, + { time: '2019-03-21', value: 97.300003 }, + { time: '2019-03-22', value: 97.660004 }, + { time: '2019-03-25', value: 97 }, + { time: '2019-03-26', value: 97.019997 }, + { time: '2019-03-27', value: 96.099998 }, + { time: '2019-03-28', value: 96.699997 }, + { time: '2019-03-29', value: 96.300003 }, + { time: '2019-04-01', value: 97.779999 }, + { time: '2019-04-02', value: 98.360001 }, + { time: '2019-04-03', value: 98.5 }, + { time: '2019-04-04', value: 98.360001 }, + { time: '2019-04-05', value: 99.540001 }, + { time: '2019-04-08', value: 98.580002 }, + { time: '2019-04-09', value: 97.239998 }, + { time: '2019-04-10', value: 97.32 }, + { time: '2019-04-11', value: 98.400002 }, + { time: '2019-04-12', value: 98.220001 }, + { time: '2019-04-15', value: 98.720001 }, + { time: '2019-04-16', value: 99.120003 }, + { time: '2019-04-17', value: 98.620003 }, + { time: '2019-04-18', value: 98 }, + { time: '2019-04-19', value: 97.599998 }, + { time: '2019-04-22', value: 97.599998 }, + { time: '2019-04-23', value: 96.800003 }, + { time: '2019-04-24', value: 96.32 }, + { time: '2019-04-25', value: 96.339996 }, + { time: '2019-04-26', value: 97.120003 }, + { time: '2019-04-29', value: 96.980003 }, + { time: '2019-04-30', value: 96.32 }, + { time: '2019-05-02', value: 96.860001 }, + { time: '2019-05-03', value: 96.699997 }, + { time: '2019-05-06', value: 94.940002 }, + { time: '2019-05-07', value: 94.5 }, + { time: '2019-05-08', value: 94.239998 }, + { time: '2019-05-10', value: 92.900002 }, + { time: '2019-05-13', value: 91.279999 }, + { time: '2019-05-14', value: 91.599998 }, + { time: '2019-05-15', value: 90.080002 }, + { time: '2019-05-16', value: 91.68 }, + { time: '2019-05-17', value: 91.959999 }, + { time: '2019-05-20', value: 91.080002 }, + { time: '2019-05-21', value: 90.760002 }, + { time: '2019-05-22', value: 91 }, + { time: '2019-05-23', value: 90.739998 }, + { time: '2019-05-24', value: 91 }, + { time: '2019-05-27', value: 91.199997 }, + { time: '2019-05-28', value: 90.68 }, + { time: '2019-05-29', value: 91.120003 }, + { time: '2019-05-30', value: 90.419998 }, + { time: '2019-05-31', value: 93.800003 }, + { time: '2019-06-03', value: 96.800003 }, + { time: '2019-06-04', value: 96.34 }, + { time: '2019-06-05', value: 95.94 }, + // hide-end +]); + +// const symbolName = 'ETC USD 7D VWAP'; + +const container = document.getElementById('container'); + +function dateToString(date) { + return `${date.year} - ${date.month} - ${date.day}`; +} + +const toolTipWidth = 80; +const priceScaleWidth = 50; +const toolTipMargin = 15; + +// Create and style the tooltip html element +const toolTip = document.createElement('div'); +toolTip.style = `width: 96px; height: 300px; position: absolute; display: none; padding: 8px; box-sizing: border-box; font-size: 12px; text-align: left; z-index: 1000; top: 12px; left: 12px; pointer-events: none; border-radius: 4px 4px 0px 0px; border-bottom: none; box-shadow: 0 2px 5px 0 rgba(117, 134, 150, 0.45);font-family: 'Trebuchet MS', Roboto, Ubuntu, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;`; +toolTip.style.background = `rgba(${CHART_BACKGROUND_RGB_COLOR}, 0.25)`; +toolTip.style.color = CHART_TEXT_COLOR; +toolTip.style.borderColor = BASELINE_BOTTOM_LINE_COLOR; +container.appendChild(toolTip); + +// update tooltip +chart.subscribeCrosshairMove(param => { + if ( + param.point === undefined || + !param.time || + param.point.x < 0 || + param.point.x > container.clientWidth || + param.point.y < 0 || + param.point.y > container.clientHeight + ) { + toolTip.style.display = 'none'; + } else { + const dateStr = dateToString(param.time); + toolTip.style.display = 'block'; + const price = param.seriesPrices.get(series); + toolTip.innerHTML = `
⬤ ABC Inc.
+ ${Math.round(100 * price) / 100} +
+ ${dateStr} +
`; + + // highlight-start + let left = param.point.x; + + if (left > container.clientWidth - toolTipWidth - toolTipMargin) { + left = container.clientWidth - toolTipWidth; + } else if (left < toolTipWidth / 2) { + left = priceScaleWidth; + } + + toolTip.style.left = left + 'px'; + toolTip.style.top = 0 + 'px'; + // highlight-end + } +}); + +chart.timeScale().fitContent(); diff --git a/website/tutorials/how_to/tooltip-tracking.js b/website/tutorials/how_to/tooltip-tracking.js new file mode 100644 index 0000000000..c494a91982 --- /dev/null +++ b/website/tutorials/how_to/tooltip-tracking.js @@ -0,0 +1,266 @@ +// remove-start +// Lightweight Charts Example: Tracking Tooltip +// https://tradingview.github.io/lightweight-charts/tutorials/how_to/tooltips + +// remove-end +const chartOptions = { + layout: { + textColor: CHART_TEXT_COLOR, + background: { type: 'solid', color: CHART_BACKGROUND_COLOR }, + }, +}; +// remove-line +/** @type {import('lightweight-charts').IChartApi} */ +const chart = createChart(document.getElementById('container'), chartOptions); + +chart.applyOptions({ + rightPriceScale: { + scaleMargins: { + top: 0.3, // leave some space for the legend + bottom: 0.25, + }, + }, + crosshair: { + // hide the horizontal crosshair line + horzLine: { + visible: false, + labelVisible: false, + }, + // hide the vertical crosshair label + vertLine: { + labelVisible: false, + }, + }, + // hide the grid lines + grid: { + vertLines: { + visible: false, + }, + horzLines: { + visible: false, + }, + }, +}); + +const series = chart.addAreaSeries({ + topColor: BASELINE_TOP_FILL_COLOR1, + bottomColor: BASELINE_TOP_FILL_COLOR2, + lineColor: BASELINE_TOP_LINE_COLOR, + lineWidth: 2, + crossHairMarkerVisible: false, +}); + +series.setData([ + { time: '2016-07-18', value: 98.66 }, + // hide-start + { time: '2016-07-25', value: 104.21 }, + { time: '2016-08-01', value: 107.48 }, + { time: '2016-08-08', value: 108.18 }, + { time: '2016-08-15', value: 109.36 }, + { time: '2016-08-22', value: 106.94 }, + { time: '2016-08-29', value: 107.73 }, + { time: '2016-09-05', value: 103.13 }, + { time: '2016-09-12', value: 114.92 }, + { time: '2016-09-19', value: 112.71 }, + { time: '2016-09-26', value: 113.05 }, + { time: '2016-10-03', value: 114.06 }, + { time: '2016-10-10', value: 117.63 }, + { time: '2016-10-17', value: 116.6 }, + { time: '2016-10-24', value: 113.72 }, + { time: '2016-10-31', value: 108.84 }, + { time: '2016-11-07', value: 108.43 }, + { time: '2016-11-14', value: 110.06 }, + { time: '2016-11-21', value: 111.79 }, + { time: '2016-11-28', value: 109.9 }, + { time: '2016-12-05', value: 113.95 }, + { time: '2016-12-12', value: 115.97 }, + { time: '2016-12-19', value: 116.52 }, + { time: '2016-12-26', value: 115.82 }, + { time: '2017-01-02', value: 117.91 }, + { time: '2017-01-09', value: 119.04 }, + { time: '2017-01-16', value: 120.0 }, + { time: '2017-01-23', value: 121.95 }, + { time: '2017-01-30', value: 129.08 }, + { time: '2017-02-06', value: 132.12 }, + { time: '2017-02-13', value: 135.72 }, + { time: '2017-02-20', value: 136.66 }, + { time: '2017-02-27', value: 139.78 }, + { time: '2017-03-06', value: 139.14 }, + { time: '2017-03-13', value: 139.99 }, + { time: '2017-03-20', value: 140.64 }, + { time: '2017-03-27', value: 143.66 }, + { time: '2017-04-03', value: 143.34 }, + { time: '2017-04-10', value: 141.05 }, + { time: '2017-04-17', value: 142.27 }, + { time: '2017-04-24', value: 143.65 }, + { time: '2017-05-01', value: 148.96 }, + { time: '2017-05-08', value: 156.1 }, + { time: '2017-05-15', value: 153.06 }, + { time: '2017-05-22', value: 153.61 }, + { time: '2017-05-29', value: 155.45 }, + { time: '2017-06-05', value: 148.98 }, + { time: '2017-06-12', value: 142.27 }, + { time: '2017-06-19', value: 146.28 }, + { time: '2017-06-26', value: 144.02 }, + { time: '2017-07-03', value: 144.18 }, + { time: '2017-07-10', value: 149.04 }, + { time: '2017-07-17', value: 150.27 }, + { time: '2017-07-24', value: 149.5 }, + { time: '2017-07-31', value: 156.39 }, + { time: '2017-08-07', value: 157.48 }, + { time: '2017-08-14', value: 157.5 }, + { time: '2017-08-21', value: 159.86 }, + { time: '2017-08-28', value: 164.05 }, + { time: '2017-09-04', value: 158.63 }, + { time: '2017-09-11', value: 159.88 }, + { time: '2017-09-18', value: 151.89 }, + { time: '2017-09-25', value: 154.12 }, + { time: '2017-10-02', value: 155.3 }, + { time: '2017-10-09', value: 156.99 }, + { time: '2017-10-16', value: 156.25 }, + { time: '2017-10-23', value: 163.05 }, + { time: '2017-10-30', value: 172.5 }, + { time: '2017-11-06', value: 174.67 }, + { time: '2017-11-13', value: 170.15 }, + { time: '2017-11-20', value: 174.97 }, + { time: '2017-11-27', value: 171.05 }, + { time: '2017-12-04', value: 169.37 }, + { time: '2017-12-11', value: 173.97 }, + { time: '2017-12-18', value: 175.01 }, + { time: '2017-12-25', value: 169.23 }, + { time: '2018-01-01', value: 175.0 }, + { time: '2018-01-08', value: 177.09 }, + { time: '2018-01-15', value: 178.46 }, + { time: '2018-01-22', value: 171.51 }, + { time: '2018-01-29', value: 160.5 }, + { time: '2018-02-05', value: 156.41 }, + { time: '2018-02-12', value: 172.43 }, + { time: '2018-02-19', value: 175.5 }, + { time: '2018-02-26', value: 176.21 }, + { time: '2018-03-05', value: 179.98 }, + { time: '2018-03-12', value: 178.02 }, + { time: '2018-03-19', value: 164.94 }, + { time: '2018-03-26', value: 167.78 }, + { time: '2018-04-02', value: 168.38 }, + { time: '2018-04-09', value: 174.73 }, + { time: '2018-04-16', value: 165.72 }, + { time: '2018-04-23', value: 162.32 }, + { time: '2018-04-30', value: 183.83 }, + { time: '2018-05-07', value: 188.59 }, + { time: '2018-05-14', value: 186.31 }, + { time: '2018-05-21', value: 188.58 }, + { time: '2018-05-28', value: 190.24 }, + { time: '2018-06-04', value: 191.7 }, + { time: '2018-06-11', value: 188.84 }, + { time: '2018-06-18', value: 184.92 }, + { time: '2018-06-25', value: 185.11 }, + { time: '2018-07-02', value: 187.97 }, + { time: '2018-07-09', value: 191.33 }, + { time: '2018-07-16', value: 191.44 }, + { time: '2018-07-23', value: 190.98 }, + { time: '2018-07-30', value: 207.99 }, + { time: '2018-08-06', value: 207.53 }, + { time: '2018-08-13', value: 217.58 }, + { time: '2018-08-20', value: 216.16 }, + { time: '2018-08-27', value: 227.63 }, + { time: '2018-09-03', value: 221.3 }, + { time: '2018-09-10', value: 223.84 }, + { time: '2018-09-17', value: 217.66 }, + { time: '2018-09-24', value: 225.74 }, + { time: '2018-10-01', value: 224.29 }, + { time: '2018-10-08', value: 222.11 }, + { time: '2018-10-15', value: 219.31 }, + { time: '2018-10-22', value: 216.3 }, + { time: '2018-10-29', value: 207.48 }, + { time: '2018-11-05', value: 204.47 }, + { time: '2018-11-12', value: 193.53 }, + { time: '2018-11-19', value: 172.29 }, + { time: '2018-11-26', value: 178.58 }, + { time: '2018-12-03', value: 168.49 }, + { time: '2018-12-10', value: 165.48 }, + { time: '2018-12-17', value: 150.73 }, + { time: '2018-12-24', value: 156.23 }, + { time: '2018-12-31', value: 148.26 }, + { time: '2019-01-07', value: 152.29 }, + { time: '2019-01-14', value: 156.82 }, + { time: '2019-01-21', value: 157.76 }, + { time: '2019-01-28', value: 166.52 }, + { time: '2019-02-04', value: 170.41 }, + { time: '2019-02-11', value: 170.42 }, + { time: '2019-02-18', value: 172.97 }, + { time: '2019-02-25', value: 174.97 }, + { time: '2019-03-04', value: 172.91 }, + { time: '2019-03-11', value: 186.12 }, + { time: '2019-03-18', value: 191.05 }, + { time: '2019-03-25', value: 189.95 }, + { time: '2019-04-01', value: 197.0 }, + { time: '2019-04-08', value: 198.87 }, + { time: '2019-04-15', value: 203.86 }, + { time: '2019-04-22', value: 204.3 }, + { time: '2019-04-29', value: 211.75 }, + { time: '2019-05-06', value: 197.18 }, + { time: '2019-05-13', value: 189.0 }, + { time: '2019-05-20', value: 178.97 }, + { time: '2019-05-27', value: 179.03 }, + // hide-end +]); + +// const symbolName = 'ETC USD 7D VWAP'; + +const container = document.getElementById('container'); + +function dateToString(date) { + return `${date.year} - ${date.month} - ${date.day}`; +} + +const toolTipWidth = 80; +const toolTipHeight = 80; +const toolTipMargin = 15; + +// Create and style the tooltip html element +const toolTip = document.createElement('div'); +toolTip.style = `width: 96px; height: 80px; position: absolute; display: none; padding: 8px; box-sizing: border-box; font-size: 12px; text-align: left; z-index: 1000; top: 12px; left: 12px; pointer-events: none; border: 1px solid; border-radius: 2px;font-family: 'Trebuchet MS', Roboto, Ubuntu, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;`; +toolTip.style.background = CHART_BACKGROUND_COLOR; +toolTip.style.color = CHART_TEXT_COLOR; +toolTip.style.borderColor = BASELINE_TOP_LINE_COLOR; +container.appendChild(toolTip); + +// update tooltip +chart.subscribeCrosshairMove(param => { + if ( + param.point === undefined || + !param.time || + param.point.x < 0 || + param.point.x > container.clientWidth || + param.point.y < 0 || + param.point.y > container.clientHeight + ) { + toolTip.style.display = 'none'; + } else { + const dateStr = dateToString(param.time); + toolTip.style.display = 'block'; + const price = param.seriesPrices.get(series); + toolTip.innerHTML = `
ABC Inc.
+ ${Math.round(100 * price) / 100} +
+ ${dateStr} +
`; + + // highlight-start + const y = param.point.y; + let left = param.point.x + toolTipMargin; + if (left > container.clientWidth - toolTipWidth) { + left = param.point.x - toolTipMargin - toolTipWidth; + } + + let top = y + toolTipMargin; + if (top > container.clientHeight - toolTipHeight) { + top = y - toolTipHeight - toolTipMargin; + } + toolTip.style.left = left + 'px'; + toolTip.style.top = top + 'px'; + // highlight-end + } +}); + +chart.timeScale().fitContent(); diff --git a/website/tutorials/how_to/tooltips.mdx b/website/tutorials/how_to/tooltips.mdx new file mode 100644 index 0000000000..5b350dd75a --- /dev/null +++ b/website/tutorials/how_to/tooltips.mdx @@ -0,0 +1,122 @@ +--- +title: Tooltips +sidebar_label: Tooltips +description: Examples on how to implement a tooltip for your chart. +pagination_prev: null +pagination_next: null +keywords: + - example + - tooltip +--- + +Lightweight charts doesn't include a built-in tooltip feature, however it is something which can be added +to your chart by following the examples presented below. + +## How to + +In order to add a tooltip to the chart we need to create and position an `html` into the desired position above +the chart. We can then subscribe to the crosshairMove events ([subscribeCrosshairMove](/docs/api/interfaces/IChartApi#subscribecrosshairmove)) provided by the [`IChartApi`](/docs/api/interfaces/IChartApi) instance, and manually +update the content within our `html` tooltip element and change it's position. + +```js +chart.subscribeCrosshairMove(param => { + if ( + param.point === undefined || + !param.time || + param.point.x < 0 || + param.point.y < 0 + ) { + toolTip.style.display = 'none'; + } else { + const dateStr = dateToString(param.time); + toolTip.style.display = 'block'; + const price = param.seriesPrices.get(series); + toolTip.innerHTML = `
${price.toFixed(2)}
`; + + // Position tooltip according to mouse cursor position + toolTip.style.left = param.point.x + 'px'; + toolTip.style.top = param.point.y + 'px'; + } +}); +``` + +The process of creating the tooltip html element and positioning can be seen within the examples below. +Essentially, we create a new div element within the container div (holding the chart) and then position +and style it using `css`. + +You can see full [working examples](#examples) below. + +### Getting the mouse cursors position + +The parameter object ([MouseEventParams Interface](/docs/api/interfaces/MouseEventParams)) passed to the +crosshairMove handler function ([MouseEventhandler](/docs/api#mouseeventhandler)) contains a +[point](/docs/api/interfaces/Point) property which gives the current mouse cursor position relative to +the top left corner of the chart. + +### Getting the data points position + +It is possible to convert a price value into it's current vertical position on the chart by using +the [priceToCoordinate](/docs/api/interfaces/ISeriesApi#pricetocoordinate) method on the series' instance. +This along with the `param.point.x` can be used to determine the position of the data point. + +```js +chart.subscribeCrosshairMove(param => { + const x = param.point.x; + const price = param.seriesPrices.get(series); + // where series is the ISeriesApi instance that we are interested in. + const y = series.priceToCoordinate(price); + console.log(`The data point is at position: ${x}, ${y}`); +}); +``` + +## Resources + +- [subscribeCrosshairMove](/docs/api/interfaces/IChartApi#subscribecrosshairmove) +- [MouseEventParams Interface](/docs/api/interfaces/MouseEventParams) +- [MouseEventhandler](/docs/api#mouseeventhandler) +- [priceToCoordinate](/docs/api/interfaces/ISeriesApi#pricetocoordinate) + +Below are a few external resources related to creating and styling html elements: + +- [createElement](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement) +- [innerHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML) +- [style property](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) + +## Examples + +import UsageGuidePartial from "./_usage-guide-partial.mdx"; + + + +import CodeBlock from "@theme/CodeBlock"; + +### Floating Tooltip + +The floating tooltip in this example will position itself next to the current datapoint. + +import floatingCode from "!!raw-loader!./tooltip-floating.js"; + + + {floatingCode} + + +### Tracking Tooltip + +The tracking tooltip will position itself next to the user's cursor. + +import trackingCode from "!!raw-loader!./tooltip-tracking.js"; + + + {trackingCode} + + +### Magnifier Tooltip + +The magnifier tooltip will position itself along the top edge of the chart while following +the user's cursor along the horizontal time axis. + +import magnifierCode from "!!raw-loader!./tooltip-magnifier.js"; + + + {magnifierCode} + diff --git a/website/tutorials/how_to/two-price-scales.js b/website/tutorials/how_to/two-price-scales.js new file mode 100644 index 0000000000..c10342bfd6 --- /dev/null +++ b/website/tutorials/how_to/two-price-scales.js @@ -0,0 +1,859 @@ +// remove-start +// Lightweight Charts Example: Two Price Scales +// https://tradingview.github.io/lightweight-charts/tutorials/how_to/two-price-scales + +// remove-end +const chartOptions = { + // highlight-start + rightPriceScale: { + visible: true, + }, + leftPriceScale: { + visible: true, + }, + // highlight-end + layout: { + textColor: CHART_TEXT_COLOR, + background: { type: 'solid', color: CHART_BACKGROUND_COLOR }, + }, + // highlight-start + crosshair: { + mode: 0, // CrosshairMode.Normal + }, + // highlight-end +}; +// remove-line +/** @type {import('lightweight-charts').IChartApi} */ +const chart = createChart(document.getElementById('container'), chartOptions); +chart + .addLineSeries({ + color: LINE_LINE_COLOR, + lineWidth: 2, + }) + .setData([ + { time: { year: 2018, month: 9, day: 22 }, value: 25.531816900940186 }, + // hide-start + { time: { year: 2018, month: 9, day: 23 }, value: 26.350850429478125 }, + { time: { year: 2018, month: 9, day: 24 }, value: 25.05218443850655 }, + { time: { year: 2018, month: 9, day: 25 }, value: 25.41022485894306 }, + { time: { year: 2018, month: 9, day: 26 }, value: 25.134847363259958 }, + { time: { year: 2018, month: 9, day: 27 }, value: 24.239250761300525 }, + { time: { year: 2018, month: 9, day: 28 }, value: 28.8673009313941 }, + { time: { year: 2018, month: 9, day: 29 }, value: 27.028082380884264 }, + { time: { year: 2018, month: 9, day: 30 }, value: 27.181577793470662 }, + { time: { year: 2018, month: 10, day: 1 }, value: 28.658957209998505 }, + { time: { year: 2018, month: 10, day: 2 }, value: 30.418392247817536 }, + { time: { year: 2018, month: 10, day: 3 }, value: 26.41825183552505 }, + { time: { year: 2018, month: 10, day: 4 }, value: 30.0951233353539 }, + { time: { year: 2018, month: 10, day: 5 }, value: 30.30985059775389 }, + { time: { year: 2018, month: 10, day: 6 }, value: 30.71612555943148 }, + { time: { year: 2018, month: 10, day: 7 }, value: 28.222424591003268 }, + { time: { year: 2018, month: 10, day: 8 }, value: 31.01149570947896 }, + { time: { year: 2018, month: 10, day: 9 }, value: 30.390225881550307 }, + { time: { year: 2018, month: 10, day: 10 }, value: 29.451733557312163 }, + { time: { year: 2018, month: 10, day: 11 }, value: 34.14376900459634 }, + { time: { year: 2018, month: 10, day: 12 }, value: 30.223333215683407 }, + { time: { year: 2018, month: 10, day: 13 }, value: 35.1548736041708 }, + { time: { year: 2018, month: 10, day: 14 }, value: 37.795223779011096 }, + { time: { year: 2018, month: 10, day: 15 }, value: 38.95966228546306 }, + { time: { year: 2018, month: 10, day: 16 }, value: 35.59132526195566 }, + { time: { year: 2018, month: 10, day: 17 }, value: 38.42249768195307 }, + { time: { year: 2018, month: 10, day: 18 }, value: 40.82520015585623 }, + { time: { year: 2018, month: 10, day: 19 }, value: 37.401446370157814 }, + { time: { year: 2018, month: 10, day: 20 }, value: 44.14728329801845 }, + { time: { year: 2018, month: 10, day: 21 }, value: 43.908512951087765 }, + { time: { year: 2018, month: 10, day: 22 }, value: 47.139711966410914 }, + { time: { year: 2018, month: 10, day: 23 }, value: 43.78495537329606 }, + { time: { year: 2018, month: 10, day: 24 }, value: 46.37910782721347 }, + { time: { year: 2018, month: 10, day: 25 }, value: 48.280192310089234 }, + { time: { year: 2018, month: 10, day: 26 }, value: 49.63767420501933 }, + { time: { year: 2018, month: 10, day: 27 }, value: 43.05752682224708 }, + { time: { year: 2018, month: 10, day: 28 }, value: 48.32708061157758 }, + { time: { year: 2018, month: 10, day: 29 }, value: 53.39600337663517 }, + { time: { year: 2018, month: 10, day: 30 }, value: 46.711006266435355 }, + { time: { year: 2018, month: 10, day: 31 }, value: 54.13809826985657 }, + { time: { year: 2018, month: 11, day: 1 }, value: 55.79021790616995 }, + { time: { year: 2018, month: 11, day: 2 }, value: 49.2873885580548 }, + { time: { year: 2018, month: 11, day: 3 }, value: 56.97009522871737 }, + { time: { year: 2018, month: 11, day: 4 }, value: 50.823930530973975 }, + { time: { year: 2018, month: 11, day: 5 }, value: 54.960060077375076 }, + { time: { year: 2018, month: 11, day: 6 }, value: 62.0222568413422 }, + { time: { year: 2018, month: 11, day: 7 }, value: 58.20081640960216 }, + { time: { year: 2018, month: 11, day: 8 }, value: 65.13004590769961 }, + { time: { year: 2018, month: 11, day: 9 }, value: 57.78891076252559 }, + { time: { year: 2018, month: 11, day: 10 }, value: 58.792896124952186 }, + { time: { year: 2018, month: 11, day: 11 }, value: 61.87890147945707 }, + { time: { year: 2018, month: 11, day: 12 }, value: 60.93156560716248 }, + { time: { year: 2018, month: 11, day: 13 }, value: 57.85928164082374 }, + { time: { year: 2018, month: 11, day: 14 }, value: 70.95139577968187 }, + { time: { year: 2018, month: 11, day: 15 }, value: 71.59735270974251 }, + { time: { year: 2018, month: 11, day: 16 }, value: 68.6730845432055 }, + { time: { year: 2018, month: 11, day: 17 }, value: 70.1298800651962 }, + { time: { year: 2018, month: 11, day: 18 }, value: 68.82963709012753 }, + { time: { year: 2018, month: 11, day: 19 }, value: 70.66316240517193 }, + { time: { year: 2018, month: 11, day: 20 }, value: 67.83320577283186 }, + { time: { year: 2018, month: 11, day: 21 }, value: 75.08486799785067 }, + { time: { year: 2018, month: 11, day: 22 }, value: 72.87979342888752 }, + { time: { year: 2018, month: 11, day: 23 }, value: 78.84973566116827 }, + { time: { year: 2018, month: 11, day: 24 }, value: 77.59573370643601 }, + { time: { year: 2018, month: 11, day: 25 }, value: 74.74726921909757 }, + { time: { year: 2018, month: 11, day: 26 }, value: 69.68128245039887 }, + { time: { year: 2018, month: 11, day: 27 }, value: 84.2489916330028 }, + { time: { year: 2018, month: 11, day: 28 }, value: 85.49947753269504 }, + { time: { year: 2018, month: 11, day: 29 }, value: 79.8486264148003 }, + { time: { year: 2018, month: 11, day: 30 }, value: 87.69287202402485 }, + { time: { year: 2018, month: 12, day: 1 }, value: 78.01690218289475 }, + { time: { year: 2018, month: 12, day: 2 }, value: 90.03789034980372 }, + { time: { year: 2018, month: 12, day: 3 }, value: 80.8840602849401 }, + { time: { year: 2018, month: 12, day: 4 }, value: 76.88131503723805 }, + { time: { year: 2018, month: 12, day: 5 }, value: 80.31060219295262 }, + { time: { year: 2018, month: 12, day: 6 }, value: 93.94619117220051 }, + { time: { year: 2018, month: 12, day: 7 }, value: 94.87133202008548 }, + { time: { year: 2018, month: 12, day: 8 }, value: 82.60328626838404 }, + { time: { year: 2018, month: 12, day: 9 }, value: 97.16768398118845 }, + { time: { year: 2018, month: 12, day: 10 }, value: 86.28219316727935 }, + { time: { year: 2018, month: 12, day: 11 }, value: 88.98768390749808 }, + { time: { year: 2018, month: 12, day: 12 }, value: 86.9799632094888 }, + { time: { year: 2018, month: 12, day: 13 }, value: 94.84612878449812 }, + { time: { year: 2018, month: 12, day: 14 }, value: 102.1160216124386 }, + { time: { year: 2018, month: 12, day: 15 }, value: 87.0646295567293 }, + { time: { year: 2018, month: 12, day: 16 }, value: 88.48802912330535 }, + { time: { year: 2018, month: 12, day: 17 }, value: 89.68490260440238 }, + { time: { year: 2018, month: 12, day: 18 }, value: 86.66224375818467 }, + { time: { year: 2018, month: 12, day: 19 }, value: 88.05916917094234 }, + { time: { year: 2018, month: 12, day: 20 }, value: 78.96513176162487 }, + { time: { year: 2018, month: 12, day: 21 }, value: 90.54239307317953 }, + { time: { year: 2018, month: 12, day: 22 }, value: 92.40550159209458 }, + { time: { year: 2018, month: 12, day: 23 }, value: 82.47365747958841 }, + { time: { year: 2018, month: 12, day: 24 }, value: 91.55327647717618 }, + { time: { year: 2018, month: 12, day: 25 }, value: 89.34790162747024 }, + { time: { year: 2018, month: 12, day: 26 }, value: 85.68927849920716 }, + { time: { year: 2018, month: 12, day: 27 }, value: 85.86795553966918 }, + { time: { year: 2018, month: 12, day: 28 }, value: 90.55358282944198 }, + { time: { year: 2018, month: 12, day: 29 }, value: 91.28939932554621 }, + { time: { year: 2018, month: 12, day: 30 }, value: 100.90495261248472 }, + { time: { year: 2018, month: 12, day: 31 }, value: 98.99348823473713 }, + // hide-end + ]); + +const candlestickSeries = chart.addCandlestickSeries({ + // highlight-start + priceScaleId: 'left', + // highlight-end + upColor: BAR_UP_COLOR, downColor: BAR_DOWN_COLOR, borderVisible: false, + wickUpColor: BAR_UP_COLOR, wickDownColor: BAR_DOWN_COLOR, +}); + +candlestickSeries.setData([ + { + close: 108.9974612905403, + high: 121.20998259466148, + low: 96.65376292551082, + open: 104.5614412226746, + time: { year: 2018, month: 9, day: 22 }, + }, + // hide-start + { + close: 110.46815600023501, + high: 111.3650273696516, + low: 82.65543461471314, + open: 110.16538466099634, + time: { year: 2018, month: 9, day: 23 }, + }, + { + close: 90.62131977740425, + high: 109.19838270252615, + low: 82.30106956144076, + open: 105.03104735287424, + time: { year: 2018, month: 9, day: 24 }, + }, + { + close: 96.80120024431532, + high: 101.92074283374939, + low: 89.25819769856513, + open: 89.25819769856513, + time: { year: 2018, month: 9, day: 25 }, + }, + { + close: 94.87113928076117, + high: 104.12503365679314, + low: 85.42405005240111, + open: 104.12503365679314, + time: { year: 2018, month: 9, day: 26 }, + }, + { + close: 100.76494087674855, + high: 102.60508417239113, + low: 80.76268547064865, + open: 92.93299948659636, + time: { year: 2018, month: 9, day: 27 }, + }, + { + close: 101.45855928883597, + high: 110.26727325817173, + low: 91.10348900896837, + open: 93.4362185148034, + time: { year: 2018, month: 9, day: 28 }, + }, + { + close: 91.68871815146144, + high: 103.73263644407702, + low: 73.5329263610334, + open: 92.96467883926464, + time: { year: 2018, month: 9, day: 29 }, + }, + { + close: 89.39633140354033, + high: 101.06569518834237, + low: 81.71149885084162, + open: 83.08248758612376, + time: { year: 2018, month: 9, day: 30 }, + }, + { + close: 93.09498513675378, + high: 93.09498513675378, + low: 76.81138276012628, + open: 91.97280452613565, + time: { year: 2018, month: 10, day: 1 }, + }, + { + close: 89.26733004009083, + high: 89.26733004009083, + low: 76.27851645958225, + open: 85.83860311023625, + time: { year: 2018, month: 10, day: 2 }, + }, + { + close: 89.66035763006947, + high: 89.66035763006947, + low: 67.63677527399729, + open: 77.79584976144585, + time: { year: 2018, month: 10, day: 3 }, + }, + { + close: 89.59687813884807, + high: 97.05916949817754, + low: 72.9823390058379, + open: 77.37388423995716, + time: { year: 2018, month: 10, day: 4 }, + }, + { + close: 83.6425403120788, + high: 91.72593377862522, + low: 65.27208271740422, + open: 85.92711686401718, + time: { year: 2018, month: 10, day: 5 }, + }, + { + close: 82.99053929200655, + high: 93.4482645370556, + low: 65.98920655616067, + open: 71.8940788814462, + time: { year: 2018, month: 10, day: 6 }, + }, + { + close: 73.09595957510754, + high: 86.65935598412881, + low: 62.710852488609326, + open: 80.78945254092527, + time: { year: 2018, month: 10, day: 7 }, + }, + { + close: 80.12127692112905, + high: 87.49034842093855, + low: 60.09987438133361, + open: 70.2408873110499, + time: { year: 2018, month: 10, day: 8 }, + }, + { + close: 77.60439116240829, + high: 83.20908153481327, + low: 68.56836128158209, + open: 75.83440719565763, + time: { year: 2018, month: 10, day: 9 }, + }, + { + close: 73.8952889203428, + high: 81.89987377779327, + low: 57.8891283348631, + open: 66.51904017615065, + time: { year: 2018, month: 10, day: 10 }, + }, + { + close: 75.08452506029613, + high: 75.08452506029613, + low: 59.208194031968155, + open: 72.14475369395771, + time: { year: 2018, month: 10, day: 11 }, + }, + { + close: 73.08898607472305, + high: 73.08898607472305, + low: 63.05632280826725, + open: 66.93050765469924, + time: { year: 2018, month: 10, day: 12 }, + }, + { + close: 58.993371469509704, + high: 73.08872095153116, + low: 53.52204433018574, + open: 66.12840939191403, + time: { year: 2018, month: 10, day: 13 }, + }, + { + close: 57.150755012485, + high: 74.57414896810235, + low: 52.6552427480398, + open: 68.50876667562338, + time: { year: 2018, month: 10, day: 14 }, + }, + { + close: 58.03147289822832, + high: 69.62445357159157, + low: 53.8260018823565, + open: 61.62298899368165, + time: { year: 2018, month: 10, day: 15 }, + }, + { + close: 60.6852855399041, + high: 69.02095441024431, + low: 54.00939224622043, + open: 64.81901552322648, + time: { year: 2018, month: 10, day: 16 }, + }, + { + close: 57.508820449565285, + high: 63.82926565242872, + low: 54.07370975509682, + open: 54.07370975509682, + time: { year: 2018, month: 10, day: 17 }, + }, + { + close: 51.60796818909221, + high: 64.88712939579875, + low: 51.60796818909221, + open: 53.489226476218434, + time: { year: 2018, month: 10, day: 18 }, + }, + { + close: 55.139520748382864, + high: 59.161320710177925, + low: 52.228139922762765, + open: 52.228139922762765, + time: { year: 2018, month: 10, day: 19 }, + }, + { + close: 47.47868992247237, + high: 58.0836625917653, + low: 46.43213518526832, + open: 47.59258635788406, + time: { year: 2018, month: 10, day: 20 }, + }, + { + close: 47.22596723150508, + high: 51.55468175560989, + low: 45.22654435521185, + open: 47.452459556200054, + time: { year: 2018, month: 10, day: 21 }, + }, + { + close: 53.39724151191295, + high: 58.256006746786035, + low: 46.40105667413804, + open: 53.41548527476272, + time: { year: 2018, month: 10, day: 22 }, + }, + { + close: 45.015877277800854, + high: 51.2955426978105, + low: 40.26534748806228, + open: 43.90158660063875, + time: { year: 2018, month: 10, day: 23 }, + }, + { + close: 49.307312373439665, + high: 49.93643636637581, + low: 43.20705757075934, + open: 45.672934511555795, + time: { year: 2018, month: 10, day: 24 }, + }, + { + close: 45.15418019295631, + high: 48.59676744409583, + low: 37.628047445918504, + open: 40.33862825788268, + time: { year: 2018, month: 10, day: 25 }, + }, + { + close: 43.2972018283068, + high: 43.2972018283068, + low: 36.335943004352245, + open: 42.605991542720965, + time: { year: 2018, month: 10, day: 26 }, + }, + { + close: 39.1153643552137, + high: 44.311406627923844, + low: 35.31457011784855, + open: 42.00000202357808, + time: { year: 2018, month: 10, day: 27 }, + }, + { + close: 36.06960076896885, + high: 42.89041111567749, + low: 33.58326637182405, + open: 37.40942817102858, + time: { year: 2018, month: 10, day: 28 }, + }, + { + close: 35.8981036950969, + high: 42.19793419602979, + low: 33.62190962880232, + open: 36.87246325249825, + time: { year: 2018, month: 10, day: 29 }, + }, + { + close: 31.378205119641457, + high: 37.33501102832602, + low: 31.30137162225214, + open: 35.651275660713694, + time: { year: 2018, month: 10, day: 30 }, + }, + { + close: 33.574536057730576, + high: 37.30152570719593, + low: 27.369689193426243, + open: 34.330180925654936, + time: { year: 2018, month: 10, day: 31 }, + }, + { + close: 28.91735096504839, + high: 32.62196350117741, + low: 27.072564759401843, + open: 29.398552328599372, + time: { year: 2018, month: 11, day: 1 }, + }, + { + close: 28.44143154172122, + high: 31.042019861166594, + low: 23.383320830495375, + open: 27.275885037308072, + time: { year: 2018, month: 11, day: 2 }, + }, + { + close: 25.92162714418916, + high: 30.57604443170622, + low: 25.418671641150752, + open: 26.775196275924657, + time: { year: 2018, month: 11, day: 3 }, + }, + { + close: 26.376994016637433, + high: 28.198647836523744, + low: 21.492969733673334, + open: 26.27980943059139, + time: { year: 2018, month: 11, day: 4 }, + }, + { + close: 28.60834088674494, + high: 28.60834088674494, + low: 21.89002840571941, + open: 25.376464895884993, + time: { year: 2018, month: 11, day: 5 }, + }, + { + close: 31.103861067101136, + high: 31.103861067101136, + low: 24.39227668420513, + open: 28.994785427089838, + time: { year: 2018, month: 11, day: 6 }, + }, + { + close: 28.6308138310307, + high: 35.430817482769164, + low: 24.069515410358232, + open: 27.109407394069084, + time: { year: 2018, month: 11, day: 7 }, + }, + { + close: 29.468889521733466, + high: 37.54082586961352, + low: 27.90833873315644, + open: 33.16901271715577, + time: { year: 2018, month: 11, day: 8 }, + }, + { + close: 35.887823124204296, + high: 39.21804237580939, + low: 30.951078044055627, + open: 30.951078044055627, + time: { year: 2018, month: 11, day: 9 }, + }, + { + close: 34.361137347097575, + high: 35.27083445807796, + low: 27.825889562160082, + open: 34.86040182980157, + time: { year: 2018, month: 11, day: 10 }, + }, + { + close: 36.61336645243868, + high: 40.31460537175622, + low: 33.96383400053921, + open: 39.70037560142739, + time: { year: 2018, month: 11, day: 11 }, + }, + { + close: 41.321693986803055, + high: 44.45481986667003, + low: 35.67563772228475, + open: 38.67059795413642, + time: { year: 2018, month: 11, day: 12 }, + }, + { + close: 40.15038854039306, + high: 41.50912000191902, + low: 32.610637769394444, + open: 41.50912000191902, + time: { year: 2018, month: 11, day: 13 }, + }, + { + close: 44.092601065208015, + high: 44.092601065208015, + low: 37.778306506100726, + open: 38.99045898154136, + time: { year: 2018, month: 11, day: 14 }, + }, + { + close: 41.42426637839382, + high: 44.72189614841937, + low: 41.42426637839382, + open: 44.72189614841937, + time: { year: 2018, month: 11, day: 15 }, + }, + { + close: 41.19513795258408, + high: 49.08084695291049, + low: 36.24282165100056, + open: 44.909248500660254, + time: { year: 2018, month: 11, day: 16 }, + }, + { + close: 44.24935708161703, + high: 53.028535501565486, + low: 40.32056743060158, + open: 46.198546801109984, + time: { year: 2018, month: 11, day: 17 }, + }, + { + close: 43.18555863372182, + high: 52.34250206788521, + low: 43.18555863372182, + open: 49.58135271619679, + time: { year: 2018, month: 11, day: 18 }, + }, + { + close: 50.8568887039091, + high: 52.60441957102357, + low: 39.917719271944364, + open: 48.197532365645806, + time: { year: 2018, month: 11, day: 19 }, + }, + { + close: 48.79128595974164, + high: 52.45087789296739, + low: 46.80633073487828, + open: 52.45087789296739, + time: { year: 2018, month: 11, day: 20 }, + }, + { + close: 46.97300046781947, + high: 55.80689868049132, + low: 46.97300046781947, + open: 55.80689868049132, + time: { year: 2018, month: 11, day: 21 }, + }, + { + close: 55.58129861112469, + high: 55.58129861112469, + low: 49.087279242343996, + open: 53.16719476594961, + time: { year: 2018, month: 11, day: 22 }, + }, + { + close: 50.058979311730226, + high: 62.55333249171461, + low: 50.058979311730226, + open: 52.628489607588826, + time: { year: 2018, month: 11, day: 23 }, + }, + { + close: 51.193155229085995, + high: 59.08949991997865, + low: 51.193155229085995, + open: 55.352594157474755, + time: { year: 2018, month: 11, day: 24 }, + }, + { + close: 60.099338327208436, + high: 66.93510126958154, + low: 55.27299867222781, + open: 61.05897620044226, + time: { year: 2018, month: 11, day: 25 }, + }, + { + close: 58.3802630890727, + high: 71.50922937699602, + low: 50.95210438359451, + open: 62.4679688326532, + time: { year: 2018, month: 11, day: 26 }, + }, + { + close: 57.875350873413225, + high: 65.59903214448208, + low: 57.875350873413225, + open: 62.747405667247016, + time: { year: 2018, month: 11, day: 27 }, + }, + { + close: 61.231150731698605, + high: 66.3829902228434, + low: 61.231150731698605, + open: 65.01028486919516, + time: { year: 2018, month: 11, day: 28 }, + }, + { + close: 64.9698540874806, + high: 77.09783903299783, + low: 58.455031795628194, + open: 58.455031795628194, + time: { year: 2018, month: 11, day: 29 }, + }, + { + close: 72.40978471883417, + high: 72.40978471883417, + low: 53.05804901549206, + open: 65.907298021202, + time: { year: 2018, month: 11, day: 30 }, + }, + { + close: 74.60745731538934, + high: 78.33742117000789, + low: 54.42067144918077, + open: 73.20930147914103, + time: { year: 2018, month: 12, day: 1 }, + }, + { + close: 64.10866184869924, + high: 76.14506447001202, + low: 61.5224432669736, + open: 69.33984127682314, + time: { year: 2018, month: 12, day: 2 }, + }, + { + close: 65.92038759928786, + high: 76.98479070362022, + low: 65.92038759928786, + open: 69.32298264631615, + time: { year: 2018, month: 12, day: 3 }, + }, + { + close: 68.23682161095334, + high: 77.6723729460968, + low: 68.23682161095334, + open: 74.39471534484744, + time: { year: 2018, month: 12, day: 4 }, + }, + { + close: 67.45035792565862, + high: 83.53728553118547, + low: 67.45035792565862, + open: 74.79418570077237, + time: { year: 2018, month: 12, day: 5 }, + }, + { + close: 79.26722967320323, + high: 79.26722967320323, + low: 68.40654829383521, + open: 68.40654829383521, + time: { year: 2018, month: 12, day: 6 }, + }, + { + close: 74.95305464030587, + high: 81.65884414224071, + low: 64.08761481290135, + open: 81.65884414224071, + time: { year: 2018, month: 12, day: 7 }, + }, + { + close: 86.30802154315482, + high: 91.21953112925642, + low: 65.46112304993535, + open: 77.82514647663533, + time: { year: 2018, month: 12, day: 8 }, + }, + { + close: 82.67218208289492, + high: 92.45833377442081, + low: 76.80728739647081, + open: 87.18916937056241, + time: { year: 2018, month: 12, day: 9 }, + }, + { + close: 73.86125805398967, + high: 83.68952750914036, + low: 73.86125805398967, + open: 75.76119064173785, + time: { year: 2018, month: 12, day: 10 }, + }, + { + close: 79.00109311074502, + high: 88.74271558831151, + low: 69.00900811612337, + open: 88.74271558831151, + time: { year: 2018, month: 12, day: 11 }, + }, + { + close: 80.98779620162513, + high: 97.07429720104427, + low: 73.76970378608283, + open: 88.57288529720472, + time: { year: 2018, month: 12, day: 12 }, + }, + { + close: 87.83619759370346, + high: 91.29759438607132, + low: 74.00740214639268, + open: 87.51853658661781, + time: { year: 2018, month: 12, day: 13 }, + }, + { + close: 87.50134898892377, + high: 102.95635188637507, + low: 81.0513723219724, + open: 94.74009794290814, + time: { year: 2018, month: 12, day: 14 }, + }, + { + close: 92.40159548029843, + high: 103.24363067710844, + low: 75.44605394394573, + open: 95.99903495559444, + time: { year: 2018, month: 12, day: 15 }, + }, + { + close: 87.43619322092951, + high: 99.39349139000474, + low: 80.24624983473528, + open: 99.39349139000474, + time: { year: 2018, month: 12, day: 16 }, + }, + { + close: 84.42724177432086, + high: 95.57485075893881, + low: 84.42724177432086, + open: 90.71070399095831, + time: { year: 2018, month: 12, day: 17 }, + }, + { + close: 96.04408990868804, + high: 101.02158248010466, + low: 94.38544669520195, + open: 101.02158248010466, + time: { year: 2018, month: 12, day: 18 }, + }, + { + close: 97.2120815653703, + high: 103.35830035963959, + low: 78.72594316029567, + open: 86.98009038330306, + time: { year: 2018, month: 12, day: 19 }, + }, + { + close: 105.23366706522204, + high: 106.56174456393981, + low: 94.6658899187065, + open: 106.56174456393981, + time: { year: 2018, month: 12, day: 20 }, + }, + { + close: 89.53750874231946, + high: 112.27917367188074, + low: 87.13586952228918, + open: 96.0857985693989, + time: { year: 2018, month: 12, day: 21 }, + }, + { + close: 88.55787263435407, + high: 112.62138454627025, + low: 80.42783344898223, + open: 88.34340019789849, + time: { year: 2018, month: 12, day: 22 }, + }, + { + close: 86.00639650371167, + high: 110.94532193307279, + low: 74.78703575498496, + open: 92.4275741143068, + time: { year: 2018, month: 12, day: 23 }, + }, + { + close: 90.45119640254379, + high: 92.51500970997435, + low: 82.69475430846728, + open: 86.21662699549296, + time: { year: 2018, month: 12, day: 24 }, + }, + { + close: 93.38124264891343, + high: 93.38124264891343, + low: 84.5674956907938, + open: 87.54923273867136, + time: { year: 2018, month: 12, day: 25 }, + }, + { + close: 87.88725775527871, + high: 90.14253631595105, + low: 77.28638555494503, + open: 83.93199044032968, + time: { year: 2018, month: 12, day: 26 }, + }, + { + close: 71.77940077333062, + high: 89.25710176370582, + low: 67.74278646676306, + open: 78.5346198695072, + time: { year: 2018, month: 12, day: 27 }, + }, + { + close: 72.08757207606054, + high: 79.36518615067514, + low: 69.18787486704672, + open: 69.18787486704672, + time: { year: 2018, month: 12, day: 28 }, + }, + { + close: 73.87977927793119, + high: 77.62891475860795, + low: 70.42293039125319, + open: 70.42293039125319, + time: { year: 2018, month: 12, day: 29 }, + }, + { + close: 74.86330345366132, + high: 75.88473282167168, + low: 62.89549355427313, + open: 74.86554252962132, + time: { year: 2018, month: 12, day: 30 }, + }, + { + close: 71.00787215611817, + high: 71.00787215611817, + low: 57.29681995441558, + open: 60.04464694823929, + time: { year: 2018, month: 12, day: 31 }, + }, + // hide-end +]); + +chart.timeScale().fitContent(); diff --git a/website/tutorials/how_to/two-price-scales.mdx b/website/tutorials/how_to/two-price-scales.mdx new file mode 100644 index 0000000000..0fd6226186 --- /dev/null +++ b/website/tutorials/how_to/two-price-scales.mdx @@ -0,0 +1,78 @@ +--- +title: Two Price Scales +sidebar_label: Two Price Scales +description: An example of how to add two price scales to a chart. +pagination_prev: null +pagination_next: null +keywords: + - price + - scale +--- + +It is possible to have two price scales visible on a Lightweight Chart, +namely one on the right side (default) and another on the left. This example +shows how to configure your chart to contain two price scales. + +## Short answer + +Ensure that `rightPriceScale` and `leftPriceScale` has the `visibility` property +set to `true` within the [chart options](/docs/api/interfaces/ChartOptions#leftpricescale). + +```js +chart.applyOptions({ + rightPriceScale: { + visible: true, + }, + leftPriceScale: { + visible: true, + }, +}); +``` + +and assign the `priceScaleId` property on the [series options](/docs/api/interfaces/SeriesOptionsCommon#pricescaleid) +for the series which you would like to use the left scale. Note that by default a +series will use the right scale thus we don't need to set that property on the other series. + +```js +const leftSeries = chart.addCandlestickSeries( + { priceScaleId: 'left' } +); +``` + +You can see a full [working example](#full-example) below. + +## Tips + +By default the crosshair will snap to the data points of the first series. +You may prefer to set the [crosshair mode](/docs/api/enums/CrosshairMode) to +`normal` so that you get a crosshair which allows sits directly beneath your cursor. + +```js +chart.applyOptions({ + crosshair: { + mode: 0, // CrosshairMode.Normal + }, +}); +``` + +## Resources + +You can learn more about price scales here: [Price scale](/docs/price-scale) + +and view the related APIs here: +- [Chart Options](/docs/api/interfaces/ChartOptions#leftpricescale) +- [PriceScaleOptions](/docs/api/interfaces/PriceScaleOptions) +- [SeriesOptionsCommon priceScaleId](/docs/api/interfaces/SeriesOptionsCommon#pricescaleid) + +## Full example + +import UsageGuidePartial from "./_usage-guide-partial.mdx"; + + + +import CodeBlock from "@theme/CodeBlock"; +import code from "!!raw-loader!./two-price-scales.js"; + + + {code} + diff --git a/website/tutorials/how_to/watermark-advanced.js b/website/tutorials/how_to/watermark-advanced.js new file mode 100644 index 0000000000..d1f4b06ad6 --- /dev/null +++ b/website/tutorials/how_to/watermark-advanced.js @@ -0,0 +1,57 @@ +// remove-start +// Lightweight Charts Example: Watermark Advanced +// https://tradingview.github.io/lightweight-charts/tutorials/how_to/watermark + +// remove-end +const chartOptions = { + layout: { + textColor: CHART_TEXT_COLOR, + // set chart background color to transparent so we can see the elements below + // highlight-next-line + background: { type: 'solid', color: 'transparent' }, + }, +}; +// remove-line +/** @type {import('lightweight-charts').IChartApi} */ +const chart = createChart(document.getElementById('container'), chartOptions); + +// highlight-start +const container = document.getElementById('container'); +const background = document.createElement('div'); +// place below the chart +background.style.zIndex = -1; +background.style.position = 'absolute'; +// set size and position to match container +background.style.inset = '0px'; +background.style.backgroundImage = `url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyOTIiIGhlaWdodD0iMTI4IiB2aWV3Qm94PSIwIDAgMjkyIDEyOCI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBkPSJtMTgyLjkzIDcuNi42My0uMzdhNjQuMSA2NC4xIDAgMCAwIDIuNDMtNS4zMWw0Ljc3LTEuMzlhNjQuNjggNjQuNjggMCAwIDEtNC43MiAxMC41NGMuMzggMTAuNDUtMy45MyAyMS4xNS0xMS4xIDI5LjM3LTExLjY2IDEzLjQxLTI2Ljk4IDE1Ljk3LTQzLjU3IDEzLjc4bDEuMDctLjk4YTIxLjEgMjEuMSAwIDAgMCAzLjcyLTQuMDUgNDguMzcgNDguMzcgMCAwIDEtMTEuMDQgMi44NGMtMTAuNjUtNS41NC0yMS42NC0xNC45NC0yNC4yNy0yNy4yNyA5LjE5LTE3IDI4Ljk1LTI0LjAxIDQ3LjM5LTE5Ljk0YTIyLjU3IDIyLjU3IDAgMCAwIDUuODYgOS4wMmMtLjEyLTEuOTItLjEtMy44NC0uMS01Ljc2bC4wMS0xLjc4YzQuOCAyLjk2IDkuNjYgNS44NSAxNS41MiA1LjcgNC4wOC0uMSA4LjQtMS41MiAxMy40LTQuNFptLTIyLjU1IDIzLjI4YTguNDggOC40OCAwIDAgMC0xMi40NS0uMzNsLTcuOS03LjI2QTguNiA4LjYgMCAwIDAgMTMyIDEyYy02LjE0IDAtMTAuMjUgNi42My03LjcgMTIuMDlsLTEzLjAyIDEyLjE5Yy00LjEtNC45Ny01LjY4LTkuMy02LjE3LTEwLjk0IDguMzYtMTMuNzIgMjQuNDYtMjAuMTggNDAuMTUtMTcuMDcgMi45MyA2LjkgOC4zOCAxMC43MiAxNC43NyAxMy45NmwtLjMzLTEuMTRjLS43NC0yLjU2LTEuNDctNS4xLTEuNjItNy43OCA3LjA1IDMuNDUgMTQuNiAzLjM1IDIxLjc2LjMxLTQuNzYgNy4yNy0xMS4xMyAxNC4yMi0xOS40NiAxNy4yNlptLTIyLjU2LTQuMTkgOC4wMyA3LjM4QTguNiA4LjYgMCAwIDAgMTU0IDQ1YTguNiA4LjYgMCAwIDAgOC4yNS0xMC41NWM3Ljk5LTMuMDggMTQuMzctOS4zOCAxOS4yOC0xNi4yMy0zLjQ3IDE5LjQ3LTIxLjk2IDM0LjYxLTQxLjkgMzIuOTggMS43Ny0yLjg0IDIuNDktNi4wNiAzLjIxLTkuMjhsLjM1LTEuNTZjLTUuNDcgMy43Ny0xMC42NyA2LjM4LTE3LjM3IDcuNTJhNDkuOSA0OS45IDAgMCAxLTExLjg1LTguNjVsMTIuODMtMTJhOC41OCA4LjU4IDAgMCAwIDExLjAyLS41NFpNMTMyIDE2YTQuNSA0LjUgMCAxIDAgMCA5IDQuNSA0LjUgMCAwIDAgMC05Wm0xNy41IDIwLjVhNC41IDQuNSAwIDEgMSA5IDAgNC41IDQuNSAwIDAgMS05IDBaTTIxLjYzIDcxLjhhMi4zMyAyLjMzIDAgMCAxIDIuMzMgMi4zNCAyLjM0IDIuMzQgMCAwIDEtMi4zMyAyLjM3IDIuMzggMi4zOCAwIDAgMS0yLjM3LTIuMzcgMi4zOCAyLjM4IDAgMCAxIDIuMzctMi4zM1ptMS43NiA4LjJ2MTZoLTMuNTJWODBoMy41MlptLTYuNDYgMTZIMi43OFY3My4yOGgzLjc1djE5LjE0aDEwLjRWOTZabTI2LjM5LTEuMDlWODBIMzkuOHYyLjE0YTYuMjYgNi4yNiAwIDAgMC01LjEyLTIuNDZjLTQuMzIgMC03LjY4IDMuNTgtNy42OCA4LjEgMCA0LjU0IDMuMzYgOC4xMiA3LjY4IDguMTIgMi4yIDAgNC4xNi0xLjA4IDUuMTItMi41djEuNDhjMCAzLjIzLTIuMTggNS00LjgzIDVhNy4wMyA3LjAzIDAgMCAxLTUuMzItMi4zNGwtMi4xNCAyLjUyYzEuNTcgMS43NiA0LjM1IDIuOTUgNy40OSAyLjk1IDQuNzMgMCA4LjMyLTIuNTMgOC4zMi04LjFabS0xMi43Ny03LjEzYTQuNyA0LjcgMCAwIDEgNC43Ny00LjkgNC43IDQuNyAwIDAgMSA0Ljc3IDQuOSA0LjcgNC43IDAgMCAxLTQuNzcgNC45IDQuNyA0LjcgMCAwIDEtNC43Ny00LjlaTTUxLjU4IDk2aC0zLjUyVjcyaDMuNTJ2MTAuMThjLjk2LTEuNiAyLjc4LTIuNSA0Ljg2LTIuNSAzLjcxIDAgNi4xMSAyLjYyIDYuMTEgNi42OVY5NmgtMy41MnYtOS4wNmMwLTIuNTItMS4yOC00LjA2LTMuMzMtNC4wNi0yLjMzIDAtNC4xMiAxLjgyLTQuMTIgNS4yNVY5NlptMjQuODYtLjJ2LTMuMTNjLS41Mi4yLTEuMjIuMzItMS45LjMyLTEuODIgMC0yLjY4LS43My0yLjY4LTIuNzJ2LTcuMTNoNC41OFY4MGgtNC41OHYtNC40NWgtMy41MlY4MGgtMy4zM3YzLjE0aDMuMzN2Ny43YzAgMy42MiAyLjQgNS4zMiA1LjQ3IDUuMzIgMS4wOSAwIDEuOTItLjEzIDIuNjMtLjM1Wm0yMC4zLjJIOTMuNGwtMy41Mi0xMC4zN0w4Ni4zOSA5NmgtMy4zMmwtNS4zOC0xNmgzLjcybDMuNDUgMTEgMy42OC0xMWgyLjY5bDMuNjUgMTEgMy40OS0xMWgzLjc0bC01LjM4IDE2Wm02Ljc2LThjMCA0Ljg2IDMuNDkgOC4zMiA4LjM1IDguMzIgMy4zNiAwIDUuODYtMS40NCA3LjMtMy43MWwtMi43LTEuOTJhNS4wMyA1LjAzIDAgMCAxLTQuNTcgMi40M2MtMi42NSAwLTQuNzctMS43My00LjkzLTQuMzVoMTIuNThjLjAzLS41MS4wMy0uOC4wMy0xLjE1IDAtNS4xNi0zLjUyLTcuOTQtNy43MS03Ljk0QTguMTIgOC4xMiAwIDAgMCAxMDMuNSA4OFptOC4yMi01LjM0YzIuMDUgMCAzLjkgMS4yNCA0LjI5IDMuNTVoLTguOWMuNDgtMi4zNyAyLjU2LTMuNTUgNC42MS0zLjU1Wm0xMy4yMi0xMC44NWEyLjMzIDIuMzMgMCAwIDEgMi4zNCAyLjMzIDIuMzQgMi4zNCAwIDAgMS0yLjM0IDIuMzcgMi4zOCAyLjM4IDAgMCAxLTIuMzctMi4zNyAyLjM4IDIuMzggMCAwIDEgMi4zNy0yLjMzWm0yMS43IDIzLjFWODBoLTMuNTN2Mi4xNGE2LjI2IDYuMjYgMCAwIDAtNS4xMi0yLjQ2Yy00LjMyIDAtNy42OCAzLjU4LTcuNjggOC4xIDAgNC41NCAzLjM2IDguMTIgNy42OCA4LjEyIDIuMiAwIDQuMTYtMS4wOCA1LjEyLTIuNXYxLjQ4YzAgMy4yMy0yLjE4IDUtNC44MyA1YTcuMDMgNy4wMyAwIDAgMS01LjMxLTIuMzRsLTIuMTUgMi41MmMxLjU3IDEuNzYgNC4zNiAyLjk1IDcuNSAyLjk1IDQuNzMgMCA4LjMxLTIuNTMgOC4zMS04LjFaTTEyNi43IDk2aC0zLjUyVjgwaDMuNTJ2MTZabTcuMTYtOC4yMmE0LjcgNC43IDAgMCAxIDQuNzctNC45IDQuNyA0LjcgMCAwIDEgNC43NyA0LjkgNC43IDQuNyAwIDAgMS00Ljc3IDQuOSA0LjcgNC43IDAgMCAxLTQuNzctNC45Wk0xNTQuOSA5NmgtMy41MlY3MmgzLjUydjEwLjE4Yy45Ni0xLjYgMi43OC0yLjUgNC44Ni0yLjUgMy43MSAwIDYuMTEgMi42MiA2LjExIDYuNjlWOTZoLTMuNTJ2LTkuMDZjMC0yLjUyLTEuMjgtNC4wNi0zLjMyLTQuMDYtMi4zNCAwLTQuMTMgMS44Mi00LjEzIDUuMjVWOTZabTI0Ljg2LS4ydi0zLjEzYy0uNTEuMi0xLjIyLjMyLTEuODkuMzItMS44MiAwLTIuNjktLjczLTIuNjktMi43MnYtNy4xM2g0LjU4VjgwaC00LjU4di00LjQ1aC0zLjUyVjgwaC0zLjMzdjMuMTRoMy4zM3Y3LjdjMCAzLjYyIDIuNCA1LjMyIDUuNDcgNS4zMiAxLjEgMCAxLjkyLS4xMyAyLjYzLS4zNVptMjEuNTkuNThhMTEuNjcgMTEuNjcgMCAwIDEtMTEuNzUtMTEuNzRjMC02LjU2IDUuMjItMTEuNzQgMTEuNzUtMTEuNzQgNC40NSAwIDguMjIgMi4yNyAxMC4yNCA1Ljc2bC0zLjIzIDEuODVhNy45NCA3Ljk0IDAgMCAwLTcuMDEtNCA3Ljk2IDcuOTYgMCAwIDAtNy45NyA4LjEzIDcuOTYgNy45NiAwIDAgMCA3Ljk3IDguMTMgNy45NCA3Ljk0IDAgMCAwIDctNGwzLjI0IDEuODVhMTEuNjYgMTEuNjYgMCAwIDEtMTAuMjQgNS43NlptMTMuNC0uMzhoMy41MnYtNy44N2MwLTMuNDMgMS44LTUuMjUgNC4xMy01LjI1IDIuMDUgMCAzLjMzIDEuNTQgMy4zMyA0LjA2Vjk2aDMuNTJ2LTkuNjNjMC00LjA3LTIuNC02LjY5LTYuMTEtNi42OS0yLjA4IDAtMy45LjktNC44NyAyLjVWNzJoLTMuNTJ2MjRabTI1LjU2LjMyYy00LjM4IDAtNy43LTMuNzQtNy43LTguMzJzMy4zMi04LjMyIDcuNy04LjMyYzIuMyAwIDQuMjMgMS4xOCA1LjEyIDIuNDZWODBoMy41MnYxNmgtMy41MnYtMi4xNGE2LjM4IDYuMzggMCAwIDEtNS4xMiAyLjQ2Wm0uNjQtMy4yYzIuODUgMCA0Ljc3LTIuMjQgNC43Ny01LjEycy0xLjkyLTUuMTItNC43Ny01LjEyYy0yLjg0IDAtNC43NiAyLjI0LTQuNzYgNS4xMnMxLjkxIDUuMTIgNC43NiA1LjEyWk0yNTMuNzEgOTZoMy41MnYtNy44YzAtMy4yIDEuODMtNC45IDMuODQtNC45LjY0IDAgMS4xNS4xIDEuNzYuMjh2LTMuNjFjLS40OC0uMS0uOTMtLjEzLTEuMzctLjEzYTQuNSA0LjUgMCAwIDAtNC4yMyAzVjgwaC0zLjUydjE2Wm0yMS43My0zLjMzdjMuMTRjLS43LjIyLTEuNTQuMzUtMi42My4zNS0zLjA3IDAtNS40Ny0xLjctNS40Ny01LjMxdi03LjcxaC0zLjMzVjgwaDMuMzN2LTQuNDVoMy41MlY4MGg0LjU4djMuMTRoLTQuNTh2Ny4xM2MwIDEuOTkuODYgMi43MiAyLjY5IDIuNzIuNjcgMCAxLjM3LS4xMyAxLjg5LS4zMlptMTQuMjEtMS4zMWMwLTIuNjItMS42Ni00LjAzLTQuNDgtNC44NmwtMS42My0uNDhjLTEuNTctLjQ1LTEuOTItMS4xMi0xLjkyLTEuOSAwLS45NSAxLjA5LTEuNSAyLjE1LTEuNSAxLjMgMCAyLjMzLjY0IDMuMDQgMS42NGwyLjQzLTEuODZjLTEuMTItMS43Ni0zLjAxLTIuNzItNS40MS0yLjcyLTMuMiAwLTUuNyAxLjczLTUuNzMgNC41OC0uMDMgMi4zNiAxLjQxIDQuMTIgNC4yIDQuOWwxLjQuMzhjMS45Mi41NyAyLjQ3IDEuMTIgMi40NyAyLjA0IDAgMS4xMi0xLjA2IDEuNy0yLjMgMS43LTEuNjQgMC0zLjItLjgtMy44NS0yLjJsLTIuNTkgMS44NWMxLjE1IDIuMjcgMy41OCAzLjM5IDYuNDMgMy4zOSAzLjMgMCA1LjgtMS44OSA1LjgtNC45NlptLTE0My4zOCAyMS40YzAgLjQ2LS4zNy44NC0uODMuODRhLjg2Ljg2IDAgMCAxLS44Ny0uODVjMC0uNDYuMzktLjg1Ljg3LS44NS40NiAwIC44My4zOS44My44NVptLS4yOSAxMS4yNGgtMS4xMnYtOGgxLjEydjhabS01Mi4wMi4xNmE0LjA0IDQuMDQgMCAwIDAgMy45OC00LjE2IDQuMDQgNC4wNCAwIDAgMC0zLjk4LTQuMTZjLTEuMjQgMC0yLjM5LjY0LTIuOTYgMS41VjExMmgtMS4xMnYxMkg5MXYtMS4zNGMuNTcuODYgMS43MiAxLjUgMi45NiAxLjVabS0uMTItMS4wNGMtMS43NCAwLTIuOTQtMS40LTIuOTQtMy4xMiAwLTEuNzMgMS4yLTMuMTIgMi45NC0zLjEyIDEuNzUgMCAyLjk1IDEuNCAyLjk1IDMuMTIgMCAxLjczLTEuMiAzLjEyLTIuOTUgMy4xMlptNy45IDQuMjIgNS4zLTExLjM0aC0xLjI2bC0yLjkzIDYuMzUtMi45My02LjM1aC0xLjI0bDMuNTUgNy42LTEuNzYgMy43NGgxLjI2Wk0xMTUuMyAxMjRoLTEuMnYtMTAuMmgtMy42OHYtMS4xNmg4LjU2djEuMTVoLTMuNjhWMTI0Wm0zLjgyIDBoMS4xMnYtNC4wMmMwLTIuMDQgMS4yMy0yLjk0IDIuMjItMi45NC4yNCAwIC40NS4wMy42Ny4xMXYtMS4xN2EyLjQ0IDIuNDQgMCAwIDAtMi45IDEuNjZWMTE2aC0xLjExdjhabTExLjcyLTEuMzRhMy42NCAzLjY0IDAgMCAxLTIuOTYgMS41IDQuMDQgNC4wNCAwIDAgMS0zLjk4LTQuMTYgNC4wNCA0LjA0IDAgMCAxIDMuOTgtNC4xNmMxLjIzIDAgMi4zOS42NCAyLjk2IDEuNVYxMTZoMS4xMnY4aC0xLjEydi0xLjM0Wm0tNS44LTIuNjZjMCAxLjczIDEuMiAzLjEyIDIuOTUgMy4xMiAxLjc1IDAgMi45NS0xLjQgMi45NS0zLjEyIDAtMS43My0xLjItMy4xMi0yLjk1LTMuMTItMS43NCAwLTIuOTQgMS40LTIuOTQgMy4xMlptMTIuOTggNC4xNmMxLjIzIDAgMi4zOS0uNjQgMi45Ni0xLjVWMTI0aDEuMTJ2LTEySDE0MXY1LjM0YTMuNjQgMy42NCAwIDAgMC0yLjk2LTEuNSA0LjA0IDQuMDQgMCAwIDAtMy45OCA0LjE2IDQuMDQgNC4wNCAwIDAgMCAzLjk4IDQuMTZabS4xMS0xLjA0Yy0xLjc0IDAtMi45NC0xLjQtMi45NC0zLjEyIDAtMS43MyAxLjItMy4xMiAyLjk0LTMuMTIgMS43NSAwIDIuOTUgMS40IDIuOTUgMy4xMiAwIDEuNzMtMS4yIDMuMTItMi45NSAzLjEyWm0xMC42Ljg4aDEuMTF2LTMuOThjMC0xLjk5IDEuMS0zLjE0IDIuNS0zLjE0IDEuMTkgMCAyLjAyLjg2IDIuMDIgMi4yN1YxMjRoMS4xMnYtNWMwLTEuOTYtMS4yNy0zLjE2LTMuMDEtMy4xNi0xLjA0IDAtMi4wNS40NS0yLjYzIDEuNVYxMTZoLTEuMTF2OFptMTYuNzEtLjQyYzAgMi42MS0xLjcyIDMuOTItMy45NSAzLjkyLTEuODQgMC0zLjE3LS44My0zLjc3LTEuNzRsLjg4LS43NWEzLjQgMy40IDAgMCAwIDIuOSAxLjQ1YzEuMzcgMCAyLjgyLS44MyAyLjgyLTIuOTR2LTEuMDJjLS41Ny44Ni0xLjcgMS41LTIuOTIgMS41YTMuOTQgMy45NCAwIDAgMS0zLjk2LTQuMDggMy45NCAzLjk0IDAgMCAxIDMuOTYtNC4wOGMxLjIzIDAgMi4zNS42NCAyLjkyIDEuNVYxMTZoMS4xMnY3LjU4Wm0tNi44NC0zLjY2YzAgMS43MyAxLjE2IDMuMDQgMi45IDMuMDQgMS43NSAwIDIuOTItMS4zMSAyLjkyLTMuMDRzLTEuMTctMy4wNC0yLjkxLTMuMDRjLTEuNzUgMC0yLjkxIDEuMzEtMi45MSAzLjA0Wm0xMy41NSA0LjA4IDQuODgtMTEuMzZoLTEuMzVsLTQuMDMgOS4zOC00LjAzLTkuMzhoLTEuMzZsNC45IDExLjM2aC45OVptNy44NC0xMS4yNWMwIC40Ny0uMzcuODUtLjgzLjg1YS44Ni44NiAwIDAgMS0uODYtLjg1YzAtLjQ2LjM4LS44NS44Ni0uODUuNDcgMCAuODMuMzkuODMuODVabS0uMjggMTEuMjVoLTEuMTN2LThoMS4xM3Y4Wm02LjIuMTZhMy45IDMuOSAwIDAgMCAzLjU2LTEuOTVsLS45MS0uNmEyLjc4IDIuNzggMCAwIDEtMi42NCAxLjUxIDIuODcgMi44NyAwIDAgMS0yLjk2LTIuOTNoNi43NXYtLjNjLS4wMi0yLjU2LTEuNjgtNC4wNS0zLjc2LTQuMDVhNC4wNSA0LjA1IDAgMCAwLTQuMTUgNC4xNmMwIDIuMyAxLjYgNC4xNiA0LjEyIDQuMTZabS0uMDEtNy4yOGMxLjM0IDAgMi40NS44OCAyLjY0IDIuMzJoLTUuNDlhMi44NCAyLjg0IDAgMCAxIDIuODUtMi4zMlptMTMuNTUgNy4xMmgtLjkzbC0yLjEtNi4xLTIuMTQgNi4xaC0uOTJsLTIuNzQtOGgxLjE1bDIuMDggNi4wOCAyLjExLTYuMDhoLjg3bDIuMTEgNi4wOCAyLjA4LTYuMDhoMS4xN2wtMi43NCA4WiIgZmlsbD0iY3VycmVudENvbG9yIj48L3BhdGg+PC9zdmc+")`; +background.style.backgroundRepeat = 'no-repeat'; +background.style.backgroundPosition = 'center'; +background.style.opacity = '0.5'; +container.appendChild(background); +// highlight-end + +const lineSeries = chart.addAreaSeries({ + topColor: AREA_TOP_COLOR, + bottomColor: AREA_BOTTOM_COLOR, + lineColor: LINE_LINE_COLOR, + lineWidth: 2, +}); + +const data = [ + { value: 0, time: 1642425322 }, + // hide-start + { value: 8, time: 1642511722 }, + { value: 10, time: 1642598122 }, + { value: 20, time: 1642684522 }, + { value: 3, time: 1642770922 }, + { value: 43, time: 1642857322 }, + { value: 41, time: 1642943722 }, + { value: 43, time: 1643030122 }, + { value: 56, time: 1643116522 }, + { value: 46, time: 1643202922 }, + // hide-end +]; + +lineSeries.setData(data); + +chart.timeScale().fitContent(); diff --git a/website/tutorials/how_to/watermark-simple.js b/website/tutorials/how_to/watermark-simple.js new file mode 100644 index 0000000000..5687e385f6 --- /dev/null +++ b/website/tutorials/how_to/watermark-simple.js @@ -0,0 +1,53 @@ +// remove-start +// Lightweight Charts Example: Watermark Simple +// https://tradingview.github.io/lightweight-charts/tutorials/how_to/watermark + +// remove-end +const chartOptions = { + layout: { + textColor: CHART_TEXT_COLOR, + background: { type: 'solid', color: CHART_BACKGROUND_COLOR }, + }, +}; +// remove-line +/** @type {import('lightweight-charts').IChartApi} */ +const chart = createChart(document.getElementById('container'), chartOptions); + +// highlight-start +chart.applyOptions({ + watermark: { + visible: true, + fontSize: 24, + horzAlign: 'center', + vertAlign: 'center', + color: 'rgba(171, 71, 188, 0.5)', + text: 'Watermark Example', + }, +}); +// highlight-end + +const lineSeries = chart.addAreaSeries({ + topColor: AREA_TOP_COLOR, + bottomColor: AREA_BOTTOM_COLOR, + lineColor: LINE_LINE_COLOR, + lineWidth: 2, +}); + +const data = [ + { value: 0, time: 1642425322 }, + // hide-start + { value: 8, time: 1642511722 }, + { value: 10, time: 1642598122 }, + { value: 20, time: 1642684522 }, + { value: 3, time: 1642770922 }, + { value: 43, time: 1642857322 }, + { value: 41, time: 1642943722 }, + { value: 43, time: 1643030122 }, + { value: 56, time: 1643116522 }, + { value: 46, time: 1643202922 }, + // hide-end +]; + +lineSeries.setData(data); + +chart.timeScale().fitContent(); diff --git a/website/tutorials/how_to/watermark.mdx b/website/tutorials/how_to/watermark.mdx new file mode 100644 index 0000000000..0af569fca8 --- /dev/null +++ b/website/tutorials/how_to/watermark.mdx @@ -0,0 +1,106 @@ +--- +title: Watermark +sidebar_label: Watermark +description: Examples of how to add a watermark to your chart. +pagination_prev: null +pagination_next: null +keywords: + - watermark + - example +--- + +Lightweight charts has a built-in feature for displaying simple text watermarks on your chart. +This example shows how to configure and add this simple text watermark to your chart. +If you are looking to add a more complex watermark then have a look at the [advanced watermark example](#advanced-watermark-example) +included below. + +## Short answer + +A simple text watermark can be configured and added by specifying the [`watermark`](/docs/api/interfaces/ChartOptions#watermark) property within +the chart options as follows: + +```js +chart.applyOptions({ + watermark: { + visible: true, + fontSize: 24, + horzAlign: 'center', + vertAlign: 'center', + color: 'rgba(171, 71, 188, 0.5)', + text: 'Watermark Example', + }, +}); +``` + +The options available for the watermark are: [Watermark Options](/docs/api/interfaces/WatermarkOptions). + +To have the watermark appear, you need to set `visible` to `true` and ensure that the `text` property isn't empty. + +You can see full [working examples](#examples) below. + +## Resources + +- [Watermark Options](/docs/api/interfaces/WatermarkOptions) + +## Examples + +import UsageGuidePartial from "./_usage-guide-partial.mdx"; + + + +import CodeBlock from "@theme/CodeBlock"; + +### Simple Watermark Example + +import codeSimple from "!!raw-loader!./watermark-simple.js"; + + + {codeSimple} + + +### Advanced Watermark Example + +If a simple text watermark doesn't meet your requirements then you can use the following tips +to rather create your own custom watermark using `html` and `css`. + +We will first set the `background` color of the chart to `transparent` so that we can +place our custom watermark underneath the chart and still see it. + +```js +chart.applyOptions({ + layout: { + // set chart background color to transparent so we can see the elements below + // highlight-next-line + background: { type: 'solid', color: 'transparent' }, + }, +}); +``` + +Next we will create a `div` element, and attach it as a child of the `container` element which is holding the chart. + +By setting the `zIndex` value for this div to be negative it will appear beneath the chart. + +We will position the div using `display: absolute` and by setting `inset: 0px` the div will fill the container. + +You can then style the div to meet your specific needs. + +```js +const container = document.getElementById('container'); +const background = document.createElement('div'); +// place below the chart +background.style.zIndex = -1; +background.style.position = 'absolute'; +// set size and position to match container +background.style.inset = '0px'; +background.style.backgroundImage = `url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyOTIiIGhlaWdodD0iMTI4IiB2aWV3Qm94PSIwIDAgMjkyIDEyOCI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBkPSJtMTgyLjkzIDcuNi42My0uMzdhNjQuMSA2NC4xIDAgMCAwIDIuNDMtNS4zMWw0Ljc3LTEuMzlhNjQuNjggNjQuNjggMCAwIDEtNC43MiAxMC41NGMuMzggMTAuNDUtMy45MyAyMS4xNS0xMS4xIDI5LjM3LTExLjY2IDEzLjQxLTI2Ljk4IDE1Ljk3LTQzLjU3IDEzLjc4bDEuMDctLjk4YTIxLjEgMjEuMSAwIDAgMCAzLjcyLTQuMDUgNDguMzcgNDguMzcgMCAwIDEtMTEuMDQgMi44NGMtMTAuNjUtNS41NC0yMS42NC0xNC45NC0yNC4yNy0yNy4yNyA5LjE5LTE3IDI4Ljk1LTI0LjAxIDQ3LjM5LTE5Ljk0YTIyLjU3IDIyLjU3IDAgMCAwIDUuODYgOS4wMmMtLjEyLTEuOTItLjEtMy44NC0uMS01Ljc2bC4wMS0xLjc4YzQuOCAyLjk2IDkuNjYgNS44NSAxNS41MiA1LjcgNC4wOC0uMSA4LjQtMS41MiAxMy40LTQuNFptLTIyLjU1IDIzLjI4YTguNDggOC40OCAwIDAgMC0xMi40NS0uMzNsLTcuOS03LjI2QTguNiA4LjYgMCAwIDAgMTMyIDEyYy02LjE0IDAtMTAuMjUgNi42My03LjcgMTIuMDlsLTEzLjAyIDEyLjE5Yy00LjEtNC45Ny01LjY4LTkuMy02LjE3LTEwLjk0IDguMzYtMTMuNzIgMjQuNDYtMjAuMTggNDAuMTUtMTcuMDcgMi45MyA2LjkgOC4zOCAxMC43MiAxNC43NyAxMy45NmwtLjMzLTEuMTRjLS43NC0yLjU2LTEuNDctNS4xLTEuNjItNy43OCA3LjA1IDMuNDUgMTQuNiAzLjM1IDIxLjc2LjMxLTQuNzYgNy4yNy0xMS4xMyAxNC4yMi0xOS40NiAxNy4yNlptLTIyLjU2LTQuMTkgOC4wMyA3LjM4QTguNiA4LjYgMCAwIDAgMTU0IDQ1YTguNiA4LjYgMCAwIDAgOC4yNS0xMC41NWM3Ljk5LTMuMDggMTQuMzctOS4zOCAxOS4yOC0xNi4yMy0zLjQ3IDE5LjQ3LTIxLjk2IDM0LjYxLTQxLjkgMzIuOTggMS43Ny0yLjg0IDIuNDktNi4wNiAzLjIxLTkuMjhsLjM1LTEuNTZjLTUuNDcgMy43Ny0xMC42NyA2LjM4LTE3LjM3IDcuNTJhNDkuOSA0OS45IDAgMCAxLTExLjg1LTguNjVsMTIuODMtMTJhOC41OCA4LjU4IDAgMCAwIDExLjAyLS41NFpNMTMyIDE2YTQuNSA0LjUgMCAxIDAgMCA5IDQuNSA0LjUgMCAwIDAgMC05Wm0xNy41IDIwLjVhNC41IDQuNSAwIDEgMSA5IDAgNC41IDQuNSAwIDAgMS05IDBaTTIxLjYzIDcxLjhhMi4zMyAyLjMzIDAgMCAxIDIuMzMgMi4zNCAyLjM0IDIuMzQgMCAwIDEtMi4zMyAyLjM3IDIuMzggMi4zOCAwIDAgMS0yLjM3LTIuMzcgMi4zOCAyLjM4IDAgMCAxIDIuMzctMi4zM1ptMS43NiA4LjJ2MTZoLTMuNTJWODBoMy41MlptLTYuNDYgMTZIMi43OFY3My4yOGgzLjc1djE5LjE0aDEwLjRWOTZabTI2LjM5LTEuMDlWODBIMzkuOHYyLjE0YTYuMjYgNi4yNiAwIDAgMC01LjEyLTIuNDZjLTQuMzIgMC03LjY4IDMuNTgtNy42OCA4LjEgMCA0LjU0IDMuMzYgOC4xMiA3LjY4IDguMTIgMi4yIDAgNC4xNi0xLjA4IDUuMTItMi41djEuNDhjMCAzLjIzLTIuMTggNS00LjgzIDVhNy4wMyA3LjAzIDAgMCAxLTUuMzItMi4zNGwtMi4xNCAyLjUyYzEuNTcgMS43NiA0LjM1IDIuOTUgNy40OSAyLjk1IDQuNzMgMCA4LjMyLTIuNTMgOC4zMi04LjFabS0xMi43Ny03LjEzYTQuNyA0LjcgMCAwIDEgNC43Ny00LjkgNC43IDQuNyAwIDAgMSA0Ljc3IDQuOSA0LjcgNC43IDAgMCAxLTQuNzcgNC45IDQuNyA0LjcgMCAwIDEtNC43Ny00LjlaTTUxLjU4IDk2aC0zLjUyVjcyaDMuNTJ2MTAuMThjLjk2LTEuNiAyLjc4LTIuNSA0Ljg2LTIuNSAzLjcxIDAgNi4xMSAyLjYyIDYuMTEgNi42OVY5NmgtMy41MnYtOS4wNmMwLTIuNTItMS4yOC00LjA2LTMuMzMtNC4wNi0yLjMzIDAtNC4xMiAxLjgyLTQuMTIgNS4yNVY5NlptMjQuODYtLjJ2LTMuMTNjLS41Mi4yLTEuMjIuMzItMS45LjMyLTEuODIgMC0yLjY4LS43My0yLjY4LTIuNzJ2LTcuMTNoNC41OFY4MGgtNC41OHYtNC40NWgtMy41MlY4MGgtMy4zM3YzLjE0aDMuMzN2Ny43YzAgMy42MiAyLjQgNS4zMiA1LjQ3IDUuMzIgMS4wOSAwIDEuOTItLjEzIDIuNjMtLjM1Wm0yMC4zLjJIOTMuNGwtMy41Mi0xMC4zN0w4Ni4zOSA5NmgtMy4zMmwtNS4zOC0xNmgzLjcybDMuNDUgMTEgMy42OC0xMWgyLjY5bDMuNjUgMTEgMy40OS0xMWgzLjc0bC01LjM4IDE2Wm02Ljc2LThjMCA0Ljg2IDMuNDkgOC4zMiA4LjM1IDguMzIgMy4zNiAwIDUuODYtMS40NCA3LjMtMy43MWwtMi43LTEuOTJhNS4wMyA1LjAzIDAgMCAxLTQuNTcgMi40M2MtMi42NSAwLTQuNzctMS43My00LjkzLTQuMzVoMTIuNThjLjAzLS41MS4wMy0uOC4wMy0xLjE1IDAtNS4xNi0zLjUyLTcuOTQtNy43MS03Ljk0QTguMTIgOC4xMiAwIDAgMCAxMDMuNSA4OFptOC4yMi01LjM0YzIuMDUgMCAzLjkgMS4yNCA0LjI5IDMuNTVoLTguOWMuNDgtMi4zNyAyLjU2LTMuNTUgNC42MS0zLjU1Wm0xMy4yMi0xMC44NWEyLjMzIDIuMzMgMCAwIDEgMi4zNCAyLjMzIDIuMzQgMi4zNCAwIDAgMS0yLjM0IDIuMzcgMi4zOCAyLjM4IDAgMCAxLTIuMzctMi4zNyAyLjM4IDIuMzggMCAwIDEgMi4zNy0yLjMzWm0yMS43IDIzLjFWODBoLTMuNTN2Mi4xNGE2LjI2IDYuMjYgMCAwIDAtNS4xMi0yLjQ2Yy00LjMyIDAtNy42OCAzLjU4LTcuNjggOC4xIDAgNC41NCAzLjM2IDguMTIgNy42OCA4LjEyIDIuMiAwIDQuMTYtMS4wOCA1LjEyLTIuNXYxLjQ4YzAgMy4yMy0yLjE4IDUtNC44MyA1YTcuMDMgNy4wMyAwIDAgMS01LjMxLTIuMzRsLTIuMTUgMi41MmMxLjU3IDEuNzYgNC4zNiAyLjk1IDcuNSAyLjk1IDQuNzMgMCA4LjMxLTIuNTMgOC4zMS04LjFaTTEyNi43IDk2aC0zLjUyVjgwaDMuNTJ2MTZabTcuMTYtOC4yMmE0LjcgNC43IDAgMCAxIDQuNzctNC45IDQuNyA0LjcgMCAwIDEgNC43NyA0LjkgNC43IDQuNyAwIDAgMS00Ljc3IDQuOSA0LjcgNC43IDAgMCAxLTQuNzctNC45Wk0xNTQuOSA5NmgtMy41MlY3MmgzLjUydjEwLjE4Yy45Ni0xLjYgMi43OC0yLjUgNC44Ni0yLjUgMy43MSAwIDYuMTEgMi42MiA2LjExIDYuNjlWOTZoLTMuNTJ2LTkuMDZjMC0yLjUyLTEuMjgtNC4wNi0zLjMyLTQuMDYtMi4zNCAwLTQuMTMgMS44Mi00LjEzIDUuMjVWOTZabTI0Ljg2LS4ydi0zLjEzYy0uNTEuMi0xLjIyLjMyLTEuODkuMzItMS44MiAwLTIuNjktLjczLTIuNjktMi43MnYtNy4xM2g0LjU4VjgwaC00LjU4di00LjQ1aC0zLjUyVjgwaC0zLjMzdjMuMTRoMy4zM3Y3LjdjMCAzLjYyIDIuNCA1LjMyIDUuNDcgNS4zMiAxLjEgMCAxLjkyLS4xMyAyLjYzLS4zNVptMjEuNTkuNThhMTEuNjcgMTEuNjcgMCAwIDEtMTEuNzUtMTEuNzRjMC02LjU2IDUuMjItMTEuNzQgMTEuNzUtMTEuNzQgNC40NSAwIDguMjIgMi4yNyAxMC4yNCA1Ljc2bC0zLjIzIDEuODVhNy45NCA3Ljk0IDAgMCAwLTcuMDEtNCA3Ljk2IDcuOTYgMCAwIDAtNy45NyA4LjEzIDcuOTYgNy45NiAwIDAgMCA3Ljk3IDguMTMgNy45NCA3Ljk0IDAgMCAwIDctNGwzLjI0IDEuODVhMTEuNjYgMTEuNjYgMCAwIDEtMTAuMjQgNS43NlptMTMuNC0uMzhoMy41MnYtNy44N2MwLTMuNDMgMS44LTUuMjUgNC4xMy01LjI1IDIuMDUgMCAzLjMzIDEuNTQgMy4zMyA0LjA2Vjk2aDMuNTJ2LTkuNjNjMC00LjA3LTIuNC02LjY5LTYuMTEtNi42OS0yLjA4IDAtMy45LjktNC44NyAyLjVWNzJoLTMuNTJ2MjRabTI1LjU2LjMyYy00LjM4IDAtNy43LTMuNzQtNy43LTguMzJzMy4zMi04LjMyIDcuNy04LjMyYzIuMyAwIDQuMjMgMS4xOCA1LjEyIDIuNDZWODBoMy41MnYxNmgtMy41MnYtMi4xNGE2LjM4IDYuMzggMCAwIDEtNS4xMiAyLjQ2Wm0uNjQtMy4yYzIuODUgMCA0Ljc3LTIuMjQgNC43Ny01LjEycy0xLjkyLTUuMTItNC43Ny01LjEyYy0yLjg0IDAtNC43NiAyLjI0LTQuNzYgNS4xMnMxLjkxIDUuMTIgNC43NiA1LjEyWk0yNTMuNzEgOTZoMy41MnYtNy44YzAtMy4yIDEuODMtNC45IDMuODQtNC45LjY0IDAgMS4xNS4xIDEuNzYuMjh2LTMuNjFjLS40OC0uMS0uOTMtLjEzLTEuMzctLjEzYTQuNSA0LjUgMCAwIDAtNC4yMyAzVjgwaC0zLjUydjE2Wm0yMS43My0zLjMzdjMuMTRjLS43LjIyLTEuNTQuMzUtMi42My4zNS0zLjA3IDAtNS40Ny0xLjctNS40Ny01LjMxdi03LjcxaC0zLjMzVjgwaDMuMzN2LTQuNDVoMy41MlY4MGg0LjU4djMuMTRoLTQuNTh2Ny4xM2MwIDEuOTkuODYgMi43MiAyLjY5IDIuNzIuNjcgMCAxLjM3LS4xMyAxLjg5LS4zMlptMTQuMjEtMS4zMWMwLTIuNjItMS42Ni00LjAzLTQuNDgtNC44NmwtMS42My0uNDhjLTEuNTctLjQ1LTEuOTItMS4xMi0xLjkyLTEuOSAwLS45NSAxLjA5LTEuNSAyLjE1LTEuNSAxLjMgMCAyLjMzLjY0IDMuMDQgMS42NGwyLjQzLTEuODZjLTEuMTItMS43Ni0zLjAxLTIuNzItNS40MS0yLjcyLTMuMiAwLTUuNyAxLjczLTUuNzMgNC41OC0uMDMgMi4zNiAxLjQxIDQuMTIgNC4yIDQuOWwxLjQuMzhjMS45Mi41NyAyLjQ3IDEuMTIgMi40NyAyLjA0IDAgMS4xMi0xLjA2IDEuNy0yLjMgMS43LTEuNjQgMC0zLjItLjgtMy44NS0yLjJsLTIuNTkgMS44NWMxLjE1IDIuMjcgMy41OCAzLjM5IDYuNDMgMy4zOSAzLjMgMCA1LjgtMS44OSA1LjgtNC45NlptLTE0My4zOCAyMS40YzAgLjQ2LS4zNy44NC0uODMuODRhLjg2Ljg2IDAgMCAxLS44Ny0uODVjMC0uNDYuMzktLjg1Ljg3LS44NS40NiAwIC44My4zOS44My44NVptLS4yOSAxMS4yNGgtMS4xMnYtOGgxLjEydjhabS01Mi4wMi4xNmE0LjA0IDQuMDQgMCAwIDAgMy45OC00LjE2IDQuMDQgNC4wNCAwIDAgMC0zLjk4LTQuMTZjLTEuMjQgMC0yLjM5LjY0LTIuOTYgMS41VjExMmgtMS4xMnYxMkg5MXYtMS4zNGMuNTcuODYgMS43MiAxLjUgMi45NiAxLjVabS0uMTItMS4wNGMtMS43NCAwLTIuOTQtMS40LTIuOTQtMy4xMiAwLTEuNzMgMS4yLTMuMTIgMi45NC0zLjEyIDEuNzUgMCAyLjk1IDEuNCAyLjk1IDMuMTIgMCAxLjczLTEuMiAzLjEyLTIuOTUgMy4xMlptNy45IDQuMjIgNS4zLTExLjM0aC0xLjI2bC0yLjkzIDYuMzUtMi45My02LjM1aC0xLjI0bDMuNTUgNy42LTEuNzYgMy43NGgxLjI2Wk0xMTUuMyAxMjRoLTEuMnYtMTAuMmgtMy42OHYtMS4xNmg4LjU2djEuMTVoLTMuNjhWMTI0Wm0zLjgyIDBoMS4xMnYtNC4wMmMwLTIuMDQgMS4yMy0yLjk0IDIuMjItMi45NC4yNCAwIC40NS4wMy42Ny4xMXYtMS4xN2EyLjQ0IDIuNDQgMCAwIDAtMi45IDEuNjZWMTE2aC0xLjExdjhabTExLjcyLTEuMzRhMy42NCAzLjY0IDAgMCAxLTIuOTYgMS41IDQuMDQgNC4wNCAwIDAgMS0zLjk4LTQuMTYgNC4wNCA0LjA0IDAgMCAxIDMuOTgtNC4xNmMxLjIzIDAgMi4zOS42NCAyLjk2IDEuNVYxMTZoMS4xMnY4aC0xLjEydi0xLjM0Wm0tNS44LTIuNjZjMCAxLjczIDEuMiAzLjEyIDIuOTUgMy4xMiAxLjc1IDAgMi45NS0xLjQgMi45NS0zLjEyIDAtMS43My0xLjItMy4xMi0yLjk1LTMuMTItMS43NCAwLTIuOTQgMS40LTIuOTQgMy4xMlptMTIuOTggNC4xNmMxLjIzIDAgMi4zOS0uNjQgMi45Ni0xLjVWMTI0aDEuMTJ2LTEySDE0MXY1LjM0YTMuNjQgMy42NCAwIDAgMC0yLjk2LTEuNSA0LjA0IDQuMDQgMCAwIDAtMy45OCA0LjE2IDQuMDQgNC4wNCAwIDAgMCAzLjk4IDQuMTZabS4xMS0xLjA0Yy0xLjc0IDAtMi45NC0xLjQtMi45NC0zLjEyIDAtMS43MyAxLjItMy4xMiAyLjk0LTMuMTIgMS43NSAwIDIuOTUgMS40IDIuOTUgMy4xMiAwIDEuNzMtMS4yIDMuMTItMi45NSAzLjEyWm0xMC42Ljg4aDEuMTF2LTMuOThjMC0xLjk5IDEuMS0zLjE0IDIuNS0zLjE0IDEuMTkgMCAyLjAyLjg2IDIuMDIgMi4yN1YxMjRoMS4xMnYtNWMwLTEuOTYtMS4yNy0zLjE2LTMuMDEtMy4xNi0xLjA0IDAtMi4wNS40NS0yLjYzIDEuNVYxMTZoLTEuMTF2OFptMTYuNzEtLjQyYzAgMi42MS0xLjcyIDMuOTItMy45NSAzLjkyLTEuODQgMC0zLjE3LS44My0zLjc3LTEuNzRsLjg4LS43NWEzLjQgMy40IDAgMCAwIDIuOSAxLjQ1YzEuMzcgMCAyLjgyLS44MyAyLjgyLTIuOTR2LTEuMDJjLS41Ny44Ni0xLjcgMS41LTIuOTIgMS41YTMuOTQgMy45NCAwIDAgMS0zLjk2LTQuMDggMy45NCAzLjk0IDAgMCAxIDMuOTYtNC4wOGMxLjIzIDAgMi4zNS42NCAyLjkyIDEuNVYxMTZoMS4xMnY3LjU4Wm0tNi44NC0zLjY2YzAgMS43MyAxLjE2IDMuMDQgMi45IDMuMDQgMS43NSAwIDIuOTItMS4zMSAyLjkyLTMuMDRzLTEuMTctMy4wNC0yLjkxLTMuMDRjLTEuNzUgMC0yLjkxIDEuMzEtMi45MSAzLjA0Wm0xMy41NSA0LjA4IDQuODgtMTEuMzZoLTEuMzVsLTQuMDMgOS4zOC00LjAzLTkuMzhoLTEuMzZsNC45IDExLjM2aC45OVptNy44NC0xMS4yNWMwIC40Ny0uMzcuODUtLjgzLjg1YS44Ni44NiAwIDAgMS0uODYtLjg1YzAtLjQ2LjM4LS44NS44Ni0uODUuNDcgMCAuODMuMzkuODMuODVabS0uMjggMTEuMjVoLTEuMTN2LThoMS4xM3Y4Wm02LjIuMTZhMy45IDMuOSAwIDAgMCAzLjU2LTEuOTVsLS45MS0uNmEyLjc4IDIuNzggMCAwIDEtMi42NCAxLjUxIDIuODcgMi44NyAwIDAgMS0yLjk2LTIuOTNoNi43NXYtLjNjLS4wMi0yLjU2LTEuNjgtNC4wNS0zLjc2LTQuMDVhNC4wNSA0LjA1IDAgMCAwLTQuMTUgNC4xNmMwIDIuMyAxLjYgNC4xNiA0LjEyIDQuMTZabS0uMDEtNy4yOGMxLjM0IDAgMi40NS44OCAyLjY0IDIuMzJoLTUuNDlhMi44NCAyLjg0IDAgMCAxIDIuODUtMi4zMlptMTMuNTUgNy4xMmgtLjkzbC0yLjEtNi4xLTIuMTQgNi4xaC0uOTJsLTIuNzQtOGgxLjE1bDIuMDggNi4wOCAyLjExLTYuMDhoLjg3bDIuMTEgNi4wOCAyLjA4LTYuMDhoMS4xN2wtMi43NCA4WiIgZmlsbD0iY3VycmVudENvbG9yIj48L3BhdGg+PC9zdmc+")`; +background.style.backgroundRepeat = 'no-repeat'; +background.style.backgroundPosition = 'center'; +background.style.opacity = '0.5'; +container.appendChild(background); +``` + +import codeAdvanced from "!!raw-loader!./watermark-advanced.js"; + + + {codeAdvanced} + diff --git a/website/tutorials/index.mdx b/website/tutorials/index.mdx index 3c44c4946e..607ce5b1b2 100644 --- a/website/tutorials/index.mdx +++ b/website/tutorials/index.mdx @@ -9,11 +9,16 @@ import ReactLogo from "@site/src/img/react.svg"; # Tutorials -import VersionWarningAdmonition from '@site/src/components/VersionWarningAdmonition' +import VersionWarningAdmonition from "@site/src/components/VersionWarningAdmonition"; - + + ## Guides @@ -49,3 +54,32 @@ If you think that a tutorial is missing feel free to ask [in the discussions](ht or submit your own. ::: + +## How To / Examples + +A collection of code examples showcasing the various capabilities of the library, and how to implement common additional features. + +import { useDocsSidebar } from "@docusaurus/theme-common/internal"; +export const ExamplesList = () => { + const examplesCategory = useDocsSidebar().items.find( + item => item.type === "category" && item.label === "How To / Examples" + ); + const examples = examplesCategory.items.filter(doc => doc.type === "link"); + return ( + + ); +}; + + + +:::tip + +More examples can be viewed on the [Lightweight Charts product page](https://www.tradingview.com/lightweight-charts/). + +::: From 12d0fcc999b54c3518d2662309a903a89316eaab Mon Sep 17 00:00:00 2001 From: Mark Silverwood Date: Thu, 18 Aug 2022 19:29:48 +0200 Subject: [PATCH 125/188] change JSFiddle to use TradingView account Changed the JSFiddle link to one created by the TradingView account. --- website/tutorials/customization/intro.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/tutorials/customization/intro.mdx b/website/tutorials/customization/intro.mdx index 4f57d6084c..0cda75cc22 100644 --- a/website/tutorials/customization/intro.mdx +++ b/website/tutorials/customization/intro.mdx @@ -77,7 +77,7 @@ You can either: - Download the file and then edit and run the example on your computer, -- or [open this JSFiddle](https://jsfiddle.net/msilverwood/391hxf6b/) and then edit and run the example within the browser. +- or [open this JSFiddle](https://jsfiddle.net/TradingView/5h76xeqk/) and then edit and run the example within the browser. :::tip From 542f97c2e1387dd784348ecc576cd762ee975458 Mon Sep 17 00:00:00 2001 From: Mark Silverwood Date: Thu, 18 Aug 2022 19:37:53 +0200 Subject: [PATCH 126/188] Replaced CodeSandbox link with anonymous authored link Replaced the CodeSandbox link with one created by an anonymous user as we don't have a TV account here yet. --- website/tutorials/how_to/_usage-guide-partial.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/tutorials/how_to/_usage-guide-partial.mdx b/website/tutorials/how_to/_usage-guide-partial.mdx index 96e607a996..746ed3e043 100644 --- a/website/tutorials/how_to/_usage-guide-partial.mdx +++ b/website/tutorials/how_to/_usage-guide-partial.mdx @@ -5,7 +5,7 @@ - That `createChart` has already been imported. See [Getting Started](/docs#creating-a-chart) for more information, - and that there is an html div element on the page with an `id` of `container`. -Here is an example skeleton setup: [Code Sandbox](https://codesandbox.io/s/lightweight-charts-example-wq1emv?file=/src/index.js). +Here is an example skeleton setup: [Code Sandbox](https://codesandbox.io/s/lightweight-charts-skeleton-n67pm6). You can paste the provided code below the `// REPLACE EVERYTHING BELOW HERE` comment. :::tip From 5fc81b740844cd3d32a914273849055f8ef0f9a4 Mon Sep 17 00:00:00 2001 From: Mark Silverwood Date: Thu, 18 Aug 2022 20:05:03 +0200 Subject: [PATCH 127/188] added @types/react to website dependencies Added `@types/react` to website dependencies because we are running v17 and v18 types were present when not specified. --- website/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/website/package.json b/website/package.json index 7d0ca33479..889297b2a8 100644 --- a/website/package.json +++ b/website/package.json @@ -17,6 +17,7 @@ "@docusaurus/preset-classic": "2.0.1", "@docusaurus/theme-search-algolia": "2.0.1", "@tsconfig/docusaurus": "~1.0.6", + "@types/react": "~17.0.39", "cross-env": "~7.0.3", "docusaurus-plugin-typedoc": "0.17.5", "lightweight-charts": "~3.8.0", From 5780f2ca59a55ba32cb0a875eca5565f701292a1 Mon Sep 17 00:00:00 2001 From: Mark Silverwood Date: Thu, 18 Aug 2022 20:05:25 +0200 Subject: [PATCH 128/188] fixed broken links in customization guide and fix sidebar order --- website/tutorials/customization/chart-colors.mdx | 2 +- website/tutorials/customization/conclusion.mdx | 2 +- website/tutorials/customization/data-points.mdx | 2 +- website/tutorials/customization/finishing-touches.mdx | 2 +- website/tutorials/customization/intro.mdx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/website/tutorials/customization/chart-colors.mdx b/website/tutorials/customization/chart-colors.mdx index b41af02ac8..bb8dcf4d70 100644 --- a/website/tutorials/customization/chart-colors.mdx +++ b/website/tutorials/customization/chart-colors.mdx @@ -99,7 +99,7 @@ const chart = LightweightCharts.createChart( ## Adjusting the border colors for the axes -We can set the border colors for the price and time scales by using the `applyOptions` method on the [IPriceScale](/docs/api/interfaces/IPriceScaleApi) and [ITimeScale](/docs/api/interfaces/ITimeScale) instances. We can get references to these instances by using the `priceScale()` and `timeScale()` methods on a chart instance. +We can set the border colors for the price and time scales by using the `applyOptions` method on the [IPriceScale](/docs/api/interfaces/IPriceScaleApi) and [ITimeScale](/docs/api/interfaces/ITimeScaleApi) instances. We can get references to these instances by using the `priceScale()` and `timeScale()` methods on a chart instance. You can add the following code beneath the `createChart` method call. diff --git a/website/tutorials/customization/conclusion.mdx b/website/tutorials/customization/conclusion.mdx index cff9677cd3..a9283e2362 100644 --- a/website/tutorials/customization/conclusion.mdx +++ b/website/tutorials/customization/conclusion.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 10 +sidebar_position: 11 title: Conclusion pagination_title: Conclusion sidebar_label: Conclusion diff --git a/website/tutorials/customization/data-points.mdx b/website/tutorials/customization/data-points.mdx index 590822c4ad..63fac42bc0 100644 --- a/website/tutorials/customization/data-points.mdx +++ b/website/tutorials/customization/data-points.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 8 +sidebar_position: 9 title: Data points pagination_title: Data points sidebar_label: Data points diff --git a/website/tutorials/customization/finishing-touches.mdx b/website/tutorials/customization/finishing-touches.mdx index d1d3d61274..0479ea0f46 100644 --- a/website/tutorials/customization/finishing-touches.mdx +++ b/website/tutorials/customization/finishing-touches.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 9 +sidebar_position: 10 title: Finishing touches pagination_title: Finishing touches sidebar_label: Finishing touches diff --git a/website/tutorials/customization/intro.mdx b/website/tutorials/customization/intro.mdx index 0cda75cc22..ef688d6efe 100644 --- a/website/tutorials/customization/intro.mdx +++ b/website/tutorials/customization/intro.mdx @@ -60,7 +60,7 @@ The tutorial will assume that you've already read the [Getting Started](/docs) s ## Terminology - **Data Series (aka data/dataset):** A collection of data points representing a specific metric over time. -- **Series Type:** A series type specifies how to draw the data on the chart. For example, a line series type will plot the data series on the chart as a series of the data points connected by straight line segments. Available series types: [Series types | Lightweight Charts](/series-types) +- **Series Type:** A series type specifies how to draw the data on the chart. For example, a line series type will plot the data series on the chart as a series of the data points connected by straight line segments. Available series types: [Series types | Lightweight Charts](/docs/series-types) - **Series:** A combination of a specified series type and a data series. - **Price Scale:** Price Scale (or price axis) is a vertical scale that mostly maps prices to coordinates and vice versa. - **Time Scale:** Time scale (or time axis) is a horizontal scale at the bottom of the chart that displays the time of bars. From b48da0572e00f2aacafa9ec7930308e5338abf1f Mon Sep 17 00:00:00 2001 From: Mark Silverwood Date: Tue, 23 Aug 2022 17:52:20 +0200 Subject: [PATCH 129/188] added note about `npm run serve` and embedded html examples Added note about incompatibility between embedded html examples and using `docusaurus serve` command. --- website/README.md | 10 ++++++++++ website/tutorials/customization/intro.mdx | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/website/README.md b/website/README.md index effee65bc1..24d5bec8d2 100644 --- a/website/README.md +++ b/website/README.md @@ -28,6 +28,16 @@ _Note_: API documentation will not be generated unless you have already built th This command generates static content in the `build` directory. +## Serve Build Locally + +```console +npm run serve +``` + +_Note_: Embedded `.html` examples won't display correctly when using this command but will work correctly when hosted online. + +This command serves the built website locally. + ## Deployment ```console diff --git a/website/tutorials/customization/intro.mdx b/website/tutorials/customization/intro.mdx index ef688d6efe..91acaa92c1 100644 --- a/website/tutorials/customization/intro.mdx +++ b/website/tutorials/customization/intro.mdx @@ -20,6 +20,10 @@ This tutorial provides an introduction to customizing Lightweight Chart's appear Before we get started, let us have a look at what we will be building in this tutorial. +