├── .circleci └── config.yml ├── .editorconfig ├── .eslintignore ├── .eslintrc.cjs ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ ├── feature_request.md │ └── other.md ├── PULL_REQUEST_TEMPLATE.md ├── logo.svg └── workflows │ ├── deploy.yml │ └── size-limit.yml ├── .gitignore ├── .mailmap ├── .markdownlint.json ├── .npmrc ├── .puppeteerrc.cjs ├── .size-limit.js ├── .vscode ├── launch.json └── settings.json ├── BUILDING.md ├── CONTRIBUTING.md ├── LICENSE ├── NOTICE ├── README.md ├── debug ├── .gitignore ├── .npmrc ├── README.md ├── default │ ├── .global.json │ ├── .local.json │ ├── index.html │ ├── index.ts │ └── tsconfig.json └── package.json ├── dts-config.json ├── package-lock.json ├── package.json ├── packages └── create-lwc-plugin │ ├── .gitignore │ ├── BUILDING.md │ ├── LICENSE │ ├── README.md │ ├── build.config.ts │ ├── index.js │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── helpers │ │ ├── io.ts │ │ ├── package.ts │ │ └── validation.ts │ ├── index.ts │ └── questions.ts │ ├── template-common │ ├── README.md │ ├── _gitignore │ ├── compile.mjs │ ├── index.html │ ├── package.json │ ├── src │ │ ├── helpers │ │ │ ├── dimensions │ │ │ │ ├── common.ts │ │ │ │ ├── crosshair-width.ts │ │ │ │ ├── full-width.ts │ │ │ │ └── positions.ts │ │ │ └── time.ts │ │ ├── vite-env.d.ts │ │ └── vite.config.js │ └── tsconfig.json │ ├── template-primitive │ └── src │ │ ├── axis-pane-renderer.ts │ │ ├── axis-pane-view.ts │ │ ├── axis-view.ts │ │ ├── data-source.ts │ │ ├── example │ │ ├── example.ts │ │ └── index.html │ │ ├── helpers │ │ └── assertions.ts │ │ ├── options.ts │ │ ├── pane-renderer.ts │ │ ├── pane-view.ts │ │ ├── plugin-base.ts │ │ ├── sample-data.ts │ │ └── template-entry.ts │ ├── template-series │ └── src │ │ ├── data.ts │ │ ├── example │ │ ├── example.ts │ │ └── index.html │ │ ├── helpers │ │ └── dimensions │ │ │ ├── candles.ts │ │ │ └── columns.ts │ │ ├── options.ts │ │ ├── renderer.ts │ │ ├── sample-data.ts │ │ └── template-entry.ts │ └── tsconfig.json ├── plugin-examples ├── .gitignore ├── README.md ├── build-website.mjs ├── compile.mjs ├── index.html ├── package-lock.json ├── package.json ├── src │ ├── combined-examples │ │ └── delta-brushable │ │ │ ├── example.ts │ │ │ └── index.html │ ├── examples-base.css │ ├── helpers │ │ ├── assertions.ts │ │ ├── closest-index.ts │ │ ├── delegate.ts │ │ ├── dimensions │ │ │ ├── candles.ts │ │ │ ├── columns.ts │ │ │ ├── common.ts │ │ │ ├── crosshair-width.ts │ │ │ ├── full-width.ts │ │ │ └── positions.ts │ │ ├── min-max-in-range.ts │ │ ├── simple-clone.ts │ │ └── time.ts │ ├── index.html │ ├── plugins │ │ ├── anchored-text │ │ │ ├── anchored-text.ts │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ │ └── package.json │ │ ├── background-shade-series │ │ │ ├── background-shade-series.ts │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ │ ├── options.ts │ │ │ ├── package.json │ │ │ └── renderer.ts │ │ ├── bands-indicator │ │ │ ├── bands-indicator.ts │ │ │ └── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ ├── box-whisker-series │ │ │ ├── box-whisker-series.ts │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ │ ├── options.ts │ │ │ ├── renderer.ts │ │ │ └── sample-data.ts │ │ ├── brushable-area-series │ │ │ ├── brushable-area-series.ts │ │ │ ├── data.ts │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ │ ├── options.ts │ │ │ └── renderer.ts │ │ ├── delta-tooltip │ │ │ ├── crosshair-line-pane.ts │ │ │ ├── delta-tooltip-pane.ts │ │ │ ├── delta-tooltip.ts │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ │ └── multi-touch-chart-events.ts │ │ ├── expiring-price-alerts │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ │ ├── expiring-price-alerts.ts │ │ │ ├── icons.ts │ │ │ ├── iexpiring-price-alerts.ts │ │ │ ├── options.ts │ │ │ ├── primitive.ts │ │ │ ├── renderer.ts │ │ │ └── sample-data.ts │ │ ├── grouped-bars-series │ │ │ ├── data.ts │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ │ ├── grouped-bars-series.ts │ │ │ ├── options.ts │ │ │ └── renderer.ts │ │ ├── heatmap-series │ │ │ ├── bell-curve-data.ts │ │ │ ├── data.ts │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ ├── example2.html │ │ │ │ ├── example2.ts │ │ │ │ └── index.html │ │ │ ├── heatmap-series.ts │ │ │ ├── options.ts │ │ │ ├── renderer.ts │ │ │ └── sample-heatmap-data.ts │ │ ├── highlight-bar-crosshair │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ │ └── highlight-bar-crosshair.ts │ │ ├── hlc-area-series │ │ │ ├── data.ts │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ │ ├── hlc-area-series.ts │ │ │ ├── options.ts │ │ │ └── renderer.ts │ │ ├── image-watermark │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ ├── image.svg │ │ │ │ └── index.html │ │ │ └── image-watermark.ts │ │ ├── lollipop-series │ │ │ ├── data.ts │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ │ ├── lollipop-series.ts │ │ │ ├── options.ts │ │ │ └── renderer.ts │ │ ├── overlay-price-scale │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ │ └── overlay-price-scale.ts │ │ ├── partial-price-line │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ │ └── partial-price-line.ts │ │ ├── plugin-base.ts │ │ ├── rectangle-drawing-tool │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ │ └── rectangle-drawing-tool.ts │ │ ├── rounded-candles-series │ │ │ ├── data.ts │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ │ ├── helpers.ts │ │ │ ├── renderer.ts │ │ │ └── rounded-candles-series.ts │ │ ├── session-highlighting │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ │ └── session-highlighting.ts │ │ ├── stacked-area-series │ │ │ ├── data.ts │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ │ ├── options.ts │ │ │ ├── renderer.ts │ │ │ └── stacked-area-series.ts │ │ ├── stacked-bars-series │ │ │ ├── data.ts │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ │ ├── options.ts │ │ │ ├── renderer.ts │ │ │ └── stacked-bars-series.ts │ │ ├── tooltip │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ │ ├── tooltip-element.ts │ │ │ └── tooltip.ts │ │ ├── trend-line │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ │ └── trend-line.ts │ │ ├── user-price-alerts │ │ │ ├── constants.ts │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ │ ├── irenderer-data.ts │ │ │ ├── mouse.ts │ │ │ ├── pane-renderer.ts │ │ │ ├── pane-view.ts │ │ │ ├── price-scale-pane-renderer.ts │ │ │ ├── renderer-base.ts │ │ │ ├── state.ts │ │ │ └── user-price-alerts.ts │ │ ├── user-price-lines │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ │ └── user-price-lines.ts │ │ ├── vertical-line │ │ │ ├── example │ │ │ │ ├── example.ts │ │ │ │ └── index.html │ │ │ └── vertical-line.ts │ │ └── volume-profile │ │ │ ├── example │ │ │ ├── example.ts │ │ │ └── index.html │ │ │ └── volume-profile.ts │ ├── sample-data.ts │ ├── style.css │ ├── vite-env.d.ts │ └── vite.config.js └── tsconfig.json ├── rollup.config.js ├── scripts ├── .eslintrc.cjs ├── check-dts-changes.sh ├── check-markdown-links.js ├── check-typings-for-duplicates.js ├── clean-package-json.js ├── compare-size-with-merge-base.js ├── githooks │ ├── install.js │ └── pre-commit │ │ └── lint.js ├── no-crlf.sh ├── run-coverage-tests.sh ├── run-graphics-tests.sh ├── run-interactions-tests.sh ├── run-memleaks-tests.sh └── trailing-newlines.sh ├── src ├── api │ ├── candlestick-series-api.ts │ ├── chart-api.ts │ ├── create-chart.ts │ ├── create-options-chart.ts │ ├── create-yield-curve-chart.ts │ ├── get-series-data-creator.ts │ ├── ichart-api.ts │ ├── ipane-api.ts │ ├── ipane-primitive-api.ts │ ├── iprice-line.ts │ ├── iprice-scale-api.ts │ ├── iseries-api.ts │ ├── iseries-primitive-api.ts │ ├── itime-scale-api.ts │ ├── iyield-chart-api.ts │ ├── options │ │ ├── chart-options-defaults.ts │ │ ├── crosshair-options-defaults.ts │ │ ├── grid-options-defaults.ts │ │ ├── layout-options-defaults.ts │ │ ├── price-line-options-defaults.ts │ │ ├── price-scale-options-defaults.ts │ │ ├── series-options-defaults.ts │ │ ├── time-scale-options-defaults.ts │ │ └── yield-curve-chart-options-defaults.ts │ ├── pane-api.ts │ ├── price-line-api.ts │ ├── price-scale-api.ts │ ├── series-api.ts │ ├── time-scale-api.ts │ ├── tsconfig.composite.json │ └── yield-chart-api.ts ├── formatters │ ├── date-formatter.ts │ ├── date-time-formatter.ts │ ├── format-date.ts │ ├── iprice-formatter.ts │ ├── percentage-formatter.ts │ ├── price-formatter.ts │ ├── time-formatter.ts │ ├── tsconfig.composite.json │ └── volume-formatter.ts ├── gui │ ├── attribution-logo-widget.ts │ ├── canvas-utils.ts │ ├── chart-widget.ts │ ├── draw-functions.ts │ ├── iaxis-view-getters.ts │ ├── internal-layout-sizes-hints.ts │ ├── ipane-view-getter.ts │ ├── mouse-event-handler.ts │ ├── pane-separator.ts │ ├── pane-widget.ts │ ├── price-axis-stub.ts │ ├── price-axis-widget.ts │ ├── time-axis-widget.ts │ └── tsconfig.composite.json ├── helpers │ ├── algorithms.ts │ ├── assertions.ts │ ├── browsers.ts │ ├── canvas-helpers.ts │ ├── delegate.ts │ ├── events.ts │ ├── idestroyable.ts │ ├── is-running-on-client-side.ts │ ├── isubscription.ts │ ├── logger.ts │ ├── make-font.ts │ ├── mathex.ts │ ├── mutable.ts │ ├── nominal.ts │ ├── strict-type-checks.ts │ └── tsconfig.composite.json ├── index.ts ├── model │ ├── autoscale-info-impl.ts │ ├── bar.ts │ ├── chart-model.ts │ ├── colors.ts │ ├── coordinate.ts │ ├── crosshair.ts │ ├── custom-price-line.ts │ ├── data-consumer.ts │ ├── data-layer.ts │ ├── data-source.ts │ ├── data-validators.ts │ ├── default-price-scale.ts │ ├── formatted-labels-cache.ts │ ├── get-series-plot-row-creator.ts │ ├── grid.ts │ ├── horz-scale-behavior-price │ │ ├── horz-scale-behaviour-price.ts │ │ ├── options.ts │ │ └── types.ts │ ├── horz-scale-behavior-time │ │ ├── default-tick-mark-formatter.ts │ │ ├── horz-scale-behavior-time.ts │ │ ├── time-based-chart-options.ts │ │ ├── time-scale-point-weight-generator.ts │ │ ├── time-utils.ts │ │ └── types.ts │ ├── icustom-series.ts │ ├── idata-source.ts │ ├── ihorz-scale-behavior.ts │ ├── invalidate-mask.ts │ ├── ipane-primitive.ts │ ├── iprice-data-source.ts │ ├── iseries-primitive.ts │ ├── iseries.ts │ ├── kinetic-animation.ts │ ├── layout-options.ts │ ├── localization-options.ts │ ├── magnet.ts │ ├── pane-hit-test.ts │ ├── pane-primitive-wrapper.ts │ ├── pane.ts │ ├── plot-data.ts │ ├── plot-list.ts │ ├── point.ts │ ├── price-data-source.ts │ ├── price-formatter-fn.ts │ ├── price-line-options.ts │ ├── price-range-impl.ts │ ├── price-scale-conversions.ts │ ├── price-scale.ts │ ├── price-tick-mark-builder.ts │ ├── price-tick-span-calculator.ts │ ├── range-impl.ts │ ├── series-bar-colorer.ts │ ├── series-data.ts │ ├── series-options.ts │ ├── series-primitive-wrapper.ts │ ├── series.ts │ ├── series │ │ ├── area-pane-view.ts │ │ ├── area-series.ts │ │ ├── bar-series.ts │ │ ├── bars-pane-view-base.ts │ │ ├── bars-pane-view.ts │ │ ├── baseline-pane-view.ts │ │ ├── baseline-series.ts │ │ ├── candlestick-series.ts │ │ ├── candlesticks-pane-view.ts │ │ ├── custom-pane-view.ts │ │ ├── custom-series.ts │ │ ├── histogram-pane-view.ts │ │ ├── histogram-series.ts │ │ ├── line-pane-view-base.ts │ │ ├── line-pane-view.ts │ │ ├── line-series.ts │ │ ├── pane-view.ts │ │ ├── series-def.ts │ │ └── series-pane-view-base.ts │ ├── sort-sources.ts │ ├── text-width-cache.ts │ ├── tick-marks.ts │ ├── time-data.ts │ ├── time-scale-visible-range.ts │ ├── time-scale.ts │ ├── touch-mouse-event-data.ts │ └── yield-curve-horz-scale-behavior │ │ ├── yield-curve-chart-options.ts │ │ └── yield-curve-horz-scale-behavior.ts ├── plugins │ ├── image-watermark │ │ ├── options.ts │ │ ├── pane-renderer.ts │ │ ├── pane-view.ts │ │ └── primitive.ts │ ├── pane-primitive-wrapper.ts │ ├── primitive-wrapper-base.ts │ ├── primitive-wrapper.ts │ ├── series-markers │ │ ├── options.ts │ │ ├── pane-view.ts │ │ ├── primitive.ts │ │ ├── renderer.ts │ │ ├── series-markers-arrow.ts │ │ ├── series-markers-circle.ts │ │ ├── series-markers-square.ts │ │ ├── series-markers-text.ts │ │ ├── types.ts │ │ ├── utils.ts │ │ └── wrapper.ts │ ├── series-primitive-adapter.ts │ ├── text-watermark │ │ ├── options.ts │ │ ├── pane-renderer.ts │ │ ├── pane-view.ts │ │ └── primitive.ts │ ├── types.ts │ └── up-down-markers-plugin │ │ ├── expiring-markers-manager.ts │ │ ├── options.ts │ │ ├── primitive.ts │ │ ├── renderer.ts │ │ ├── types.ts │ │ ├── view.ts │ │ └── wrapper.ts ├── renderers │ ├── area-renderer-base.ts │ ├── area-renderer.ts │ ├── bars-renderer.ts │ ├── baseline-renderer-area.ts │ ├── baseline-renderer-line.ts │ ├── bitmap-coordinates-pane-renderer.ts │ ├── candlesticks-renderer.ts │ ├── composite-renderer.ts │ ├── crosshair-renderer.ts │ ├── draw-line.ts │ ├── draw-series-point-markers.ts │ ├── gradient-style-cache.ts │ ├── grid-renderer.ts │ ├── histogram-renderer.ts │ ├── horizontal-line-renderer.ts │ ├── iaxis-view-renderer.ts │ ├── ipane-renderer.ts │ ├── iprice-axis-view-renderer.ts │ ├── itime-axis-view-renderer.ts │ ├── line-renderer-base.ts │ ├── line-renderer.ts │ ├── marks-renderer.ts │ ├── media-coordinates-pane-renderer.ts │ ├── optimal-bar-width.ts │ ├── price-axis-renderer-options-provider.ts │ ├── price-axis-view-renderer.ts │ ├── series-last-price-animation-renderer.ts │ ├── time-axis-view-renderer.ts │ └── walk-line.ts ├── standalone.ts ├── tsconfig.composite.json ├── tsconfig.model.json ├── typings │ ├── _build-time-constants │ │ └── index.d.ts │ ├── _global-types │ │ └── index.d.ts │ └── dom-not-standarted │ │ └── index.d.ts └── views │ ├── pane │ ├── crosshair-marks-pane-view.ts │ ├── crosshair-pane-view.ts │ ├── custom-price-line-pane-view.ts │ ├── grid-pane-view.ts │ ├── iaxis-view.ts │ ├── ipane-view.ts │ ├── iupdatable-pane-view.ts │ ├── pane-price-axis-view.ts │ ├── series-horizontal-base-line-pane-view.ts │ ├── series-horizontal-line-pane-view.ts │ ├── series-last-price-animation-pane-view.ts │ └── series-price-line-pane-view.ts │ ├── price-axis │ ├── crosshair-price-axis-view.ts │ ├── custom-price-line-price-axis-view.ts │ ├── iprice-axis-view.ts │ ├── price-axis-view.ts │ └── series-price-axis-view.ts │ └── time-axis │ ├── crosshair-time-axis-view.ts │ └── itime-axis-view.ts ├── tests ├── README.md ├── e2e │ ├── .eslintrc.cjs │ ├── coverage │ │ ├── coverage-config.ts │ │ ├── coverage-test-cases.ts │ │ ├── helpers │ │ │ ├── get-coverage-test-cases.ts │ │ │ └── test-page-dummy.html │ │ ├── runner.ts │ │ └── test-cases │ │ │ ├── .eslintrc.cjs │ │ │ ├── chart │ │ │ ├── auto-size.js │ │ │ ├── color-p3-support.js │ │ │ ├── color-support.js │ │ │ ├── create-string.js │ │ │ ├── crosshair.js │ │ │ ├── handle-scale.js │ │ │ ├── handle-scroll.js │ │ │ ├── horizontal-scale.js │ │ │ ├── kinetic-scroll.js │ │ │ ├── layout.js │ │ │ ├── localization.js │ │ │ ├── options.js │ │ │ ├── plugins.js │ │ │ ├── remove.js │ │ │ ├── resize.js │ │ │ ├── screenshot.js │ │ │ └── yield-curve.js │ │ │ ├── exports.js │ │ │ ├── interactions │ │ │ ├── interactions.js │ │ │ └── tracking-mode.js │ │ │ ├── panes │ │ │ ├── add-pane.js │ │ │ ├── options.js │ │ │ ├── pane-api.js │ │ │ ├── pane-size.js │ │ │ ├── panes-sceenshot.js │ │ │ ├── remove-pane.js │ │ │ ├── set-crosshair.js │ │ │ └── swap-panes.js │ │ │ ├── plugins │ │ │ ├── auto-scale.js │ │ │ ├── custom-series.js │ │ │ ├── hit-test.js │ │ │ ├── image-watermark.js │ │ │ ├── labels.js │ │ │ ├── layers.js │ │ │ ├── pane-primitive-with-hit-test.js │ │ │ ├── panes.js │ │ │ ├── text-watermark.js │ │ │ └── up-down-markers.js │ │ │ ├── price-scale │ │ │ ├── change-auto-scale.js │ │ │ ├── change-edge-marks.js │ │ │ ├── change-scales.js │ │ │ ├── log-scale.js │ │ │ ├── options.js │ │ │ └── percentage-scale.js │ │ │ ├── series │ │ │ ├── area-inverted.js │ │ │ ├── area-series.js │ │ │ ├── bar-series.js │ │ │ ├── baseline-series.js │ │ │ ├── candlestick-series.js │ │ │ ├── histogram-series.js │ │ │ ├── line-series.js │ │ │ ├── markers.js │ │ │ ├── overlay-series.js │ │ │ ├── price-format.js │ │ │ ├── price-line.js │ │ │ ├── remove-series.js │ │ │ ├── styling.js │ │ │ ├── title.js │ │ │ └── update-data.js │ │ │ └── time-scale │ │ │ ├── bar-width.js │ │ │ ├── general.js │ │ │ ├── hidden.js │ │ │ └── seconds.js │ ├── graphics │ │ ├── README.md │ │ ├── generate-golden-content.ts │ │ ├── generate-test-cases.ts │ │ ├── graphics-test-cases.ts │ │ ├── helpers │ │ │ ├── compare-screenshots.ts │ │ │ ├── get-test-cases.ts │ │ │ ├── screenshoter.ts │ │ │ ├── test-page-dummy.html │ │ │ └── testcase-window-type.ts │ │ ├── runner.ts │ │ ├── test-cases │ │ │ ├── .eslintrc.cjs │ │ │ ├── add-markers-with-autosize-enabled.js │ │ │ ├── add-series-after-time.js │ │ │ ├── api │ │ │ │ ├── change-series-order.js │ │ │ │ ├── getting-series-price-scale.js │ │ │ │ ├── ignore-resize-if-autosize-active.js │ │ │ │ ├── price-scale-width.js │ │ │ │ ├── series-data-by-index.js │ │ │ │ ├── series-markers.js │ │ │ │ ├── subscribe-crosshair-move.js │ │ │ │ ├── time-scale-coordinate-to-logical.js │ │ │ │ ├── time-scale-coordinate-to-time.js │ │ │ │ ├── time-scale-logical-to-coordinate.js │ │ │ │ └── time-scale-time-to-coordinate.js │ │ │ ├── applying-options │ │ │ │ ├── apply-do-not-draw-price-ticks.js │ │ │ │ ├── change-bar-colors.js │ │ │ │ ├── change-candlestick-colors.js │ │ │ │ ├── crosshair-mode-hidden-unhide.js │ │ │ │ ├── crosshair-mode-hidden.js │ │ │ │ ├── empty-time-scale-options.js │ │ │ │ ├── fix-both-edges-then-scroll.js │ │ │ │ ├── fix-both-edges-time-scale-labels.js │ │ │ │ ├── fix-both-edges.js │ │ │ │ ├── fix-left-edge.js │ │ │ │ ├── increase-min-bar-spacing.js │ │ │ │ ├── make-series-hidden.js │ │ │ │ ├── make-series-visible.js │ │ │ │ ├── move-price-scale.js │ │ │ │ ├── move-series-from-right-to-left.js │ │ │ │ ├── move-series-to-overlay.js │ │ │ │ ├── re-enable-autosize.js │ │ │ │ ├── reduce-min-bar-spacing.js │ │ │ │ ├── scale-margins-for-overlay-series.js │ │ │ │ ├── scroll-to-future-then-fix-right-edge.js │ │ │ │ ├── scroll-to-past-then-fix-right-edge.js │ │ │ │ ├── series-price-format.js │ │ │ │ ├── series-title.js │ │ │ │ ├── unfix-right-edge-then-scroll-to-future.js │ │ │ │ ├── update-chart-width.js │ │ │ │ ├── watermark.js │ │ │ │ ├── zoom-in-then-fix-both-edges.js │ │ │ │ └── zoom-out-then-fix-both-edges.js │ │ │ ├── attribution-logo-dark.js │ │ │ ├── attribution-logo-light.js │ │ │ ├── color-p3-support.js │ │ │ ├── color-support.js │ │ │ ├── constrast-colors.js │ │ │ ├── correct-price-range-in-autoscale.js │ │ │ ├── crosshair-marker-position.js │ │ │ ├── crosshair-visible-startup.js │ │ │ ├── data-validation.js │ │ │ ├── degenerative-horizontal-series-with-integer-min-tick │ │ │ │ ├── indexed-to-100-scale.js │ │ │ │ ├── normal-scale.js │ │ │ │ └── percentage-scale.js │ │ │ ├── dont-draw-crosshair-lines-when-mouse-leave.js │ │ │ ├── dont-draw-lines-when-out-of-visible-range.js │ │ │ ├── fit-content-with-few-data.js │ │ │ ├── fit-content-with-lot-data.js │ │ │ ├── historical-data-updates.js │ │ │ ├── horizontal-price-scale.js │ │ │ ├── incorrect-autoscale-when-prices-are-small.js │ │ │ ├── initial-options │ │ │ │ ├── base-line-style.js │ │ │ │ ├── chart-width-and-height.js │ │ │ │ ├── crosshair-label-color-black.js │ │ │ │ ├── crosshair-label-color-greys.js │ │ │ │ ├── crosshair-label-color-named.js │ │ │ │ ├── crosshair-label-color-white.js │ │ │ │ ├── crosshair-label-color-with-transparency.js │ │ │ │ ├── crosshair-magnet-ohlc.js │ │ │ │ ├── crosshair-mode-hidden.js │ │ │ │ ├── crosshair.js │ │ │ │ ├── custom-date-format.js │ │ │ │ ├── custom-percentage-format.js │ │ │ │ ├── custom-price-format-with-extending-axis.js │ │ │ │ ├── custom-price-format.js │ │ │ │ ├── date-format.js │ │ │ │ ├── draw-price-ticks.js │ │ │ │ ├── draw-time-ticks.js │ │ │ │ ├── fat-bars.js │ │ │ │ ├── fix-left-edge-and-update-data.js │ │ │ │ ├── fix-left-edge.js │ │ │ │ ├── gradient-background.js │ │ │ │ ├── invalid-bar-spacing.js │ │ │ │ ├── left-price-scale.js │ │ │ │ ├── log-price-scale-mode.js │ │ │ │ ├── min-visible-bars.js │ │ │ │ ├── no-autoscale.js │ │ │ │ ├── no-base-line.js │ │ │ │ ├── no-price-line.js │ │ │ │ ├── no-price-scale.js │ │ │ │ ├── non-auto-scale-price-scale.js │ │ │ │ ├── price-format.js │ │ │ │ ├── price-line-source-default.js │ │ │ │ ├── price-line-source-last-visible.js │ │ │ │ ├── price-line-style.js │ │ │ │ ├── price-scale-entire-text-only.js │ │ │ │ ├── small-candlesticks.js │ │ │ │ ├── tick-marks-formatter-2.js │ │ │ │ ├── tick-marks-formatter.js │ │ │ │ ├── time-formatter-2.js │ │ │ │ ├── time-formatter.js │ │ │ │ ├── time-scale.js │ │ │ │ ├── transparent-background.js │ │ │ │ ├── use-observer.js │ │ │ │ ├── watermark.js │ │ │ │ └── zero-precision.js │ │ │ ├── logical-range │ │ │ │ ├── bars-in-gap.js │ │ │ │ ├── bars-in-range.js │ │ │ │ └── subscribe-visible-logical-range-change.js │ │ │ ├── months-chart.js │ │ │ ├── options-chart.js │ │ │ ├── panes │ │ │ │ ├── adding-pane.js │ │ │ │ ├── change-height.js │ │ │ │ ├── different-scales-options.js │ │ │ │ ├── different-scales.js │ │ │ │ ├── empty-pane-remove.js │ │ │ │ ├── empty-pane-swap.js │ │ │ │ ├── remove-pane.js │ │ │ │ ├── set-crosshair.js │ │ │ │ └── swap-panes.js │ │ │ ├── plugins │ │ │ │ ├── autoscaling.js │ │ │ │ ├── axis-labels.js │ │ │ │ ├── basic.js │ │ │ │ ├── custom-series.js │ │ │ │ ├── detach.js │ │ │ │ ├── image-watermark.js │ │ │ │ ├── layer-bottom.js │ │ │ │ ├── layer-normal.js │ │ │ │ ├── layer-top.js │ │ │ │ ├── layers.js │ │ │ │ ├── pane-primitives.js │ │ │ │ ├── panes.js │ │ │ │ └── up-down-markers.js │ │ │ ├── price-line-label-colors.js │ │ │ ├── price-scale │ │ │ │ ├── edge-marks-disabled-on-auto-scale-off.js │ │ │ │ ├── edge-marks-enabled-on-auto-scale-on.js │ │ │ │ ├── edge-marks-logarithmic.js │ │ │ │ ├── edge-marks.js │ │ │ │ ├── improve-alignment-2.js │ │ │ │ ├── improve-alignment.js │ │ │ │ ├── logarithmic-scale-on-extra-small-values.js │ │ │ │ ├── logarithmic-scale-on-small-values.js │ │ │ │ ├── minimum-scale-dimensions.js │ │ │ │ ├── no-empty-mark-on-price-scale.js │ │ │ │ ├── percentage-first-value-invisible-series.js │ │ │ │ ├── price-scale-width-without-crosshair.js │ │ │ │ ├── series-label-is-fully-visible-at-edge-of-scale.js │ │ │ │ ├── set-visible-range.js │ │ │ │ └── toggle-auto-scale.js │ │ │ ├── remove-series-extended-time-scale.js │ │ │ ├── rtl-page.js │ │ │ ├── series-markers │ │ │ │ ├── marker-in-gap-from-left.js │ │ │ │ ├── marker-in-gap.js │ │ │ │ ├── series-arrow-markers.js │ │ │ │ ├── series-circle-markers.js │ │ │ │ ├── series-markers-aligned.js │ │ │ │ ├── series-markers-all-above.js │ │ │ │ ├── series-markers-all-below.js │ │ │ │ ├── series-markers-all-inbar.js │ │ │ │ ├── series-markers-max-bar-spacing.js │ │ │ │ ├── series-markers-min-bar-spacing.js │ │ │ │ ├── series-markers-object-business-day.js │ │ │ │ ├── series-markers-out-of-visible-range.js │ │ │ │ ├── series-markers-price-positioned.js │ │ │ │ ├── series-markers-re-aligned.js │ │ │ │ ├── series-markers-update.js │ │ │ │ ├── series-markers-with-text.js │ │ │ │ ├── series-markers-zorder.js │ │ │ │ ├── series-square-markers.js │ │ │ │ └── set-markers-before-series-data.js │ │ │ ├── series │ │ │ │ ├── 2-baseline-series.js │ │ │ │ ├── 2-points-line-series.js │ │ │ │ ├── add-series-after-volume.js │ │ │ │ ├── alternate-histogram-items-with-gaps.js │ │ │ │ ├── area-inverted-after-delay.js │ │ │ │ ├── area-inverted.js │ │ │ │ ├── area-out-of-viewport.js │ │ │ │ ├── area-relative-gradient-inverted.js │ │ │ │ ├── area-relative-gradient.js │ │ │ │ ├── area-with-custom-colors.js │ │ │ │ ├── area-with-point-markers.js │ │ │ │ ├── area-with-whitespaces.js │ │ │ │ ├── area.js │ │ │ │ ├── bar-semitransparent.js │ │ │ │ ├── bar-with-custom-colors.js │ │ │ │ ├── bar.js │ │ │ │ ├── bars-with-whitespaces.js │ │ │ │ ├── baseline-relative-gradient.js │ │ │ │ ├── baseline-with-custom-colors.js │ │ │ │ ├── baseline-with-point-markers.js │ │ │ │ ├── baseline.js │ │ │ │ ├── candlesticks-semitransparent.js │ │ │ │ ├── candlesticks-with-custom-colors.js │ │ │ │ ├── candlesticks-with-huge-range.js │ │ │ │ ├── candlesticks.js │ │ │ │ ├── candlestics-with-whitespaces.js │ │ │ │ ├── change-colors-via-set-data-in-histogram.js │ │ │ │ ├── crosshair-marker-border-width.js │ │ │ │ ├── crosshair-marker-colors-change-to-default.js │ │ │ │ ├── crosshair-marker-colors-change.js │ │ │ │ ├── crosshair-marker-colors.js │ │ │ │ ├── curved-line-2-points.js │ │ │ │ ├── curved-line-3-points.js │ │ │ │ ├── curved-line-area.js │ │ │ │ ├── curved-line-baseline.js │ │ │ │ ├── curved-line-colored-items.js │ │ │ │ ├── hidden-series-and-autoscale.js │ │ │ │ ├── histogram-add-new-color-on-update.js │ │ │ │ ├── histogram-change-default-color.js │ │ │ │ ├── histogram-out-of-range.js │ │ │ │ ├── histogram-update-without-set-data.js │ │ │ │ ├── histogram-with-whitespaces.js │ │ │ │ ├── histogram.js │ │ │ │ ├── line-dotted.js │ │ │ │ ├── line-overlap.js │ │ │ │ ├── line-series-with-point-markers-and-hidden-line.js │ │ │ │ ├── line-with-custom-color-change-color-later.js │ │ │ │ ├── line-with-custom-color.js │ │ │ │ ├── line-with-point-markers.js │ │ │ │ ├── line-with-steps-with-custom-color.js │ │ │ │ ├── line-with-whitespaces.js │ │ │ │ ├── line.js │ │ │ │ ├── no-last-price-animation-on-adding-whitespace.js │ │ │ │ ├── no-last-price-animation-on-first-set-data-after-reseting-data.js │ │ │ │ ├── no-last-price-animation-on-first-set-data.js │ │ │ │ ├── no-last-price-animation-on-loading-history-data.js │ │ │ │ ├── no-visible-points-in-the-middle.js │ │ │ │ ├── overlay-series-title-only-overlay-price-scale.js │ │ │ │ ├── overlay-series-title-only-overlay-stacked.js │ │ │ │ ├── overlay-series-title.js │ │ │ │ ├── override-autoscale-fixed-range.js │ │ │ │ ├── point-markers-not-in-range.js │ │ │ │ ├── price-line-apply-options.js │ │ │ │ ├── price-line-line-visibility.js │ │ │ │ ├── price-line-overlapping-series-title.js │ │ │ │ ├── price-line-with-percentage-scale-mode.js │ │ │ │ ├── price-lines-align-labels.js │ │ │ │ ├── price-lines-do-not-align-labels.js │ │ │ │ ├── price-lines-getter.js │ │ │ │ ├── price-lines-on-two-axis.js │ │ │ │ ├── price-lines-remove.js │ │ │ │ ├── price-lines.js │ │ │ │ ├── series-price-formatter.js │ │ │ │ ├── series-type.js │ │ │ │ ├── series-visibility.js │ │ │ │ ├── set-empty-data-to-histogram.js │ │ │ │ ├── setting-same-data-after-time.js │ │ │ │ ├── several-non-regular-series.js │ │ │ │ ├── single-point-area.js │ │ │ │ ├── single-point-line.js │ │ │ │ ├── single-visible-point-line-first-bar.js │ │ │ │ ├── single-visible-point-line-last-bar.js │ │ │ │ ├── step-line-area.js │ │ │ │ ├── step-line-baseline.js │ │ │ │ ├── step-line.js │ │ │ │ ├── two-series-one-not-autoscaled.js │ │ │ │ ├── update-baseline-base-value.js │ │ │ │ ├── update-removed-series-data.js │ │ │ │ └── whitespace-updates.js │ │ │ ├── set-crosshair-position.js │ │ │ ├── set-price-line-label.js │ │ │ ├── take-screenshot.js │ │ │ ├── time-scale │ │ │ │ ├── add-data-to-left-two-series.js │ │ │ │ ├── add-data-to-left.js │ │ │ │ ├── add-data-to-right-two-series.js │ │ │ │ ├── add-data-to-right.js │ │ │ │ ├── allow-shift-range-whitespace-replacement.js │ │ │ │ ├── both-edges-fixed.js │ │ │ │ ├── change-weight-of-day-tickmark.js │ │ │ │ ├── disable-bold-labels.js │ │ │ │ ├── do-not-render-overlapping-marks-font-size-24.js │ │ │ │ ├── do-not-render-overlapping-marks.js │ │ │ │ ├── do-not-shift-on-new-data-from-right.js │ │ │ │ ├── do-not-shift-range-when-replacing-whitespace.js │ │ │ │ ├── fit-content-pricescale-changes.js │ │ │ │ ├── hidden-time-scale-size.js │ │ │ │ ├── ignore-whitespace-indices.js │ │ │ │ ├── lock-visible-range-on-resize-and-fixing-edges.js │ │ │ │ ├── max-bar-spacing.js │ │ │ │ ├── min-bar-spacing.js │ │ │ │ ├── realign-partially-hidden-time-scale-mark-on-left.js │ │ │ │ ├── realign-partially-hidden-time-scale-mark-on-right.js │ │ │ │ ├── realign-partially-hidden-time-scale-marks.js │ │ │ │ ├── reset-time-scale-after-set-visible-range.js │ │ │ │ ├── scroll-to-position-after-set-visible-range.js │ │ │ │ ├── scroll-to-position-with-no-data.js │ │ │ │ ├── set-and-clear-series.js │ │ │ │ ├── set-right-offset-after-set-visible-range.js │ │ │ │ ├── set-visible-logical-range-with-no-data.js │ │ │ │ ├── set-visible-range-after-time.js │ │ │ │ ├── set-visible-range-with-small-range.js │ │ │ │ ├── set-visible-range-with-two-series.js │ │ │ │ ├── set-visible-range.js │ │ │ │ ├── time-scale-size-on-changing-chart-size.js │ │ │ │ ├── time-scale-size-with-hidden-price-scale.js │ │ │ │ ├── updates-after-replacing-whitespace-should-shift-timescale.js │ │ │ │ ├── updates-shift-the-timescale.js │ │ │ │ └── updates-to-existing-data-points-should-not-shift-timescale.js │ │ │ ├── transparent-color.js │ │ │ ├── two-scales │ │ │ │ ├── basic.js │ │ │ │ ├── empty-price-scale-id.js │ │ │ │ ├── left-percent.js │ │ │ │ ├── price-scale-text-colors.js │ │ │ │ ├── two-groups-of-overlays.js │ │ │ │ └── two-overlays-share-scale.js │ │ │ └── yield-curve-chart.js │ │ └── utils.ts │ ├── helpers │ │ ├── get-test-cases.ts │ │ ├── mouse-drag-actions.ts │ │ ├── mouse-scroll-actions.ts │ │ ├── page-timeout.ts │ │ ├── perform-interactions.ts │ │ ├── retry-tests.ts │ │ ├── touch-actions.ts │ │ └── zoom-action.ts │ ├── interactions │ │ ├── helpers │ │ │ ├── get-interaction-test-cases.ts │ │ │ └── test-page-dummy.html │ │ ├── interactions-test-cases.ts │ │ ├── runner.ts │ │ └── test-cases │ │ │ ├── .eslintrc.cjs │ │ │ ├── markers │ │ │ ├── hit-test-after-timescale-change.js │ │ │ ├── hit-test-priceline-overlap.js │ │ │ ├── price-positioned-text-hit-test.js │ │ │ └── text-hit-test.js │ │ │ ├── mouse-wheel │ │ │ ├── default-scroll-handler.js │ │ │ ├── disabled-scroll-handler.js │ │ │ ├── enabled-scroll-handler.js │ │ │ └── reenabled-scroll-handler.js │ │ │ ├── mouse │ │ │ └── double-click-pane.js │ │ │ ├── plugins │ │ │ ├── custom-series-mouse-params.js │ │ │ ├── hit-test-bottom.js │ │ │ ├── hit-test-top.js │ │ │ └── pane-primitive-hit-test.js │ │ │ ├── price-line │ │ │ ├── hovered-object-hit-test.js │ │ │ └── hovered-object-miss-test.js │ │ │ └── touch │ │ │ ├── dont-horz-page-scroll-when-handle-scroll-is-enabled.js │ │ │ ├── dont-vertical-page-scroll-when-handle-scroll-is-enabled.js │ │ │ ├── horz-page-scroll-when-handle-scroll-is-disabled.js │ │ │ └── vertical-page-scroll-when-handle-scroll-is-disabled.js │ ├── memleaks │ │ ├── helpers │ │ │ ├── get-all-class-names.ts │ │ │ ├── get-test-cases.ts │ │ │ └── test-page.html │ │ ├── memleaks-test-cases.ts │ │ ├── runner.ts │ │ └── test-cases │ │ │ ├── .eslintrc.cjs │ │ │ ├── control-test-case.js │ │ │ ├── expect-fail.js │ │ │ ├── series.js │ │ │ └── simple.js │ ├── runner.ts │ ├── serve-local-files.ts │ └── tsconfig.composite.json ├── setup.mjs ├── tsconfig.composite.json ├── type-checks │ ├── non-time-based-custom-series.ts │ ├── plugins.ts │ ├── series-markers.ts │ ├── series.ts │ ├── tsconfig.composite.json │ └── watermarks.ts └── unittests │ ├── color.spec.ts │ ├── data-layer.spec.ts │ ├── default-tick-mark-formatter.spec.ts │ ├── delegate.spec.ts │ ├── format-date.spec.ts │ ├── formatters.spec.ts │ ├── get-series-data-creator.spec.ts │ ├── get-series-plot-row-creator.spec.ts │ ├── helpers.spec.ts │ ├── make-font.spec.ts │ ├── plot-list.spec.ts │ ├── series-options.spec.ts │ ├── setup.units.mjs │ ├── text-width-cache.spec.ts │ ├── time-scale.spec.ts │ ├── timed-data.spec.ts │ └── tsconfig.composite.json ├── tsconfig.composite.base.json ├── tsconfig.composite.json ├── tsconfig.json ├── tsconfig.options.json ├── tsconfig.prod.json ├── tsdoc.json └── website ├── .browserslistrc ├── .eslintrc.js ├── .npmrc ├── .previous-typings-cache └── tsconfig.json ├── README.md ├── docs ├── android.md ├── chart-types.mdx ├── intro.mdx ├── ios.md ├── migrations │ ├── _category_.yml │ ├── from-v2-to-v3.md │ ├── from-v3-to-v4.md │ └── from-v4-to-v5.md ├── panes.md ├── plugins │ ├── .eslintrc.js │ ├── canvas-rendering-target.md │ ├── custom_series.md │ ├── explainer-layers-demo.js │ ├── explainer-sections-demo.js │ ├── intro.md │ ├── pane-primitives.md │ ├── pixel-perfect-rendering │ │ ├── _category_.yml │ │ ├── index.md │ │ └── widths │ │ │ ├── _category_.yml │ │ │ ├── candlestick.md │ │ │ ├── columns.md │ │ │ ├── crosshair.md │ │ │ └── full-bar-width.md │ └── series-primitives.mdx ├── price-scale.md ├── release-notes.md ├── series-types.mdx ├── time-scale.md └── time-zones.md ├── docusaurus.config.js ├── package.json ├── plugins ├── enhanced-codeblock │ ├── index.js │ └── theme │ │ └── CodeBlock │ │ ├── chart.tsx │ │ ├── hidden-lines-styles.css │ │ ├── import-lightweight-charts-version.ts │ │ ├── index.jsx │ │ ├── styles.module.css │ │ └── use-id.ts └── suppress-resize-observer-error │ └── index.js ├── scripts └── generate-versions-dts.js ├── sidebars-tutorials.js ├── sidebars.js ├── src ├── components │ ├── CardLink │ │ ├── index.tsx │ │ └── styles.module.css │ ├── CardLinkList │ │ └── index.tsx │ ├── InstantDetails │ │ ├── index.tsx │ │ └── styles.module.css │ ├── MinimumVersionWrapper │ │ └── index.tsx │ ├── VersionWarningAdmonition │ │ └── index.tsx │ ├── landing-page │ │ ├── Banner │ │ │ ├── banner.module.css │ │ │ └── index.tsx │ │ ├── CTAButton │ │ │ ├── ctabutton.module.css │ │ │ └── index.tsx │ │ ├── Cards │ │ │ ├── cards.module.css │ │ │ └── index.tsx │ │ ├── Codeblock │ │ │ ├── codeblock.module.css │ │ │ └── index.tsx │ │ ├── Hero │ │ │ ├── hero.module.css │ │ │ └── index.tsx │ │ └── HeroChart │ │ │ ├── hero-chart-data.json │ │ │ ├── index.module.css │ │ │ └── index.tsx │ └── tutorials │ │ ├── advanced-react-example.jsx │ │ ├── simple-react-example.jsx │ │ └── themed-chart-colors-wrapper.tsx ├── css │ └── custom.css ├── img │ ├── a11y.svg │ ├── cog.svg │ ├── github-logo.svg │ ├── react.svg │ ├── shapes.svg │ ├── tradingview-logo-small.svg │ ├── tradingview-logo.svg │ ├── vuejs.svg │ └── webcomponents.svg ├── pages │ ├── chart.module.css │ ├── index.module.css │ └── index.tsx └── theme │ ├── Footer │ └── index.js │ ├── Logo │ ├── index.module.css │ └── index.tsx │ ├── analytics-tracker.js │ └── analytics-wrapper.js ├── static ├── .nojekyll ├── favicon.ico └── img │ ├── area-series.png │ ├── bar-series.png │ ├── baseline-series.png │ ├── candlestick-series.png │ ├── extra-margin.png │ ├── favicon │ ├── apple-touch-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ └── safari-pinned-tab.svg │ ├── first-chart.png │ ├── histogram-series.png │ ├── line-series.png │ ├── logical-range.png │ ├── margin.png │ ├── navbar-logo-desktop-laptop-tablet-dark.svg │ ├── navbar-logo-desktop-laptop-tablet-light.svg │ ├── navbar-logo-mobile-dark.svg │ ├── navbar-logo-mobile-light.svg │ ├── price-scales.png │ └── time-scale.png ├── svgr.config.js ├── theme-colors.js ├── tsconfig.json ├── tutorials ├── _usage-guide-partial.mdx ├── a11y │ ├── _category_.yml │ ├── assets │ │ └── a11y-chart.html │ ├── conclusion.mdx │ ├── intro.mdx │ ├── keyboard.mdx │ ├── readability.mdx │ └── screenreader.mdx ├── customization │ ├── .eslintrc.js │ ├── _apply-options-tabs-partial.mdx │ ├── _category_.yml │ ├── _iterative-guide-warning-partial.mdx │ ├── assets │ │ ├── start.html │ │ ├── step1.html │ │ ├── step10.html │ │ ├── step2.html │ │ ├── step3.html │ │ ├── step4.html │ │ ├── step5.html │ │ ├── step6.html │ │ ├── step7.html │ │ ├── step8.html │ │ └── step9.html │ ├── chart-colors.mdx │ ├── conclusion.mdx │ ├── creating-a-chart.mdx │ ├── crosshair.mdx │ ├── data-points.mdx │ ├── finishing-touches.mdx │ ├── intro.mdx │ ├── price-format.mdx │ ├── price-scale.mdx │ ├── second-series.mdx │ ├── series.mdx │ └── time-scale.mdx ├── demos │ ├── .eslintrc.js │ ├── compare-multiple-series.js │ ├── compare-multiple-series.mdx │ ├── custom-font-family.js │ ├── custom-font-family.mdx │ ├── custom-locale.js │ ├── custom-locale.mdx │ ├── infinite-history.js │ ├── infinite-history.mdx │ ├── moving-average.js │ ├── moving-average.mdx │ ├── range-switcher.js │ ├── range-switcher.mdx │ ├── realtime-updates.js │ ├── realtime-updates.mdx │ ├── whitespace.js │ ├── whitespace.mdx │ ├── yield-curve-with-update-markers.js │ └── yield-curve-with-update-markers.mdx ├── how_to │ ├── .eslintrc.js │ ├── _usage-guide-partial.mdx │ ├── horizontal-price-scale.js │ ├── horizontal-price-scale.mdx │ ├── inverted-price-scale.js │ ├── inverted-price-scale.mdx │ ├── legend-3line.js │ ├── legend.js │ ├── legends.mdx │ ├── no-time-scale.js │ ├── panes.js │ ├── panes.mdx │ ├── price-and-volume.js │ ├── price-and-volume.mdx │ ├── price-line.js │ ├── price-line.mdx │ ├── series-markers.js │ ├── series-markers.mdx │ ├── set-crosshair-position-syncing.js │ ├── set-crosshair-position-tracking.js │ ├── set-crosshair-position.mdx │ ├── tooltip-floating.js │ ├── tooltip-magnifier.js │ ├── tooltip-tracking.js │ ├── tooltips.mdx │ ├── two-price-scales.js │ ├── two-price-scales.mdx │ ├── watermark-advanced.js │ ├── watermark-simple.js │ └── watermark.mdx ├── index.mdx ├── react │ ├── 01-simple.mdx │ ├── 02-advanced.mdx │ └── _category_.yml ├── vuejs │ ├── 01-wrapper.mdx │ ├── _category_.yml │ └── assets │ │ ├── .eslintrc.js │ │ ├── app.vue │ │ ├── composition-api.vue │ │ ├── options-api.vue │ │ └── web-component.js └── webcomponents │ ├── 01-custom-element.mdx │ ├── _category_.yml │ └── assets │ ├── .eslintrc.js │ ├── lw-chart.js │ └── wc-example.js ├── versioned_docs ├── version-3.8 │ ├── android.md │ ├── intro.md │ ├── ios.md │ ├── migrations │ │ ├── _category_.yml │ │ └── from-v2-to-v3.md │ ├── price-scale.md │ ├── release-notes.md │ ├── series-types.md │ ├── time-scale.md │ └── time-zones.md ├── version-4.0 │ ├── android.md │ ├── intro.md │ ├── ios.md │ ├── migrations │ │ ├── _category_.yml │ │ ├── from-v2-to-v3.md │ │ └── from-v3-to-v4.md │ ├── price-scale.md │ ├── release-notes.md │ ├── series-types.md │ ├── time-scale.md │ └── time-zones.md ├── version-4.1 │ ├── android.md │ ├── intro.md │ ├── ios.md │ ├── migrations │ │ ├── _category_.yml │ │ ├── from-v2-to-v3.md │ │ └── from-v3-to-v4.md │ ├── plugins │ │ ├── .eslintrc.js │ │ ├── canvas-rendering-target.md │ │ ├── custom_series.md │ │ ├── explainer-layers-demo.js │ │ ├── explainer-sections-demo.js │ │ ├── intro.md │ │ ├── pixel-perfect-rendering │ │ │ ├── _category_.yml │ │ │ ├── index.md │ │ │ └── widths │ │ │ │ ├── _category_.yml │ │ │ │ ├── candlestick.md │ │ │ │ ├── columns.md │ │ │ │ ├── crosshair.md │ │ │ │ └── full-bar-width.md │ │ └── series-primitives.mdx │ ├── price-scale.md │ ├── release-notes.md │ ├── series-types.md │ ├── time-scale.md │ └── time-zones.md ├── version-4.2 │ ├── android.md │ ├── intro.md │ ├── ios.md │ ├── migrations │ │ ├── _category_.yml │ │ ├── from-v2-to-v3.md │ │ └── from-v3-to-v4.md │ ├── plugins │ │ ├── .eslintrc.js │ │ ├── canvas-rendering-target.md │ │ ├── custom_series.md │ │ ├── explainer-layers-demo.js │ │ ├── explainer-sections-demo.js │ │ ├── intro.md │ │ ├── pixel-perfect-rendering │ │ │ ├── _category_.yml │ │ │ ├── index.md │ │ │ └── widths │ │ │ │ ├── _category_.yml │ │ │ │ ├── candlestick.md │ │ │ │ ├── columns.md │ │ │ │ ├── crosshair.md │ │ │ │ └── full-bar-width.md │ │ └── series-primitives.mdx │ ├── price-scale.md │ ├── release-notes.md │ ├── series-types.md │ ├── time-scale.md │ └── time-zones.md └── version-5.0 │ ├── android.md │ ├── chart-types.mdx │ ├── intro.mdx │ ├── ios.md │ ├── migrations │ ├── _category_.yml │ ├── from-v2-to-v3.md │ ├── from-v3-to-v4.md │ └── from-v4-to-v5.md │ ├── panes.md │ ├── plugins │ ├── .eslintrc.js │ ├── canvas-rendering-target.md │ ├── custom_series.md │ ├── explainer-layers-demo.js │ ├── explainer-sections-demo.js │ ├── intro.md │ ├── pane-primitives.md │ ├── pixel-perfect-rendering │ │ ├── _category_.yml │ │ ├── index.md │ │ └── widths │ │ │ ├── _category_.yml │ │ │ ├── candlestick.md │ │ │ ├── columns.md │ │ │ ├── crosshair.md │ │ │ └── full-bar-width.md │ └── series-primitives.mdx │ ├── price-scale.md │ ├── release-notes.md │ ├── series-types.mdx │ ├── time-scale.md │ └── time-zones.md ├── versioned_sidebars ├── version-3.8-sidebars.json ├── version-4.0-sidebars.json ├── version-4.1-sidebars.json ├── version-4.2-sidebars.json └── version-5.0-sidebars.json └── versions.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = tab 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [*.yml] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | 17 | [package.json] 18 | indent_style = space 19 | indent_size = 2 20 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | !.eslintrc.js 2 | !.size-limit.js 3 | 4 | /dist/** 5 | !/dist/typings.d.ts 6 | /lib/** 7 | 8 | /src/typings/_resize-observer/index.d.ts 9 | 10 | **/node_modules 11 | 12 | /website/docs/api/** 13 | /website/versioned_docs/**/api/** 14 | /website/build/** 15 | /website/src/theme/**/*.js 16 | 17 | /plugin-examples 18 | /packages/create-lwc-plugin 19 | 20 | /debug 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Lightweight Charts™ Version:** 11 | 12 | **Steps/code to reproduce:** 13 | 14 | 18 | 19 | **Actual behavior:** 20 | 21 | 22 | 23 | **Expected behavior:** 24 | 25 | 26 | 27 | **Screenshots:** 28 | 29 | 30 | 31 | **CodeSandbox/JSFiddle/etc link:** 32 | 33 | 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | blank_issues_enabled: false 3 | contact_links: 4 | - 5 | about: "Please ask and answer usage questions on Stack Overflow." 6 | name: Question 7 | url: "https://stackoverflow.com/questions/tagged/lightweight-charts" 8 | - 9 | about: "Alternatively, you can use the TradingView Community Discord." 10 | name: Chat 11 | url: "https://discord.gg/UC7cGkvn4U" 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | 12 | 13 | 14 | **Describe the solution you'd like** 15 | 16 | 17 | 18 | **Additional context** 19 | 20 | 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/other.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Other 3 | about: Any other type of an issue 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Type of PR:** 2 | 3 | **PR checklist:** 4 | 5 | - [ ] Addresses an existing issue: fixes # 6 | - [ ] Includes tests 7 | - [ ] Documentation update 8 | 9 | **Overview of change:** 10 | 11 | 12 | 13 | **Is there anything you'd like reviewers to focus on?** 14 | 15 | 16 | -------------------------------------------------------------------------------- /.github/workflows/size-limit.yml: -------------------------------------------------------------------------------- 1 | name: "size" 2 | on: 3 | pull_request: 4 | branches: 5 | - master 6 | permissions: 7 | pull-requests: write 8 | jobs: 9 | size: 10 | runs-on: ubuntu-latest 11 | env: 12 | CI_JOB_NUMBER: 1 13 | steps: 14 | - uses: actions/checkout@v1 15 | - uses: andresz1/size-limit-action@v1 16 | with: 17 | github_token: ${{ secrets.GITHUB_TOKEN }} 18 | build_script: build:prod 19 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Eugene Korobko 2 | Eugene Korobko 3 | 4 | 5 | <39053314+kpaape@users.noreply.github.com> 6 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | "line-length": false 4 | } 5 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=true 2 | legacy-peer-deps=true 3 | engine-strict=false # memlab doesn't explictly mention node v22 yet. 4 | prefer-dedupe=true 5 | prefer-offline=true 6 | save-prefix=~ 7 | -------------------------------------------------------------------------------- /.puppeteerrc.cjs: -------------------------------------------------------------------------------- 1 | const { join } = require('path'); 2 | 3 | /** 4 | * @type {import("puppeteer").Configuration} 5 | */ 6 | module.exports = { 7 | /** 8 | * We need to set the cacheDirectory so that the CircleCI pipeline 9 | * can reliably find the installed chrome binary 10 | */ 11 | cacheDirectory: join(__dirname, 'node_modules', '.cache', 'puppeteer'), 12 | experiments: { 13 | /** 14 | * This can also be configured / overridden with the 15 | * PUPPETEER_EXPERIMENTAL_CHROMIUM_MAC_ARM 16 | * env variable 17 | */ 18 | macArmChromiumEnabled: true, 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "dist/**": true, 4 | "lib/**": true, 5 | "tests/e2e/graphics/.gendata/**": true, 6 | "tests/e2e/coverage/.gendata/**": true, 7 | "website/.docusaurus/**": true, 8 | "website/build/**": true, 9 | "website/docs/api/**": true, 10 | }, 11 | "typescript.tsdk": "node_modules\\typescript\\lib" 12 | } 13 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | TradingView Lightweight Charts™ 2 | Copyright (с) 2025 TradingView, Inc. https://www.tradingview.com/ 3 | -------------------------------------------------------------------------------- /debug/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /debug/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock = false 2 | fund = false 3 | audit = false 4 | loglevel = error 5 | -------------------------------------------------------------------------------- /debug/default/.global.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "lightweight-charts": "file://../.." 4 | }, 5 | "devDependencies": { 6 | "typescript": "^5.7.3", 7 | "vite": "^6.1.0" 8 | }, 9 | "private": true, 10 | "workspaces": [ 11 | "./*" 12 | ], 13 | "scripts": { 14 | "create": "sh -c 'mkdir -p $1.d && cd $1.d && cp ../../default/.local.json package.json && cp ../../default/* . && npm install' -", 15 | "remove": "sh -c 'rm -fr $PWD/${1##*/}.d' -", 16 | "serve": "sh -c 'vite $1.d' -" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /debug/default/.local.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /debug/default/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /debug/default/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ESNext", "DOM"], 7 | "moduleResolution": "Node", 8 | "strict": true, 9 | "resolveJsonModule": true, 10 | "isolatedModules": true, 11 | "esModuleInterop": true, 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true, 14 | "noImplicitReturns": true, 15 | "skipLibCheck": true, 16 | "declaration": true, 17 | "declarationDir": "typings", 18 | "emitDeclarationOnly": true 19 | }, 20 | "include": [ 21 | "*.ts", 22 | "*.tsx", 23 | "*.js", 24 | "*.jsx", 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /debug/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "playground_root", 3 | "os": [ 4 | "darwin", 5 | "linux" 6 | ], 7 | "private": true, 8 | "scripts": { 9 | "init": "mkdir playground && cp default/.global.json playground/package.json && cp .npmrc playground && npm install --prefix playground;", 10 | "create": "npm run --prefix playground create", 11 | "remove": "npm run --prefix playground remove", 12 | "list": "npm run --prefix playground list", 13 | "serve": "npm run --prefix playground serve", 14 | "serve:prod": "NODE_ENV=production npm run --prefix playground serve" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /dts-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilationOptions": { 3 | "preferredConfigPath": "./tsconfig.prod.json" 4 | }, 5 | "entries": [ 6 | { 7 | "filePath": "./lib/prod/src/index.d.ts", 8 | "outFile": "./dist/typings.d.ts", 9 | "output": { 10 | "sortNodes": true, 11 | "respectPreserveConstEnum": true 12 | }, 13 | "failOnClass": true 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/create-lwc-plugin/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /packages/create-lwc-plugin/build.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from 'unbuild'; 2 | 3 | export default defineBuildConfig({ 4 | entries: ['src/index'], 5 | clean: true, 6 | rollup: { 7 | inlineDependencies: true, 8 | esbuild: { 9 | target: 'node18', 10 | minify: true, 11 | }, 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /packages/create-lwc-plugin/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import './dist/index.mjs'; 4 | -------------------------------------------------------------------------------- /packages/create-lwc-plugin/src/helpers/package.ts: -------------------------------------------------------------------------------- 1 | function pkgFromUserAgent(userAgent: string | undefined) { 2 | if (!userAgent) return undefined; 3 | const pkgSpec = userAgent.split(' ')[0]; 4 | const pkgSpecArr = pkgSpec.split('/'); 5 | return { 6 | name: pkgSpecArr[0], 7 | version: pkgSpecArr[1], 8 | }; 9 | } 10 | 11 | export function getPkgManagerName() { 12 | const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent); 13 | return pkgInfo ? pkgInfo.name : 'npm'; 14 | } 15 | -------------------------------------------------------------------------------- /packages/create-lwc-plugin/src/helpers/validation.ts: -------------------------------------------------------------------------------- 1 | export function isValidPackageName(projectName: string) { 2 | return /^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test( 3 | projectName 4 | ); 5 | } 6 | 7 | export function toValidPackageName(projectName: string) { 8 | return projectName 9 | .trim() 10 | .toLowerCase() 11 | .replace(/\s+/g, '-') 12 | .replace(/^[._]/, '') 13 | .replace(/[^a-z\d\-~]+/g, '-'); 14 | } 15 | -------------------------------------------------------------------------------- /packages/create-lwc-plugin/template-common/_gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | typings 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /packages/create-lwc-plugin/template-common/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packages/create-lwc-plugin/template-common/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "to-be-replaced", 3 | "type": "module", 4 | "scripts": { 5 | "dev": "vite --config src/vite.config.js", 6 | "compile": "tsc && node compile.mjs" 7 | }, 8 | "devDependencies": { 9 | "typescript": "^5.0.4", 10 | "vite": "^4.3.1" 11 | }, 12 | "dependencies": { 13 | "dts-bundle-generator": "^8.0.1", 14 | "fancy-canvas": "^2.1.0", 15 | "lightweight-charts": "~5.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/create-lwc-plugin/template-common/src/helpers/dimensions/common.ts: -------------------------------------------------------------------------------- 1 | export interface BitmapPositionLength { 2 | /** coordinate for use with a bitmap rendering scope */ 3 | position: number; 4 | /** length for use with a bitmap rendering scope */ 5 | length: number; 6 | } 7 | -------------------------------------------------------------------------------- /packages/create-lwc-plugin/template-common/src/helpers/dimensions/crosshair-width.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Default grid / crosshair line width in Bitmap sizing 3 | * @param horizontalPixelRatio - horizontal pixel ratio 4 | * @returns default grid / crosshair line width in Bitmap sizing 5 | */ 6 | export function gridAndCrosshairBitmapWidth( 7 | horizontalPixelRatio: number 8 | ): number { 9 | return Math.max(1, Math.floor(horizontalPixelRatio)); 10 | } 11 | 12 | /** 13 | * Default grid / crosshair line width in Media sizing 14 | * @param horizontalPixelRatio - horizontal pixel ratio 15 | * @returns default grid / crosshair line width in Media sizing 16 | */ 17 | export function gridAndCrosshairMediaWidth( 18 | horizontalPixelRatio: number 19 | ): number { 20 | return ( 21 | gridAndCrosshairBitmapWidth(horizontalPixelRatio) / horizontalPixelRatio 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /packages/create-lwc-plugin/template-common/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/create-lwc-plugin/template-common/src/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | 3 | const input = { 4 | main: './src/example/index.html', 5 | }; 6 | 7 | export default defineConfig({ 8 | build: { 9 | rollupOptions: { 10 | input, 11 | }, 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /packages/create-lwc-plugin/template-common/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ESNext", "DOM"], 7 | "moduleResolution": "Node", 8 | "strict": true, 9 | "resolveJsonModule": true, 10 | "isolatedModules": true, 11 | "esModuleInterop": true, 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true, 14 | "noImplicitReturns": true, 15 | "skipLibCheck": true, 16 | "declaration": true, 17 | "declarationDir": "typings", 18 | "emitDeclarationOnly": true 19 | }, 20 | "include": ["src"] 21 | } 22 | -------------------------------------------------------------------------------- /packages/create-lwc-plugin/template-primitive/src/data-source.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IChartApi, 3 | ISeriesApi, 4 | SeriesOptionsMap, 5 | Time, 6 | } from 'lightweight-charts'; 7 | import { _CLASSNAME_Options } from './options'; 8 | 9 | export interface Point { 10 | time: Time; 11 | price: number; 12 | } 13 | 14 | export interface _CLASSNAME_DataSource { 15 | chart: IChartApi; 16 | series: ISeriesApi; 17 | options: _CLASSNAME_Options; 18 | p1: Point; 19 | p2: Point; 20 | } 21 | -------------------------------------------------------------------------------- /packages/create-lwc-plugin/template-primitive/src/example/example.ts: -------------------------------------------------------------------------------- 1 | import { LineSeries, createChart } from 'lightweight-charts'; 2 | import { generateLineData } from '../sample-data'; 3 | import { _CLASSNAME_ } from '../template-entry'; 4 | 5 | const chart = ((window as unknown as any).chart = createChart('chart', { 6 | autoSize: true, 7 | })); 8 | 9 | const lineSeries = chart.addSeries(LineSeries, { 10 | color: '#000000', 11 | }); 12 | const data = generateLineData(); 13 | lineSeries.setData(data); 14 | 15 | const time1 = data[data.length - 50].time; 16 | const time2 = data[data.length - 10].time; 17 | 18 | const primitive = new _CLASSNAME_( 19 | { price: 100, time: time1 }, 20 | { price: 500, time: time2 } 21 | ); 22 | 23 | lineSeries.attachPrimitive(primitive); 24 | -------------------------------------------------------------------------------- /packages/create-lwc-plugin/template-primitive/src/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Template Drawing Primitive Plugin Example 7 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /packages/create-lwc-plugin/template-series/src/data.ts: -------------------------------------------------------------------------------- 1 | import { CustomData } from 'lightweight-charts'; 2 | 3 | /** 4 | * _CLASSNAME_ Data 5 | */ 6 | export interface _CLASSNAME_Data extends CustomData { 7 | //* Define the structure of the data required for the series. 8 | //* You could also 'extend' an existing Lightweight Charts Data type like LineData or CandlestickData 9 | high: number; 10 | low: number; 11 | } 12 | -------------------------------------------------------------------------------- /packages/create-lwc-plugin/template-series/src/example/example.ts: -------------------------------------------------------------------------------- 1 | import { createChart } from 'lightweight-charts'; 2 | import { _CLASSNAME_ } from '../template-entry'; 3 | import { _CLASSNAME_Data } from '../data'; 4 | import { generateSampleData } from '../sample-data'; 5 | 6 | const chart = ((window as unknown as any).chart = createChart('chart', { 7 | autoSize: true, 8 | })); 9 | 10 | const series = chart.addCustomSeries(new _CLASSNAME_(), { 11 | /* Options */ 12 | }); 13 | 14 | const data: _CLASSNAME_Data[] = generateSampleData(500, 50); 15 | series.setData(data); 16 | -------------------------------------------------------------------------------- /packages/create-lwc-plugin/template-series/src/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | _PLUGINNAME_ Plugin Example 7 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /packages/create-lwc-plugin/template-series/src/options.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CustomSeriesOptions, 3 | customSeriesDefaultOptions, 4 | } from 'lightweight-charts'; 5 | 6 | export interface _CLASSNAME_Options extends CustomSeriesOptions { 7 | //* Define the options for the series. 8 | highLineColor: string; 9 | lowLineColor: string; 10 | areaColor: string; 11 | highLineWidth: number; 12 | lowLineWidth: number; 13 | } 14 | 15 | export const defaultOptions: _CLASSNAME_Options = { 16 | //* Define the default values for all the series options. 17 | ...customSeriesDefaultOptions, 18 | highLineColor: '#049981', 19 | lowLineColor: '#F23645', 20 | areaColor: 'rgba(41, 98, 255, 0.2)', 21 | highLineWidth: 2, 22 | lowLineWidth: 2, 23 | } as const; 24 | -------------------------------------------------------------------------------- /packages/create-lwc-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["build.config.ts", "src", "__tests__"], 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "target": "ES2022", 6 | "module": "ES2020", 7 | "moduleResolution": "bundler", 8 | "strict": true, 9 | "skipLibCheck": true, 10 | "declaration": false, 11 | "sourceMap": false, 12 | "noUnusedLocals": true, 13 | "esModuleInterop": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /plugin-examples/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | compiled 4 | typings 5 | website 6 | *.local 7 | 8 | # Editor directories and files 9 | .vscode/* 10 | !.vscode/extensions.json 11 | .idea 12 | .DS_Store 13 | *.suo 14 | *.ntvs* 15 | *.njsproj 16 | *.sln 17 | *.sw? 18 | -------------------------------------------------------------------------------- /plugin-examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

List of example previews are here

8 | 9 | 10 | -------------------------------------------------------------------------------- /plugin-examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lightweight-charts-plugin-examples", 3 | "type": "module", 4 | "scripts": { 5 | "build": "tsc", 6 | "compile": "tsc && node compile.mjs", 7 | "dev": "vite --config src/vite.config.js", 8 | "build:examples:site": "vite build --config src/vite.config.js && node build-website.mjs" 9 | }, 10 | "devDependencies": { 11 | "typescript": "5.5.4", 12 | "vite": "^6.3.4" 13 | }, 14 | "dependencies": { 15 | "dts-bundle-generator": "^9.5.1", 16 | "fancy-canvas": "^2.1.0", 17 | "globby": "^14.1.0", 18 | "lightweight-charts": "file:.." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /plugin-examples/src/helpers/dimensions/common.ts: -------------------------------------------------------------------------------- 1 | export interface BitmapPositionLength { 2 | /** coordinate for use with a bitmap rendering scope */ 3 | position: number; 4 | /** length for use with a bitmap rendering scope */ 5 | length: number; 6 | } 7 | -------------------------------------------------------------------------------- /plugin-examples/src/helpers/dimensions/crosshair-width.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Default grid / crosshair line width in Bitmap sizing 3 | * @param horizontalPixelRatio - horizontal pixel ratio 4 | * @returns default grid / crosshair line width in Bitmap sizing 5 | */ 6 | export function gridAndCrosshairBitmapWidth( 7 | horizontalPixelRatio: number 8 | ): number { 9 | return Math.max(1, Math.floor(horizontalPixelRatio)); 10 | } 11 | 12 | /** 13 | * Default grid / crosshair line width in Media sizing 14 | * @param horizontalPixelRatio - horizontal pixel ratio 15 | * @returns default grid / crosshair line width in Media sizing 16 | */ 17 | export function gridAndCrosshairMediaWidth( 18 | horizontalPixelRatio: number 19 | ): number { 20 | return ( 21 | gridAndCrosshairBitmapWidth(horizontalPixelRatio) / horizontalPixelRatio 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /plugin-examples/src/helpers/simple-clone.ts: -------------------------------------------------------------------------------- 1 | type Mutable = { 2 | -readonly [K in keyof T]: T[K] 3 | } 4 | 5 | export function cloneReadonly(obj: T): Mutable { 6 | return JSON.parse(JSON.stringify(obj)); 7 | } 8 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/anchored-text/example/example.ts: -------------------------------------------------------------------------------- 1 | import { createChart, LineSeries } from 'lightweight-charts'; 2 | import { generateLineData } from '../../../sample-data'; 3 | import { AnchoredText } from '../anchored-text'; 4 | 5 | const chart = ((window as unknown as any).chart = createChart('chart', { 6 | autoSize: true 7 | })); 8 | 9 | const lineSeries = chart.addSeries(LineSeries); 10 | 11 | lineSeries.setData(generateLineData()); 12 | 13 | const anchoredText = new AnchoredText({ 14 | vertAlign: 'middle', 15 | horzAlign: 'middle', 16 | text: 'Anchored Text', 17 | lineHeight: 54, 18 | font: 'italic bold 54px Arial', 19 | color: 'red', 20 | }); 21 | lineSeries.attachPrimitive(anchoredText); 22 | 23 | // testing the requestUpdate method 24 | setTimeout(() => { 25 | anchoredText.applyOptions({ 26 | text: 'New Text', 27 | }); 28 | }, 2000); 29 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/anchored-text/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lightweight Charts - Anchored Text Plugin Example 7 | 8 | 9 | 10 |
11 |
12 |

Anchored Text

13 |

14 | Draws text anchored to the center of the chart pane. The text will change after 2 seconds. 15 |

16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/anchored-text/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "main": "anchored-text", 4 | "types": "anchored-text.d.ts" 5 | } 6 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/background-shade-series/example/example.ts: -------------------------------------------------------------------------------- 1 | import { createChart, LineSeries } from 'lightweight-charts'; 2 | import { generateLineData } from '../../../sample-data'; 3 | import { BackgroundShadeSeries } from '../background-shade-series'; 4 | 5 | const chart = ((window as unknown as any).chart = createChart('chart', { 6 | autoSize: true, 7 | })); 8 | 9 | const data = generateLineData(); 10 | 11 | const customSeriesView = new BackgroundShadeSeries(); 12 | const myCustomSeries = chart.addCustomSeries(customSeriesView, { 13 | lowValue: 0, 14 | highValue: 1000, 15 | }); 16 | 17 | myCustomSeries.setData(data); 18 | 19 | const lineSeries = chart.addSeries(LineSeries, { color: 'black' }); 20 | lineSeries.setData(data); 21 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/background-shade-series/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lightweight Charts - Background Shade Series Example 7 | 8 | 9 | 10 |
11 |
12 |

Background Shade Series

13 |

14 | Shades the background of the chart based on the series value. A separate 15 | line series is plotted on top to shows the values of the series. 16 |
Hint: 17 | zoom in and out to see the full effect. 18 |

19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/background-shade-series/options.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CustomSeriesOptions, 3 | customSeriesDefaultOptions, 4 | } from 'lightweight-charts'; 5 | 6 | export interface BackgroundShadeSeriesOptions extends CustomSeriesOptions { 7 | lowColor: string; 8 | highColor: string; 9 | opacity: number; 10 | lowValue: number; 11 | highValue: number; 12 | } 13 | 14 | export const defaultOptions: BackgroundShadeSeriesOptions = { 15 | ...customSeriesDefaultOptions, 16 | lowColor: 'rgb(50, 50, 255)', 17 | highColor: 'rgb(255, 50, 50)', 18 | lowValue: 0, 19 | highValue: 100, 20 | opacity: 0.8, 21 | } as const; 22 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/background-shade-series/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "main": "background-shade-series", 4 | "types": "background-shade-series.d.ts" 5 | } 6 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/bands-indicator/example/example.ts: -------------------------------------------------------------------------------- 1 | import { createChart, LineSeries } from 'lightweight-charts'; 2 | import { generateLineData } from '../../../sample-data'; 3 | import { BandsIndicator } from '../bands-indicator'; 4 | 5 | const chart = ((window as unknown as any).chart = createChart('chart', { 6 | autoSize: true, 7 | })); 8 | 9 | const lineSeries = chart.addSeries(LineSeries); 10 | const data = generateLineData(); 11 | lineSeries.setData(data); 12 | 13 | const bandIndicator = new BandsIndicator(); 14 | lineSeries.attachPrimitive(bandIndicator); 15 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/bands-indicator/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lightweight Charts - Bands Indicator Plugin Example 7 | 8 | 9 | 10 |
11 |
12 |

Bands Indicator

13 |

14 | Draws a filled area band surrounding the series line, which is rendered 15 | beneath the line.
Note: this example is randomly 16 | generated, so refresh the page to see different data. 17 |

18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/box-whisker-series/example/example.ts: -------------------------------------------------------------------------------- 1 | import { WhitespaceData, createChart } from 'lightweight-charts'; 2 | import { WhiskerBoxSeries } from '../box-whisker-series'; 3 | import { WhiskerData, sampleWhiskerData } from '../sample-data'; 4 | 5 | const chart = ((window as unknown as any).chart = createChart('chart', { 6 | autoSize: true, 7 | })); 8 | 9 | const customSeriesView = new WhiskerBoxSeries(); 10 | const myCustomSeries = chart.addCustomSeries(customSeriesView, { 11 | baseLineColor: '', 12 | priceLineVisible: false, 13 | lastValueVisible: false, 14 | }); 15 | 16 | const data: (WhiskerData | WhitespaceData)[] = sampleWhiskerData(); 17 | // data[data.length -2] = { time: data[data.length -2].time }; // test whitespace data 18 | myCustomSeries.setData(data); 19 | 20 | chart.timeScale().fitContent(); 21 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/box-whisker-series/options.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CustomSeriesOptions, 3 | customSeriesDefaultOptions, 4 | } from 'lightweight-charts'; 5 | 6 | export interface WhiskerBoxSeriesOptions extends CustomSeriesOptions { 7 | whiskerColor: string; 8 | lowerQuartileFill: string; 9 | upperQuartileFill: string; 10 | outlierColor: string; 11 | } 12 | 13 | export const defaultOptions: WhiskerBoxSeriesOptions = { 14 | ...customSeriesDefaultOptions, 15 | whiskerColor: 'rgba(106, 27, 154, 1)', 16 | lowerQuartileFill: 'rgba(103, 58, 183, 1)', 17 | upperQuartileFill: 'rgba(233, 30, 99, 1)', 18 | outlierColor: 'rgba(149, 152, 161, 1)', 19 | } as const; 20 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/brushable-area-series/data.ts: -------------------------------------------------------------------------------- 1 | import { CustomData } from 'lightweight-charts'; 2 | 3 | /** 4 | * BrushableArea Series Data 5 | */ 6 | export interface BrushableAreaData extends CustomData { 7 | value: number; 8 | } 9 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/brushable-area-series/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lightweight Charts - Brushable Area Series Plugin Example 7 | 8 | 9 | 10 |
11 |
12 |

Brushable Area Series

13 |

14 | HINT: Click and drag across the chart. Click once to clear. 15 |

16 |

17 | An area series plot where the styling of individual points can be 18 | changed dynamically without needing to set new data. 19 |

20 |
21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/expiring-price-alerts/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lightweight Charts - Rectangle Plugin Example 7 | 8 | 9 | 10 |
11 |
12 |

Expiring Price Alerts

13 |

14 | Price alerts with defined start and end times (expiration dates). 15 |

16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/expiring-price-alerts/iexpiring-price-alerts.ts: -------------------------------------------------------------------------------- 1 | import { IChartApi, ISeriesApi, SeriesOptionsMap } from 'lightweight-charts'; 2 | import { ExpiringPriceAlertParameters } from "./options"; 3 | 4 | export interface ExpiringPriceAlert { 5 | price: number; 6 | start: number; 7 | end: number; 8 | parameters: ExpiringPriceAlertParameters; 9 | crossed: boolean; 10 | expired: boolean; 11 | } 12 | 13 | export interface IExpiringPriceAlerts { 14 | alerts(): Map; 15 | chart(): IChartApi | null; 16 | series(): ISeriesApi; 17 | } 18 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/expiring-price-alerts/options.ts: -------------------------------------------------------------------------------- 1 | export interface ExpiringPriceAlertsOptions { 2 | /** Interval between bars (in seconds) */ 3 | interval: number; 4 | /** Delay when removing an alert */ 5 | clearTimeout: number; 6 | } 7 | 8 | export const defaultOptions: ExpiringPriceAlertsOptions = { 9 | interval: 60 * 60 * 24, 10 | clearTimeout: 3000, 11 | }; 12 | 13 | export interface ExpiringPriceAlertParameters { 14 | // color: string; 15 | title: string; 16 | crossingDirection: 'up' | 'down'; 17 | } 18 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/grouped-bars-series/data.ts: -------------------------------------------------------------------------------- 1 | import { CustomData } from 'lightweight-charts'; 2 | 3 | /** 4 | * GroupedBars Series Data 5 | */ 6 | export interface GroupedBarsData extends CustomData { 7 | values: number[]; 8 | } 9 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/grouped-bars-series/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lightweight Charts - Grouped Bars Series Plugin Example 7 | 8 | 9 | 10 |
11 |
12 |

Grouped Bars Series

13 |

14 | A series containing multiple columns for each data point. The Highlight 15 | Bar Crosshair primitive plugin is also applied to this example to help 16 | define the bars for each data point. 17 |

18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/grouped-bars-series/options.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CustomSeriesOptions, 3 | customSeriesDefaultOptions, 4 | } from 'lightweight-charts'; 5 | 6 | export interface GroupedBarsSeriesOptions extends CustomSeriesOptions { 7 | colors: readonly string[]; 8 | } 9 | 10 | export const defaultOptions: GroupedBarsSeriesOptions = { 11 | ...customSeriesDefaultOptions, 12 | colors: [ 13 | '#2962FF', 14 | '#E1575A', 15 | '#F28E2C', 16 | 'rgb(164, 89, 209)', 17 | 'rgb(27, 156, 133)', 18 | ], 19 | } as const; 20 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/heatmap-series/data.ts: -------------------------------------------------------------------------------- 1 | import { CustomData } from 'lightweight-charts'; 2 | 3 | export interface HeatmapCell { 4 | // Price for the lower edge of the heatmap cell 5 | low: number; 6 | // Price for the upper edge of the heatmap cell 7 | high: number; 8 | // Amount for the cell 9 | amount: number; 10 | } 11 | 12 | /** 13 | * HeatMap Series Data 14 | */ 15 | export interface HeatMapData extends CustomData { 16 | cells: HeatmapCell[]; 17 | } 18 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/heatmap-series/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lightweight Charts - HeatMap Series Plugin Example 7 | 8 | 9 | 10 |
11 |
12 |

Heat Map Series

13 |

14 | Heat map series where each data point (time on the price scale) can have 15 | multiple heat map cells defined for price ranges. 16 |

17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/highlight-bar-crosshair/example/example.ts: -------------------------------------------------------------------------------- 1 | import { CandlestickSeries, createChart } from 'lightweight-charts'; 2 | import { generateAlternativeCandleData } from '../../../sample-data'; 3 | import { CrosshairHighlightPrimitive } from '../highlight-bar-crosshair'; 4 | 5 | const chart = ((window as unknown as any).chart = createChart('chart', { 6 | autoSize: true, 7 | })); 8 | 9 | const candleSeries = chart.addSeries(CandlestickSeries); 10 | candleSeries.setData(generateAlternativeCandleData()); 11 | 12 | const highlightPrimitive = new CrosshairHighlightPrimitive({ 13 | color: 'rgba(0, 50, 100, 0.2)', 14 | }); 15 | 16 | candleSeries.attachPrimitive(highlightPrimitive); 17 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/highlight-bar-crosshair/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lightweight Charts - Highlight Bar Crosshair Plugin Example 7 | 8 | 9 | 10 |
11 |
12 |

Highlight Bar Crosshair

13 |

14 | Shades the background of the data point below the mouse cursor. The 15 | Highlight bar width matches the bar spacing. 16 |

17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/hlc-area-series/data.ts: -------------------------------------------------------------------------------- 1 | import { CustomData } from 'lightweight-charts'; 2 | 3 | /** 4 | * HLCArea Series Data 5 | */ 6 | export interface HLCAreaData extends CustomData { 7 | high: number; 8 | low: number; 9 | close: number; 10 | } 11 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/hlc-area-series/example/example.ts: -------------------------------------------------------------------------------- 1 | import { WhitespaceData, createChart } from 'lightweight-charts'; 2 | import { HLCAreaSeries } from '../hlc-area-series'; 3 | import { HLCAreaData } from '../data'; 4 | import { generateAlternativeCandleData } from '../../../sample-data'; 5 | 6 | const chart = ((window as unknown as any).chart = createChart('chart', { 7 | autoSize: true, 8 | })); 9 | 10 | const customSeriesView = new HLCAreaSeries(); 11 | const myCustomSeries = chart.addCustomSeries(customSeriesView, { 12 | /* Options */ 13 | }); 14 | 15 | const data: (HLCAreaData | WhitespaceData)[] = generateAlternativeCandleData(100); 16 | myCustomSeries.setData(data); 17 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/hlc-area-series/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lightweight Charts - HLCArea Series Plugin Example 7 | 8 | 9 | 10 |
11 |
12 |

HLC Area Series

13 |

14 | HLC (High Low Close) Area Series. A line for each of the high, low, and 15 | close values is plotted whilst the areas are filled between the close 16 | value and the high and low values. 17 |

18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/hlc-area-series/options.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CustomSeriesOptions, 3 | customSeriesDefaultOptions, 4 | } from 'lightweight-charts'; 5 | 6 | export interface HLCAreaSeriesOptions extends CustomSeriesOptions { 7 | highLineColor: string; 8 | lowLineColor: string; 9 | closeLineColor: string; 10 | areaBottomColor: string; 11 | areaTopColor: string; 12 | highLineWidth: number; 13 | lowLineWidth: number; 14 | closeLineWidth: number; 15 | } 16 | 17 | export const defaultOptions: HLCAreaSeriesOptions = { 18 | ...customSeriesDefaultOptions, 19 | highLineColor: '#049981', 20 | lowLineColor: '#F23645', 21 | closeLineColor: '#878993', 22 | areaBottomColor: 'rgba(242, 54, 69, 0.2)', 23 | areaTopColor: 'rgba(4, 153, 129, 0.2)', 24 | highLineWidth: 2, 25 | lowLineWidth: 2, 26 | closeLineWidth: 2, 27 | } as const; 28 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/image-watermark/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lightweight Charts - Image Watermark Plugin Example 7 | 8 | 13 | 14 | 15 |
16 |
17 |

Image Watermark

18 |

19 | Image watermark which is responsive to the size of the chart pane. 20 | Padding, maximum width, maximum height values can be defined to control 21 | the behaviour of the watermark. 22 |

23 |
24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/lollipop-series/data.ts: -------------------------------------------------------------------------------- 1 | import { CustomData } from 'lightweight-charts'; 2 | 3 | /** 4 | * Lollipop Series Data 5 | */ 6 | export interface LollipopData extends CustomData { 7 | value: number; 8 | } 9 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/lollipop-series/example/example.ts: -------------------------------------------------------------------------------- 1 | import { WhitespaceData, createChart } from 'lightweight-charts'; 2 | import { generateLineData, shuffleValuesWithLimit } from '../../../sample-data'; 3 | import { LollipopSeries } from '../lollipop-series'; 4 | import { LollipopData } from '../data'; 5 | 6 | const chart = ((window as unknown as any).chart = createChart('chart', { 7 | autoSize: true, 8 | })); 9 | 10 | const customSeriesView = new LollipopSeries(); 11 | const myCustomSeries = chart.addCustomSeries(customSeriesView, { 12 | /* Options */ 13 | lineWidth: 2, 14 | }); 15 | 16 | const data: (LollipopData | WhitespaceData)[] = shuffleValuesWithLimit(generateLineData(100), 10); 17 | myCustomSeries.setData(data); 18 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/lollipop-series/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lightweight Charts - Lollipop Series Plugin Example 7 | 8 | 9 | 10 |
11 |
12 |

Lollipop Series

13 |

14 | Series where the price values are plotted as circular marks with a 15 | column towards the baseline value (typically zero). 16 |

17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/lollipop-series/options.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CustomSeriesOptions, 3 | customSeriesDefaultOptions, 4 | } from 'lightweight-charts'; 5 | 6 | export interface LollipopSeriesOptions extends CustomSeriesOptions { 7 | lineWidth: number; 8 | } 9 | 10 | export const defaultOptions: LollipopSeriesOptions = { 11 | ...customSeriesDefaultOptions, 12 | lineWidth: 2, 13 | } as const; 14 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/overlay-price-scale/example/example.ts: -------------------------------------------------------------------------------- 1 | import { createChart, LineSeries } from 'lightweight-charts'; 2 | import { generateLineData } from '../../../sample-data'; 3 | import { OverlayPriceScale } from '../overlay-price-scale'; 4 | 5 | const chart = ((window as unknown as any).chart = createChart('chart', { 6 | autoSize: true, 7 | rightPriceScale: { 8 | visible: false, 9 | }, 10 | grid: { 11 | horzLines: { 12 | visible: false, 13 | }, 14 | }, 15 | })); 16 | 17 | const lineSeries = chart.addSeries(LineSeries, { 18 | priceScaleId: 'overlay', 19 | }); 20 | 21 | const data = generateLineData(); 22 | lineSeries.setData(data); 23 | 24 | lineSeries.attachPrimitive(new OverlayPriceScale({})); 25 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/overlay-price-scale/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lightweight Charts - Overlay Price Scale Plugin Example 7 | 8 | 9 | 10 |
11 |
12 |

Overlay Price Scale

13 |

14 | A price scale which appears on the main chart pane area. For use with 15 | series assigned to an overlay price scale. 16 |

17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/partial-price-line/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lightweight Charts - Partial Price Line Plugin Example 7 | 8 | 9 | 10 |
11 |
12 |

Partial Price Line

13 |

14 | A Price line which is only drawn from the latest price to the right 15 | price scale (instead of across the entire width of the chart). 16 |

17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/rectangle-drawing-tool/example/example.ts: -------------------------------------------------------------------------------- 1 | import { LineSeries, createChart } from 'lightweight-charts'; 2 | import { generateLineData } from '../../../sample-data'; 3 | import { RectangleDrawingTool } from '../rectangle-drawing-tool'; 4 | 5 | const chart = ((window as unknown as any).chart = createChart('chart', { 6 | autoSize: true, 7 | })); 8 | 9 | const lineSeries = chart.addSeries(LineSeries); 10 | const data = generateLineData(); 11 | lineSeries.setData(data); 12 | 13 | new RectangleDrawingTool( 14 | chart, 15 | lineSeries, 16 | document.querySelector('#toolbar')!, 17 | { 18 | showLabels: false, 19 | } 20 | ); 21 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/rounded-candles-series/data.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CandlestickData, 3 | CustomData, 4 | } from 'lightweight-charts'; 5 | 6 | export interface RoundedCandleSeriesData 7 | extends CandlestickData, 8 | CustomData { 9 | rounded?: boolean; 10 | } 11 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/rounded-candles-series/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lightweight Charts - Rounded Candles Series Example 7 | 8 | 9 | 10 |
11 |
12 |

Rounded Candles Series

13 |

14 | Candle series where at larger sizes the corners of the candle bodies are rounded. 15 |

16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/stacked-area-series/data.ts: -------------------------------------------------------------------------------- 1 | import { CustomData } from 'lightweight-charts'; 2 | 3 | /** 4 | * StackedArea Series Data 5 | */ 6 | export interface StackedAreaData extends CustomData { 7 | values: number[]; 8 | } 9 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/stacked-area-series/example/example.ts: -------------------------------------------------------------------------------- 1 | import { WhitespaceData, createChart } from 'lightweight-charts'; 2 | import { StackedAreaData } from '../data'; 3 | import { multipleBarData } from '../../../sample-data'; 4 | import { StackedAreaSeries } from '../stacked-area-series'; 5 | 6 | const chart = ((window as unknown as any).chart = createChart('chart', { 7 | autoSize: true, 8 | rightPriceScale: { 9 | scaleMargins: { 10 | top: 0.05, 11 | bottom: 0.05, 12 | } 13 | } 14 | })); 15 | 16 | const customSeriesView = new StackedAreaSeries(); 17 | const myCustomSeries = chart.addCustomSeries(customSeriesView, { 18 | /* Options */ 19 | }); 20 | 21 | const data: (StackedAreaData | WhitespaceData)[] = multipleBarData(5, 200, 2); 22 | myCustomSeries.setData(data); 23 | 24 | chart.timeScale().fitContent(); 25 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/stacked-area-series/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lightweight Charts - StackedArea Series Plugin Example 7 | 8 | 9 | 10 |
11 |
12 |

Stacked Area Series

13 |

14 | Multiple filled areas stacked on top of each other, representing the 15 | proportion of each variable at different points along the axis. 16 |

17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/stacked-bars-series/data.ts: -------------------------------------------------------------------------------- 1 | import { CustomData } from 'lightweight-charts'; 2 | 3 | /** 4 | * StackedBars Series Data 5 | */ 6 | export interface StackedBarsData extends CustomData { 7 | values: number[]; 8 | } 9 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/stacked-bars-series/example/example.ts: -------------------------------------------------------------------------------- 1 | import { WhitespaceData, createChart } from 'lightweight-charts'; 2 | import { StackedBarsSeries } from '../stacked-bars-series'; 3 | import { StackedBarsData } from '../data'; 4 | import { multipleBarData } from '../../../sample-data'; 5 | 6 | const chart = ((window as unknown as any).chart = createChart('chart', { 7 | autoSize: true, 8 | timeScale: { 9 | minBarSpacing: 3, 10 | } 11 | })); 12 | 13 | const customSeriesView = new StackedBarsSeries(); 14 | const myCustomSeries = chart.addCustomSeries(customSeriesView, { 15 | /* Options */ 16 | color: 'black', // for the price line 17 | }); 18 | 19 | const data: (StackedBarsData | WhitespaceData)[] = multipleBarData(3, 200, 20); 20 | myCustomSeries.setData(data); 21 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/stacked-bars-series/options.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CustomSeriesOptions, 3 | customSeriesDefaultOptions, 4 | } from 'lightweight-charts'; 5 | 6 | export interface StackedBarsSeriesOptions extends CustomSeriesOptions { 7 | colors: readonly string[]; 8 | } 9 | 10 | export const defaultOptions: StackedBarsSeriesOptions = { 11 | ...customSeriesDefaultOptions, 12 | colors: [ 13 | '#2962FF', 14 | '#E1575A', 15 | '#F28E2C', 16 | 'rgb(164, 89, 209)', 17 | 'rgb(27, 156, 133)', 18 | ], 19 | } as const; 20 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/trend-line/example/example.ts: -------------------------------------------------------------------------------- 1 | import { LineSeries, createChart } from 'lightweight-charts'; 2 | import { generateLineData } from '../../../sample-data'; 3 | import { TrendLine } from '../trend-line'; 4 | 5 | const chart = ((window as unknown as any).chart = createChart('chart', { 6 | autoSize: true, 7 | })); 8 | 9 | const lineSeries = chart.addSeries(LineSeries); 10 | const data = generateLineData(); 11 | lineSeries.setData(data); 12 | 13 | const dataLength = data.length; 14 | const point1 = { 15 | time: data[dataLength - 50].time, 16 | price: data[dataLength - 50].value * 0.9, 17 | }; 18 | const point2 = { 19 | time: data[dataLength - 5].time, 20 | price: data[dataLength - 5].value * 1.10, 21 | }; 22 | const trend = new TrendLine(chart, lineSeries, point1, point2); 23 | lineSeries.attachPrimitive(trend); 24 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/trend-line/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lightweight Charts - Trend Line Plugin Example 7 | 8 | 9 | 10 |
11 |
12 |

Trend Line

13 |

14 | A trend line drawn between two points (defined by price and time values) 15 | on the chart.
Note: This example is randomly generated. 16 |

17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/user-price-alerts/renderer-base.ts: -------------------------------------------------------------------------------- 1 | import { CanvasRenderingTarget2D } from 'fancy-canvas'; 2 | import { IPrimitivePaneRenderer } from 'lightweight-charts'; 3 | import { IRendererData } from './irenderer-data'; 4 | 5 | export abstract class PaneRendererBase implements IPrimitivePaneRenderer { 6 | _data: IRendererData | null = null; 7 | abstract draw(target: CanvasRenderingTarget2D): void; 8 | update(data: IRendererData | null) { 9 | this._data = data; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/user-price-lines/example/example.ts: -------------------------------------------------------------------------------- 1 | import { LineSeries, createChart } from 'lightweight-charts'; 2 | import { generateLineData } from '../../../sample-data'; 3 | import { UserPriceLines } from '../user-price-lines'; 4 | 5 | const chart = ((window as unknown as any).chart = createChart('chart', { 6 | autoSize: true, 7 | })); 8 | 9 | const lineSeries = chart.addSeries(LineSeries); 10 | const data = generateLineData(); 11 | lineSeries.setData(data); 12 | 13 | new UserPriceLines(chart, lineSeries, { color: 'hotpink' }); 14 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/user-price-lines/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lightweight Charts - User Price Lines Plugin Example 7 | 8 | 9 | 10 |
11 |
12 |

User Price Line

13 |

14 | Price lines created via the crosshair button (next to the price scale). 15 | The crosshair button only appears when the cursor is near the price 16 | scale. 17 |

18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/vertical-line/example/example.ts: -------------------------------------------------------------------------------- 1 | import { LineSeries, createChart } from 'lightweight-charts'; 2 | import { generateLineData } from '../../../sample-data'; 3 | import { VertLine } from '../vertical-line'; 4 | 5 | const chart = ((window as unknown as any).chart = createChart('chart', { 6 | autoSize: true, 7 | })); 8 | 9 | const lineSeries = chart.addSeries(LineSeries); 10 | const data = generateLineData(); 11 | lineSeries.setData(data); 12 | 13 | const vertLine = new VertLine(chart, lineSeries, data[data.length - 50].time, { 14 | showLabel: true, 15 | labelText: 'Hello', 16 | }); 17 | lineSeries.attachPrimitive(vertLine); 18 | 19 | const vertLine2 = new VertLine(chart, lineSeries, data[data.length - 25].time, { 20 | showLabel: false, 21 | color: 'red', 22 | width: 2, 23 | }); 24 | lineSeries.attachPrimitive(vertLine2); 25 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/vertical-line/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lightweight Charts - Vertical Line Plugin Example 7 | 8 | 9 | 10 |
11 |
12 |

Vertical Line

13 |

14 | Vertical Line draw at specified time values with a corresponding label 15 | on the time scale. 16 |

17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /plugin-examples/src/plugins/volume-profile/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lightweight Charts - Volume Profile Plugin Example 7 | 8 | 9 | 10 |
11 |
12 |

Volume Profile

13 |

14 | A Volume Profile anchored to a specified point (defined by price and 15 | time values) on the chart.
Note: that the example 16 | is randomly generated so be sure to refresh the chart a few times. 17 |

18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /plugin-examples/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /plugin-examples/src/vite.config.js: -------------------------------------------------------------------------------- 1 | import { resolve } from 'path'; 2 | import { defineConfig } from 'vite'; 3 | import { globby } from 'globby'; 4 | 5 | const paths = (await globby(['./src/**/*.html'])).map(path => 6 | path.replace('./src/', '') 7 | ); 8 | 9 | const input = { 10 | main: resolve(__dirname, '../index.html'), 11 | }; 12 | 13 | let count = 0; 14 | paths.forEach(p => { 15 | input[count++] = resolve(__dirname, p); 16 | }); 17 | 18 | export default defineConfig({ 19 | base: './', 20 | build: { 21 | rollupOptions: { 22 | input, 23 | }, 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /plugin-examples/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ESNext", "DOM"], 7 | "moduleResolution": "Node", 8 | "strict": true, 9 | "resolveJsonModule": true, 10 | "isolatedModules": true, 11 | "esModuleInterop": true, 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true, 14 | "noImplicitReturns": true, 15 | "skipLibCheck": true, 16 | "declaration": true, 17 | "declarationDir": "typings", 18 | "emitDeclarationOnly": true, 19 | "typeRoots": [ 20 | "./node_modules/@types" 21 | ] 22 | }, 23 | "include": ["src"] 24 | } 25 | -------------------------------------------------------------------------------- /scripts/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | node: true, 4 | }, 5 | rules: { 6 | 'no-console': [ 7 | 'off', 8 | ], 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /scripts/no-crlf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | git --no-pager grep -I --perl-regexp --files-with-matches '\r' ':!*.bat' . 4 | 5 | if [ $? -eq 0 ] 6 | then 7 | echo -e "\033[31mThese files has CRLF line endings!\033[m" 8 | exit 1 9 | fi 10 | -------------------------------------------------------------------------------- /scripts/run-coverage-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | echo "Preparing" 4 | 5 | npm run build 6 | 7 | echo "Coverage tests" 8 | npm run e2e:coverage 9 | -------------------------------------------------------------------------------- /scripts/run-interactions-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | echo "Preparing" 4 | 5 | npm run build 6 | 7 | echo "Interactions tests" 8 | npm run e2e:interactions 9 | -------------------------------------------------------------------------------- /scripts/run-memleaks-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | echo "Preparing" 4 | 5 | # npm run build 6 | 7 | echo "Memleaks tests" 8 | if [[ ! -z "$RUNNING_ON_CI" ]]; then 9 | echo "Running on CI, therefore logging mem leaks output to artifact file" 10 | rm -rf tests/e2e/memleaks/.logs 11 | mkdir tests/e2e/memleaks/.logs 12 | npm run e2e:memleaks > ./tests/e2e/memleaks/.logs/memleaks.txt 13 | else 14 | npm run e2e:memleaks 15 | fi 16 | -------------------------------------------------------------------------------- /scripts/trailing-newlines.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | lf=$(printf "\n") 4 | 5 | git ls-files \ 6 | '*.sh' \ 7 | '*.js' '*.ts' \ 8 | '*.md' \ 9 | '*.json' \ 10 | | { 11 | exit_code=0 12 | while read filename; do 13 | last_char=$(tail -c 1 $filename) 14 | if [ "$last_char" != "$lf" ] && [ "$last_char" != "" ]; then 15 | echo $filename 16 | exit_code=1 17 | fi 18 | done 19 | exit $exit_code 20 | } 21 | 22 | if [ $? -ne 0 ]; then 23 | echo -e "\033[31mThese files should end with a newline!\033[m" 24 | exit 1 25 | fi 26 | -------------------------------------------------------------------------------- /src/api/candlestick-series-api.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CandlestickSeriesPartialOptions, 3 | fillUpDownCandlesticksColors, 4 | } from '../model/series-options'; 5 | 6 | import { SeriesApi } from './series-api'; 7 | 8 | export class CandlestickSeriesApi extends SeriesApi<'Candlestick', HorzScaleItem> { 9 | public override applyOptions(options: CandlestickSeriesPartialOptions): void { 10 | fillUpDownCandlesticksColors(options); 11 | super.applyOptions(options); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/api/iprice-line.ts: -------------------------------------------------------------------------------- 1 | import { PriceLineOptions } from '../model/price-line-options'; 2 | 3 | /** 4 | * Represents the interface for interacting with price lines. 5 | */ 6 | export interface IPriceLine { 7 | /** 8 | * Apply options to the price line. 9 | * 10 | * @param options - Any subset of options. 11 | * @example 12 | * ```js 13 | * priceLine.applyOptions({ 14 | * price: 90.0, 15 | * color: 'red', 16 | * lineWidth: 3, 17 | * lineStyle: LightweightCharts.LineStyle.Dashed, 18 | * axisLabelVisible: false, 19 | * title: 'P/L 600', 20 | * }); 21 | * ``` 22 | */ 23 | applyOptions(options: Partial): void; 24 | /** 25 | * Get the currently applied options. 26 | */ 27 | options(): Readonly; 28 | } 29 | -------------------------------------------------------------------------------- /src/api/options/crosshair-options-defaults.ts: -------------------------------------------------------------------------------- 1 | import { CrosshairMode, CrosshairOptions } from '../../model/crosshair'; import { LineStyle } from '../../renderers/draw-line'; 2 | 3 | export const crosshairOptionsDefaults: CrosshairOptions = { 4 | vertLine: { 5 | color: '#9598A1', 6 | width: 1, 7 | style: LineStyle.LargeDashed, 8 | visible: true, 9 | labelVisible: true, 10 | labelBackgroundColor: '#131722', 11 | }, 12 | horzLine: { 13 | color: '#9598A1', 14 | width: 1, 15 | style: LineStyle.LargeDashed, 16 | visible: true, 17 | labelVisible: true, 18 | labelBackgroundColor: '#131722', 19 | }, 20 | mode: CrosshairMode.Magnet, 21 | }; 22 | -------------------------------------------------------------------------------- /src/api/options/grid-options-defaults.ts: -------------------------------------------------------------------------------- 1 | import { GridOptions } from '../../model/grid'; 2 | import { LineStyle } from '../../renderers/draw-line'; 3 | 4 | export const gridOptionsDefaults: GridOptions = { 5 | vertLines: { 6 | color: '#D6DCDE', 7 | style: LineStyle.Solid, 8 | visible: true, 9 | }, 10 | horzLines: { 11 | color: '#D6DCDE', 12 | style: LineStyle.Solid, 13 | visible: true, 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /src/api/options/layout-options-defaults.ts: -------------------------------------------------------------------------------- 1 | import { defaultFontFamily } from '../../helpers/make-font'; 2 | 3 | import { ColorType, LayoutOptions } from '../../model/layout-options'; 4 | 5 | export const layoutOptionsDefaults: LayoutOptions = { 6 | background: { 7 | type: ColorType.Solid, 8 | color: '#FFFFFF', 9 | }, 10 | textColor: '#191919', 11 | fontSize: 12, 12 | fontFamily: defaultFontFamily, 13 | panes: { 14 | enableResize: true, 15 | separatorColor: '#E0E3EB', 16 | separatorHoverColor: 'rgba(178, 181, 189, 0.2)', 17 | }, 18 | attributionLogo: true, 19 | colorSpace: 'srgb', 20 | colorParsers: [], 21 | }; 22 | -------------------------------------------------------------------------------- /src/api/options/price-line-options-defaults.ts: -------------------------------------------------------------------------------- 1 | import { PriceLineOptions } from '../../model/price-line-options'; 2 | import { LineStyle } from '../../renderers/draw-line'; 3 | 4 | export const priceLineOptionsDefaults: PriceLineOptions = { 5 | color: '#FF0000', 6 | price: 0, 7 | lineStyle: LineStyle.Dashed, 8 | lineWidth: 1, 9 | lineVisible: true, 10 | axisLabelVisible: true, 11 | title: '', 12 | axisLabelColor: '', 13 | axisLabelTextColor: '', 14 | }; 15 | -------------------------------------------------------------------------------- /src/api/options/price-scale-options-defaults.ts: -------------------------------------------------------------------------------- 1 | import { PriceScaleMode, PriceScaleOptions } from '../../model/price-scale'; 2 | 3 | export const priceScaleOptionsDefaults: PriceScaleOptions = { 4 | autoScale: true, 5 | mode: PriceScaleMode.Normal, 6 | invertScale: false, 7 | alignLabels: true, 8 | borderVisible: true, 9 | borderColor: '#2B2B43', 10 | entireTextOnly: false, 11 | visible: false, 12 | ticksVisible: false, 13 | scaleMargins: { 14 | bottom: 0.1, 15 | top: 0.2, 16 | }, 17 | minimumWidth: 0, 18 | ensureEdgeTickMarksVisible: false, 19 | }; 20 | -------------------------------------------------------------------------------- /src/api/options/series-options-defaults.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CustomStyleOptions, 3 | PriceLineSource, 4 | SeriesOptionsCommon, 5 | } from '../../model/series-options'; 6 | import { LineStyle } from '../../renderers/draw-line'; 7 | 8 | export const customStyleDefaults: CustomStyleOptions = { 9 | color: '#2196f3', 10 | }; 11 | 12 | export const seriesOptionsDefaults: SeriesOptionsCommon = { 13 | title: '', 14 | visible: true, 15 | lastValueVisible: true, 16 | priceLineVisible: true, 17 | priceLineSource: PriceLineSource.LastBar, 18 | priceLineWidth: 1, 19 | priceLineColor: '', 20 | priceLineStyle: LineStyle.Dashed, 21 | baseLineVisible: true, 22 | baseLineWidth: 1, 23 | baseLineColor: '#B2B5BE', 24 | baseLineStyle: LineStyle.Solid, 25 | priceFormat: { 26 | type: 'price', 27 | precision: 2, 28 | minMove: 0.01, 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /src/api/options/time-scale-options-defaults.ts: -------------------------------------------------------------------------------- 1 | import { HorzScaleOptions } from '../../model/time-scale'; 2 | 3 | export const timeScaleOptionsDefaults: HorzScaleOptions = { 4 | rightOffset: 0, 5 | barSpacing: 6, 6 | minBarSpacing: 0.5, 7 | maxBarSpacing: 0, 8 | fixLeftEdge: false, 9 | fixRightEdge: false, 10 | lockVisibleTimeRangeOnResize: false, 11 | rightBarStaysOnScroll: false, 12 | borderVisible: true, 13 | borderColor: '#2B2B43', 14 | visible: true, 15 | timeVisible: false, 16 | secondsVisible: true, 17 | shiftVisibleRangeOnNewBar: true, 18 | allowShiftVisibleRangeOnWhitespaceReplacement: false, 19 | ticksVisible: false, 20 | uniformDistribution: false, 21 | minimumHeight: 0, 22 | allowBoldLabels: true, 23 | ignoreWhitespaceIndices: false, 24 | }; 25 | -------------------------------------------------------------------------------- /src/api/options/yield-curve-chart-options-defaults.ts: -------------------------------------------------------------------------------- 1 | import { YieldCurveOptions } from '../../model/yield-curve-horz-scale-behavior/yield-curve-chart-options'; 2 | 3 | export const yieldChartOptionsDefaults: YieldCurveOptions = { 4 | baseResolution: 1, 5 | minimumTimeRange: 120, 6 | startTimeRange: 0, 7 | }; 8 | -------------------------------------------------------------------------------- /src/api/price-line-api.ts: -------------------------------------------------------------------------------- 1 | import { CustomPriceLine } from '../model/custom-price-line'; 2 | import { PriceLineOptions } from '../model/price-line-options'; 3 | 4 | import { IPriceLine } from './iprice-line'; 5 | 6 | export class PriceLine implements IPriceLine { 7 | private readonly _priceLine: CustomPriceLine; 8 | 9 | public constructor(priceLine: CustomPriceLine) { 10 | this._priceLine = priceLine; 11 | } 12 | 13 | public applyOptions(options: Partial): void { 14 | this._priceLine.applyOptions(options); 15 | } 16 | public options(): Readonly { 17 | return this._priceLine.options(); 18 | } 19 | 20 | public priceLine(): CustomPriceLine { 21 | return this._priceLine; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/api/tsconfig.composite.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.composite.base.json", 3 | "references": [ 4 | { "path": "../formatters/tsconfig.composite.json" }, 5 | { "path": "../gui/tsconfig.composite.json" }, 6 | { "path": "../tsconfig.model.json" } 7 | ], 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/formatters/date-formatter.ts: -------------------------------------------------------------------------------- 1 | import { formatDate } from './format-date'; 2 | 3 | export class DateFormatter { 4 | private readonly _locale: string; 5 | private readonly _dateFormat: string; 6 | 7 | public constructor(dateFormat: string = 'yyyy-MM-dd', locale: string = 'default') { 8 | this._dateFormat = dateFormat; 9 | this._locale = locale; 10 | } 11 | 12 | public format(date: Date): string { 13 | return formatDate(date, this._dateFormat, this._locale); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/formatters/iprice-formatter.ts: -------------------------------------------------------------------------------- 1 | /** Interface to be implemented by the object in order to be used as a price formatter */ 2 | export interface IPriceFormatter { 3 | /** 4 | * Formatting function 5 | * 6 | * @param price - Original price to be formatted 7 | * @returns Formatted price 8 | */ 9 | format(price: number): string; 10 | } 11 | -------------------------------------------------------------------------------- /src/formatters/percentage-formatter.ts: -------------------------------------------------------------------------------- 1 | import { PriceFormatter } from './price-formatter'; 2 | 3 | export class PercentageFormatter extends PriceFormatter { 4 | public constructor(priceScale: number = 100) { 5 | super(priceScale); 6 | } 7 | 8 | public override format(price: number): string { 9 | return `${super.format(price)}%`; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/formatters/time-formatter.ts: -------------------------------------------------------------------------------- 1 | import { numberToStringWithLeadingZero } from './price-formatter'; 2 | 3 | export class TimeFormatter { 4 | private _formatStr: string; 5 | 6 | public constructor(format?: string) { 7 | this._formatStr = format || '%h:%m:%s'; 8 | } 9 | 10 | public format(date: Date): string { 11 | return this._formatStr.replace('%h', numberToStringWithLeadingZero(date.getUTCHours(), 2)). 12 | replace('%m', numberToStringWithLeadingZero(date.getUTCMinutes(), 2)). 13 | replace('%s', numberToStringWithLeadingZero(date.getUTCSeconds(), 2)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/formatters/tsconfig.composite.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.composite.base.json", 3 | "references": [ 4 | { "path": "../helpers/tsconfig.composite.json" } 5 | ], 6 | "include": [ 7 | "./**/*.ts" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /src/gui/iaxis-view-getters.ts: -------------------------------------------------------------------------------- 1 | import { IDataSource } from '../model/idata-source'; 2 | import { Pane } from '../model/pane'; 3 | import { IAxisView } from '../views/pane/iaxis-view'; 4 | 5 | export type IAxisViewsGetter = ( 6 | source: IDataSource, 7 | pane?: Pane, 8 | ) => readonly IAxisView[]; 9 | 10 | export type IPriceAxisViewsGetter = IAxisViewsGetter; 11 | export type ITimeAxisViewsGetter = IAxisViewsGetter; 12 | -------------------------------------------------------------------------------- /src/gui/ipane-view-getter.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IDataSourcePaneViews, 3 | } from '../model/idata-source'; 4 | import { Pane } from '../model/pane'; 5 | import { IPaneView } from '../views/pane/ipane-view'; 6 | 7 | export type IPaneViewsGetter = ( 8 | source: IDataSourcePaneViews, 9 | pane: Pane 10 | ) => readonly IPaneView[]; 11 | -------------------------------------------------------------------------------- /src/gui/tsconfig.composite.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.composite.base.json", 3 | "references": [ 4 | { "path": "../tsconfig.model.json" } 5 | ], 6 | "include": [ 7 | "./**/*.ts", 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /src/helpers/events.ts: -------------------------------------------------------------------------------- 1 | import { isChrome } from './browsers'; 2 | 3 | export function preventScrollByWheelClick(el: HTMLElement): void { 4 | if (!isChrome()) { 5 | return; 6 | } 7 | 8 | el.addEventListener('mousedown', (e: MouseEvent) => { 9 | // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison 10 | if (e.button === MouseEventButton.Middle) { 11 | // prevent incorrect scrolling event 12 | e.preventDefault(); 13 | return false; 14 | } 15 | return undefined; 16 | }); 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/helpers/idestroyable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * IDESTRØYÅBLE 3 | * ┌────────────────┐ 4 | * │ ┏━━━━━┓ │ 5 | * │ ┃ ○ ○ ┃ │ ┏━━━━━┓ ┌╲╌╌╱╌┐ 6 | * │ ┃ ○ ○ ┃ x 1 │ ┃ ○ ○ ┃ destroy() ┊ ╲╱ ╵ 7 | * │ ┠─────┨ │ ┃ ○ ○ ┃ ╭───╯╲ ╷ ╱╲ ┊ 8 | * │ ┗━━━━━┛ │ ┠─────┨ ╰───╮╱ ├╱ ╌╲ ┤ 9 | * │ destroy() x 1 │ ┗━━━━━┛ └ ╌╌ ╌┘ 10 | * └────────────────┘ 11 | */ 12 | export interface IDestroyable { 13 | destroy(): void; 14 | } 15 | -------------------------------------------------------------------------------- /src/helpers/is-running-on-client-side.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * When you're trying to use the library in server-side context (for instance in SSR) 3 | * you don't have some browser-specific variables like navigator or window 4 | * and if the library will use them on the top level of the library 5 | * the import will fail due ReferenceError 6 | * thus, this allows use the navigator on the top level and being imported in server-side context as well 7 | * See issue #446 8 | */ 9 | // eslint-disable-next-line @typescript-eslint/tslint/config 10 | export const isRunningOnClientSide = typeof window !== 'undefined'; 11 | -------------------------------------------------------------------------------- /src/helpers/isubscription.ts: -------------------------------------------------------------------------------- 1 | export type Callback = (param1: T1, param2: T2, param3: T3) => void; 2 | 3 | export interface ISubscription { 4 | subscribe(callback: Callback, linkedObject?: unknown, singleshot?: boolean): void; 5 | unsubscribe(callback: Callback): void; 6 | unsubscribeAll(linkedObject: unknown): void; 7 | } 8 | -------------------------------------------------------------------------------- /src/helpers/logger.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export function warn(msg: string): void { 4 | if (process.env.NODE_ENV === 'development') { 5 | // eslint-disable-next-line no-console 6 | console.warn(msg); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/helpers/mutable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Removes "readonly" from all properties 3 | */ 4 | export type Mutable = { 5 | -readonly [P in keyof T]: T[P]; 6 | }; 7 | -------------------------------------------------------------------------------- /src/helpers/nominal.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This is the generic type useful for declaring a nominal type, 3 | * which does not structurally matches with the base type and 4 | * the other types declared over the same base type 5 | * 6 | * @example 7 | * ```ts 8 | * type Index = Nominal; 9 | * // let i: Index = 42; // this fails to compile 10 | * let i: Index = 42 as Index; // OK 11 | * ``` 12 | * @example 13 | * ```ts 14 | * type TagName = Nominal; 15 | * ``` 16 | */ 17 | export type Nominal = T & { 18 | /** The 'name' or species of the nominal. */ 19 | [Symbol.species]: Name; 20 | }; 21 | -------------------------------------------------------------------------------- /src/helpers/tsconfig.composite.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.composite.base.json", 3 | "include": [ 4 | "./**/*.ts" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /src/model/bar.ts: -------------------------------------------------------------------------------- 1 | import { Nominal } from '../helpers/nominal'; 2 | 3 | import { Coordinate } from './coordinate'; 4 | 5 | /** 6 | * Represents a price as a `number`. 7 | */ 8 | export type BarPrice = Nominal; 9 | 10 | /** 11 | * Represents a bar's open, high, low, close (OHLC) prices. 12 | */ 13 | export interface BarPrices { 14 | /** 15 | * The open price. 16 | */ 17 | open: BarPrice; 18 | /** 19 | * The high price. 20 | */ 21 | high: BarPrice; 22 | /** 23 | * The low price. 24 | */ 25 | low: BarPrice; 26 | /** 27 | * The close price. 28 | */ 29 | close: BarPrice; 30 | } 31 | 32 | /** 33 | * Represents the y-axis coordinates of a bar's open, high, low, close prices. 34 | */ 35 | export interface BarCoordinates { 36 | openY: Coordinate; 37 | highY: Coordinate; 38 | lowY: Coordinate; 39 | closeY: Coordinate; 40 | } 41 | -------------------------------------------------------------------------------- /src/model/coordinate.ts: -------------------------------------------------------------------------------- 1 | import { Nominal } from '../helpers/nominal'; 2 | 3 | /** 4 | * Represents a coordiate as a `number`. 5 | */ 6 | export type Coordinate = Nominal; 7 | -------------------------------------------------------------------------------- /src/model/default-price-scale.ts: -------------------------------------------------------------------------------- 1 | export const enum DefaultPriceScaleId { 2 | Left = 'left', 3 | Right = 'right', 4 | } 5 | 6 | export function isDefaultPriceScale(priceScaleId: string): priceScaleId is DefaultPriceScaleId { 7 | // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison 8 | return priceScaleId === DefaultPriceScaleId.Left || priceScaleId === DefaultPriceScaleId.Right; 9 | } 10 | -------------------------------------------------------------------------------- /src/model/horz-scale-behavior-price/types.ts: -------------------------------------------------------------------------------- 1 | export type HorzScalePriceItem = number; 2 | -------------------------------------------------------------------------------- /src/model/iprice-data-source.ts: -------------------------------------------------------------------------------- 1 | import { IPriceFormatter } from '../formatters/iprice-formatter'; 2 | 3 | import { AutoscaleInfoImpl } from './autoscale-info-impl'; 4 | import { IChartModelBase } from './chart-model'; 5 | import { IDataSource } from './idata-source'; 6 | import { InternalHorzScaleItem } from './ihorz-scale-behavior'; 7 | import { TimePointIndex } from './time-data'; 8 | 9 | export interface FirstValue { 10 | value: number; 11 | timePoint: InternalHorzScaleItem; 12 | } 13 | 14 | export interface IPriceDataSource extends IDataSource { 15 | firstValue(): FirstValue | null; 16 | formatter(): IPriceFormatter; 17 | priceLineColor(lastBarColor: string): string; 18 | minMove(): number; 19 | autoscaleInfo(startTimePoint: TimePointIndex, endTimePoint: TimePointIndex): AutoscaleInfoImpl | null; 20 | model(): IChartModelBase; 21 | } 22 | -------------------------------------------------------------------------------- /src/model/plot-data.ts: -------------------------------------------------------------------------------- 1 | import { InternalHorzScaleItem } from './ihorz-scale-behavior'; 2 | import { TimePointIndex } from './time-data'; 3 | 4 | /** 5 | * Plot's index in plot list tuple for series 6 | */ 7 | export const enum PlotRowValueIndex { 8 | Open = 0, 9 | High = 1, 10 | Low = 2, 11 | Close = 3, 12 | } 13 | 14 | export type PlotRowValue = [ 15 | number, // open 16 | number, // high 17 | number, // low 18 | number, // close 19 | ]; 20 | 21 | export interface PlotRow { 22 | readonly index: TimePointIndex; 23 | readonly time: InternalHorzScaleItem; 24 | readonly originalTime: unknown; 25 | readonly value: PlotRowValue; 26 | readonly customValues?: Record; 27 | } 28 | -------------------------------------------------------------------------------- /src/model/point.ts: -------------------------------------------------------------------------------- 1 | import { Coordinate } from './coordinate'; 2 | 3 | /** 4 | * Represents a point on the chart. 5 | */ 6 | export interface Point { 7 | /** 8 | * The x coordinate. 9 | */ 10 | readonly x: Coordinate; 11 | /** 12 | * The y coordinate. 13 | */ 14 | readonly y: Coordinate; 15 | } 16 | -------------------------------------------------------------------------------- /src/model/price-formatter-fn.ts: -------------------------------------------------------------------------------- 1 | import { BarPrice } from './bar'; 2 | 3 | /** 4 | * A function used to format a {@link BarPrice} as a string. 5 | */ 6 | export type PriceFormatterFn = (priceValue: BarPrice) => string; 7 | 8 | /** 9 | * A function used to format a percentage value as a string. 10 | */ 11 | export type PercentageFormatterFn = (percentageValue: number) => string; 12 | -------------------------------------------------------------------------------- /src/model/series/pane-view.ts: -------------------------------------------------------------------------------- 1 | import { IUpdatablePaneView } from '../../views/pane/iupdatable-pane-view'; 2 | 3 | import { IChartModelBase } from '../chart-model'; 4 | import { CustomData, CustomSeriesPricePlotValues, CustomSeriesWhitespaceData } from '../icustom-series'; 5 | import { ISeries } from '../iseries'; 6 | import { SeriesType } from '../series-options'; 7 | 8 | export interface ISeriesCustomPaneView extends IUpdatablePaneView { 9 | priceValueBuilder(plotRow: CustomData | CustomSeriesWhitespaceData): CustomSeriesPricePlotValues; 10 | isWhitespace(data: CustomData | CustomSeriesWhitespaceData): data is CustomSeriesWhitespaceData; 11 | } 12 | export type BuiltInPaneViewFactory = (series: ISeries, model: IChartModelBase) => IUpdatablePaneView; 13 | -------------------------------------------------------------------------------- /src/model/sort-sources.ts: -------------------------------------------------------------------------------- 1 | import { ensureNotNull } from '../helpers/assertions'; 2 | 3 | import { ZOrdered } from './idata-source'; 4 | 5 | export function sortSources(sources: readonly T[]): T[] { 6 | return sources.slice().sort((s1: ZOrdered, s2: ZOrdered) => { 7 | return (ensureNotNull(s1.zorder()) - ensureNotNull(s2.zorder())); 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /src/plugins/image-watermark/options.ts: -------------------------------------------------------------------------------- 1 | export interface ImageWatermarkOptions { 2 | /** 3 | * Maximum width for the image watermark. 4 | * 5 | * @defaultValue undefined 6 | */ 7 | maxWidth?: number; 8 | /** 9 | * Maximum height for the image watermark. 10 | * 11 | * @defaultValue undefined 12 | */ 13 | maxHeight?: number; 14 | /** 15 | * Padding to maintain around the image watermark relative 16 | * to the chart pane edges. 17 | * 18 | * @defaultValue 0 19 | */ 20 | padding: number; 21 | /** 22 | * The alpha (opacity) for the image watermark. Where `1` is fully 23 | * opaque (visible) and `0` is fully transparent. 24 | * 25 | * @defaultValue 1 26 | */ 27 | alpha: number; 28 | } 29 | 30 | export const imageWatermarkOptionsDefaults: ImageWatermarkOptions = { 31 | alpha: 1, 32 | padding: 0, 33 | }; 34 | -------------------------------------------------------------------------------- /src/plugins/series-markers/series-markers-text.ts: -------------------------------------------------------------------------------- 1 | import { Coordinate } from '../../model/coordinate'; 2 | 3 | export function drawText( 4 | ctx: CanvasRenderingContext2D, 5 | text: string, 6 | x: number, 7 | y: number, 8 | horizontalPixelRatio: number, 9 | verticalPixelRatio: number 10 | ): void { 11 | ctx.save(); 12 | ctx.scale(horizontalPixelRatio, verticalPixelRatio); 13 | ctx.fillText(text, x, y); 14 | ctx.restore(); 15 | } 16 | 17 | export function hitTestText( 18 | textX: number, 19 | textY: number, 20 | textWidth: number, 21 | textHeight: number, 22 | x: Coordinate, 23 | y: Coordinate 24 | ): boolean { 25 | const halfHeight = textHeight / 2; 26 | 27 | return x >= textX && x <= textX + textWidth && 28 | y >= textY - halfHeight && y <= textY + halfHeight; 29 | } 30 | -------------------------------------------------------------------------------- /src/renderers/composite-renderer.ts: -------------------------------------------------------------------------------- 1 | import { CanvasRenderingTarget2D } from 'fancy-canvas'; 2 | 3 | import { IPaneRenderer } from './ipane-renderer'; 4 | 5 | export class CompositeRenderer implements IPaneRenderer { 6 | private _renderers: readonly IPaneRenderer[] = []; 7 | 8 | public setRenderers(renderers: readonly IPaneRenderer[]): void { 9 | this._renderers = renderers; 10 | } 11 | 12 | public draw(target: CanvasRenderingTarget2D, isHovered: boolean, hitTestData?: unknown): void { 13 | this._renderers.forEach((r: IPaneRenderer) => { 14 | r.draw(target, isHovered, hitTestData); 15 | }); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/renderers/iaxis-view-renderer.ts: -------------------------------------------------------------------------------- 1 | import { CanvasRenderingTarget2D } from 'fancy-canvas'; 2 | 3 | export interface IAxisRenderer { 4 | draw(target: CanvasRenderingTarget2D, isHovered: boolean, hitTestData?: unknown): void; 5 | drawBackground?(target: CanvasRenderingTarget2D, isHovered: boolean, hitTestData?: unknown): void; 6 | } 7 | -------------------------------------------------------------------------------- /src/renderers/ipane-renderer.ts: -------------------------------------------------------------------------------- 1 | import { CanvasRenderingTarget2D } from 'fancy-canvas'; 2 | 3 | import { HoveredObject } from '../model/chart-model'; 4 | import { Coordinate } from '../model/coordinate'; 5 | 6 | export interface IPaneRenderer { 7 | draw(target: CanvasRenderingTarget2D, isHovered: boolean, hitTestData?: unknown): void; 8 | drawBackground?(target: CanvasRenderingTarget2D, isHovered: boolean, hitTestData?: unknown): void; 9 | hitTest?(x: Coordinate, y: Coordinate): HoveredObject | null; 10 | } 11 | -------------------------------------------------------------------------------- /src/renderers/itime-axis-view-renderer.ts: -------------------------------------------------------------------------------- 1 | import { CanvasRenderingTarget2D } from 'fancy-canvas'; 2 | 3 | import { TextWidthCache } from '../model/text-width-cache'; 4 | 5 | export interface TimeAxisViewRendererOptions { 6 | baselineOffset: number; 7 | borderSize: number; 8 | font: string; 9 | fontSize: number; 10 | paddingBottom: number; 11 | paddingTop: number; 12 | tickLength: number; 13 | paddingHorizontal: number; 14 | widthCache: TextWidthCache; 15 | labelBottomOffset: number; 16 | } 17 | 18 | export interface ITimeAxisViewRenderer { 19 | draw(target: CanvasRenderingTarget2D, rendererOptions: TimeAxisViewRendererOptions): void; 20 | } 21 | -------------------------------------------------------------------------------- /src/renderers/line-renderer.ts: -------------------------------------------------------------------------------- 1 | import { MediaCoordinatesRenderingScope } from 'fancy-canvas'; 2 | 3 | import { LineStrokeColorerStyle } from '../model/series-bar-colorer'; 4 | 5 | import { LineItemBase, PaneRendererLineBase, PaneRendererLineDataBase } from './line-renderer-base'; 6 | 7 | export type LineStrokeItem = LineItemBase & LineStrokeColorerStyle; 8 | export interface PaneRendererLineData extends PaneRendererLineDataBase { 9 | } 10 | 11 | export class PaneRendererLine extends PaneRendererLineBase { 12 | protected override _strokeStyle(renderingScope: MediaCoordinatesRenderingScope, item: LineStrokeItem): CanvasRenderingContext2D['strokeStyle'] { 13 | return item.lineColor; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/standalone.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/naming-convention 2 | import * as LightweightChartsModule from './index'; 3 | 4 | // put all exports from package to window.LightweightCharts object 5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-member-access 6 | (window as any).LightweightCharts = LightweightChartsModule; 7 | -------------------------------------------------------------------------------- /src/tsconfig.composite.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.composite.base.json", 3 | "references": [ 4 | { "path": "./api/tsconfig.composite.json" }, 5 | { "path": "./formatters/tsconfig.composite.json" }, 6 | { "path": "./gui/tsconfig.composite.json" }, 7 | { "path": "./helpers/tsconfig.composite.json" }, 8 | { "path": "./tsconfig.model.json" } 9 | ], 10 | "include": [ 11 | "./plugins/**/*.ts", 12 | "./index.ts", 13 | "./standalone.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/tsconfig.model.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.composite.base.json", 3 | "references": [ 4 | { "path": "./formatters/tsconfig.composite.json" }, 5 | { "path": "./helpers/tsconfig.composite.json" } 6 | ], 7 | "include": [ 8 | "./model/**/*.ts", 9 | "./renderers/**/*.ts", 10 | "./views/**/*.ts" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /src/typings/_build-time-constants/index.d.ts: -------------------------------------------------------------------------------- 1 | // this file contains build-time constants 2 | // which will be replaced (injected) by rollup while bundling 3 | // see rollup.config.js for the reference 4 | 5 | declare namespace NodeJS { 6 | interface ProcessEnv { 7 | // eslint-disable-next-line @typescript-eslint/naming-convention 8 | NODE_ENV: 'development' | 'production'; 9 | 10 | // eslint-disable-next-line @typescript-eslint/naming-convention 11 | BUILD_VERSION: string; 12 | } 13 | 14 | interface Process { 15 | env: ProcessEnv; 16 | } 17 | } 18 | 19 | // eslint-disable-next-line no-var 20 | declare var process: NodeJS.Process; 21 | -------------------------------------------------------------------------------- /src/typings/_global-types/index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This type should be used when you need to save result of the setTimeout/setInterval functions. 3 | * It makes the compilation with non-composite project happy. 4 | */ 5 | type TimerId = ReturnType; 6 | 7 | /** 8 | * The type declares compile-time constants for mouse buttons. 9 | * e.button values for MouseEvents. 10 | * It's NOT e.buttons (with s)! 11 | */ 12 | declare const enum MouseEventButton { 13 | Left = 0, 14 | Middle = 1, 15 | Right = 2, 16 | Fourth = 3, 17 | Fifth = 4, 18 | } 19 | -------------------------------------------------------------------------------- /src/views/pane/iaxis-view.ts: -------------------------------------------------------------------------------- 1 | import { Pane } from '../../model/pane'; 2 | import { IAxisRenderer } from '../../renderers/iaxis-view-renderer'; 3 | 4 | export interface IAxisView { 5 | renderer(pane: Pane): IAxisRenderer | null; 6 | } 7 | -------------------------------------------------------------------------------- /src/views/pane/ipane-view.ts: -------------------------------------------------------------------------------- 1 | import { Pane } from '../../model/pane'; 2 | import { IPaneRenderer } from '../../renderers/ipane-renderer'; 3 | 4 | export interface IPaneView { 5 | renderer(pane: Pane, addAnchors?: boolean): IPaneRenderer | null; 6 | } 7 | -------------------------------------------------------------------------------- /src/views/pane/iupdatable-pane-view.ts: -------------------------------------------------------------------------------- 1 | import { IPaneView } from './ipane-view'; 2 | 3 | export type UpdateType = 'data' | 'other' | 'options'; 4 | 5 | export interface IUpdatablePaneView extends IPaneView { 6 | update(updateType?: UpdateType): void; 7 | } 8 | -------------------------------------------------------------------------------- /src/views/price-axis/iprice-axis-view.ts: -------------------------------------------------------------------------------- 1 | import { PriceScale } from '../../model/price-scale'; 2 | import { 3 | IPriceAxisViewRenderer, 4 | PriceAxisViewRendererOptions, 5 | } from '../../renderers/iprice-axis-view-renderer'; 6 | 7 | export interface IPriceAxisView { 8 | coordinate(): number; 9 | getFixedCoordinate(): number; 10 | height(rendererOptions: PriceAxisViewRendererOptions, useSecondLine?: boolean): number; 11 | isVisible(): boolean; 12 | isAxisLabelVisible(): boolean; 13 | renderer(priceScale: PriceScale): IPriceAxisViewRenderer; 14 | paneRenderer(): IPriceAxisViewRenderer; 15 | setFixedCoordinate(value: number | null): void; 16 | text(): string; 17 | update(): void; 18 | } 19 | -------------------------------------------------------------------------------- /src/views/time-axis/itime-axis-view.ts: -------------------------------------------------------------------------------- 1 | import { TimeAxisViewRenderer } from '../../renderers/time-axis-view-renderer'; 2 | 3 | export interface ITimeAxisView { 4 | renderer(): TimeAxisViewRenderer; 5 | } 6 | -------------------------------------------------------------------------------- /tests/e2e/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rules: { 3 | 'no-console': [ 4 | 'off', 5 | ], 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /tests/e2e/coverage/coverage-config.ts: -------------------------------------------------------------------------------- 1 | export const expectedCoverage = 90; 2 | export const threshold = 1; 3 | -------------------------------------------------------------------------------- /tests/e2e/coverage/helpers/get-coverage-test-cases.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { dirname, join } from 'node:path'; 3 | import { fileURLToPath } from 'node:url'; 4 | 5 | import { getTestCases as getTestCasesImpl, TestCase } from '../../helpers/get-test-cases'; 6 | 7 | const currentFilePath = fileURLToPath(import.meta.url); 8 | const currentDirectory = dirname(currentFilePath); 9 | 10 | const testCasesDir = join(currentDirectory, '..', 'test-cases'); 11 | 12 | export function getTestCases(): Record { 13 | return getTestCasesImpl(testCasesDir); 14 | } 15 | -------------------------------------------------------------------------------- /tests/e2e/coverage/test-cases/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | module.exports = { 4 | env: { 5 | browser: true, 6 | node: false, 7 | }, 8 | rules: { 9 | 'no-unused-vars': ['error', { varsIgnorePattern: '^(beforeInteractions|afterInteractions|interactionsToPerform)$', args: 'none' }], 10 | }, 11 | globals: { 12 | LightweightCharts: false, 13 | generateLineData: false, 14 | generateHistogramData: false, 15 | generateBars: false, 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /tests/e2e/coverage/test-cases/chart/auto-size.js: -------------------------------------------------------------------------------- 1 | function interactionsToPerform() { 2 | return []; 3 | } 4 | 5 | function beforeInteractions(container) { 6 | const chart = LightweightCharts.createChart(container); 7 | 8 | const mainSeries = chart.addSeries(LightweightCharts.AreaSeries); 9 | 10 | mainSeries.setData(generateLineData()); 11 | 12 | chart.applyOptions({ 13 | autoSize: true, 14 | }); 15 | 16 | return new Promise(resolve => { 17 | requestAnimationFrame(() => { 18 | container.style.height = '200px'; 19 | container.style.width = '250px'; 20 | requestAnimationFrame(resolve); 21 | }); 22 | }); 23 | } 24 | 25 | function afterInteractions() { 26 | return Promise.resolve(); 27 | } 28 | -------------------------------------------------------------------------------- /tests/e2e/coverage/test-cases/chart/create-string.js: -------------------------------------------------------------------------------- 1 | function interactionsToPerform() { 2 | return []; 3 | } 4 | 5 | function beforeInteractions() { 6 | LightweightCharts.createChart('container'); 7 | return Promise.resolve(); 8 | } 9 | 10 | function afterInteractions() { 11 | return Promise.resolve(); 12 | } 13 | -------------------------------------------------------------------------------- /tests/e2e/coverage/test-cases/chart/handle-scroll.js: -------------------------------------------------------------------------------- 1 | function simpleData() { 2 | return [ 3 | { time: 1663740000, value: 10 }, 4 | { time: 1663750000, value: 20 }, 5 | { time: 1663760000, value: 30 }, 6 | ]; 7 | } 8 | 9 | function interactionsToPerform() { 10 | return [ 11 | { action: 'scrollUpRight', target: 'pane' }, 12 | { action: 'scrollDownLeft', target: 'pane' }, 13 | ]; 14 | } 15 | 16 | function beforeInteractions(container) { 17 | const chart = LightweightCharts.createChart(container, { 18 | handleScroll: false, 19 | }); 20 | 21 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries); 22 | 23 | mainSeries.setData(simpleData()); 24 | 25 | return Promise.resolve(); 26 | } 27 | 28 | function afterInteractions() { 29 | return Promise.resolve(); 30 | } 31 | -------------------------------------------------------------------------------- /tests/e2e/coverage/test-cases/chart/kinetic-scroll.js: -------------------------------------------------------------------------------- 1 | function simpleData() { 2 | return [ 3 | { time: 1663740000, value: 10 }, 4 | { time: 1663750000, value: 20 }, 5 | { time: 1663760000, value: 30 }, 6 | ]; 7 | } 8 | 9 | function interactionsToPerform() { 10 | return [{ action: 'kineticAnimation', target: 'pane' }]; 11 | } 12 | 13 | function beforeInteractions(container) { 14 | const chart = LightweightCharts.createChart(container, { 15 | kineticScroll: { 16 | mouse: true, 17 | }, 18 | }); 19 | 20 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries); 21 | 22 | mainSeries.setData(simpleData()); 23 | 24 | return Promise.resolve(); 25 | } 26 | 27 | function afterInteractions() { 28 | return Promise.resolve(); 29 | } 30 | -------------------------------------------------------------------------------- /tests/e2e/coverage/test-cases/chart/localization.js: -------------------------------------------------------------------------------- 1 | function interactionsToPerform() { 2 | return []; 3 | } 4 | 5 | function beforeInteractions(container) { 6 | const chart = LightweightCharts.createChart(container, { 7 | localization: { 8 | locale: 'en-GB-u-ca-islamic', 9 | }, 10 | }); 11 | 12 | const mainSeries = chart.addSeries(LightweightCharts.AreaSeries); 13 | 14 | mainSeries.setData(generateLineData()); 15 | 16 | chart.options(); 17 | 18 | chart.applyOptions({ 19 | localization: { 20 | dateFormat: 'yyyy MM dd', 21 | priceFormatter: p => `£${p.toFixed(2)}`, 22 | timeFormatter: t => `${t.toString()}`, 23 | }, 24 | }); 25 | 26 | return new Promise(resolve => { 27 | requestAnimationFrame(resolve); 28 | }); 29 | } 30 | 31 | function afterInteractions() { 32 | return Promise.resolve(); 33 | } 34 | -------------------------------------------------------------------------------- /tests/e2e/coverage/test-cases/chart/options.js: -------------------------------------------------------------------------------- 1 | function interactionsToPerform() { 2 | return []; 3 | } 4 | 5 | function beforeInteractions(container) { 6 | const chart = LightweightCharts.createChart(container); 7 | 8 | const mainSeries = chart.addSeries(LightweightCharts.AreaSeries); 9 | 10 | mainSeries.setData(generateLineData()); 11 | 12 | chart.options(); 13 | 14 | chart.applyOptions({ 15 | localization: { 16 | dateFormat: 'yyyy MM dd', 17 | }, 18 | }); 19 | 20 | return new Promise(resolve => { 21 | requestAnimationFrame(resolve); 22 | }); 23 | } 24 | 25 | function afterInteractions() { 26 | return Promise.resolve(); 27 | } 28 | -------------------------------------------------------------------------------- /tests/e2e/coverage/test-cases/chart/remove.js: -------------------------------------------------------------------------------- 1 | function interactionsToPerform() { 2 | return []; 3 | } 4 | 5 | let chart; 6 | 7 | function beforeInteractions(container) { 8 | chart = LightweightCharts.createChart(container); 9 | return Promise.resolve(); 10 | } 11 | 12 | function afterInteractions() { 13 | chart.remove(); 14 | chart = null; 15 | return Promise.resolve(); 16 | } 17 | -------------------------------------------------------------------------------- /tests/e2e/coverage/test-cases/chart/screenshot.js: -------------------------------------------------------------------------------- 1 | function interactionsToPerform() { 2 | return []; 3 | } 4 | 5 | let chart; 6 | 7 | function beforeInteractions(container) { 8 | chart = LightweightCharts.createChart(container); 9 | 10 | const mainSeries = chart.addSeries(LightweightCharts.HistogramSeries); 11 | 12 | mainSeries.setData(generateHistogramData()); 13 | 14 | return new Promise(resolve => { 15 | requestAnimationFrame(resolve); 16 | }); 17 | } 18 | 19 | function afterInteractions() { 20 | chart.takeScreenshot(); 21 | return Promise.resolve(); 22 | } 23 | -------------------------------------------------------------------------------- /tests/e2e/coverage/test-cases/exports.js: -------------------------------------------------------------------------------- 1 | function interactionsToPerform() { 2 | return []; 3 | } 4 | 5 | function beforeInteractions() { 6 | console.log(LightweightCharts.TrackingModeExitMode); 7 | console.log(LightweightCharts.MismatchDirection); 8 | console.log(LightweightCharts.PriceLineSource); 9 | console.log(LightweightCharts.TickMarkType); 10 | console.log(LightweightCharts.version()); 11 | return Promise.resolve(); 12 | } 13 | 14 | function afterInteractions() { 15 | return Promise.resolve(); 16 | } 17 | -------------------------------------------------------------------------------- /tests/e2e/coverage/test-cases/panes/add-pane.js: -------------------------------------------------------------------------------- 1 | function simpleData() { 2 | return [ 3 | { time: 1663740000, value: 10 }, 4 | { time: 1663750000, value: 20 }, 5 | { time: 1663760000, value: 30 }, 6 | ]; 7 | } 8 | 9 | function interactionsToPerform() { 10 | return []; 11 | } 12 | 13 | let chart; 14 | function beforeInteractions(container) { 15 | chart = LightweightCharts.createChart(container); 16 | 17 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries); 18 | const secondSeries = chart.addSeries(LightweightCharts.LineSeries, {}, 1); 19 | 20 | mainSeries.setData(simpleData()); 21 | secondSeries.setData(simpleData()); 22 | 23 | return Promise.resolve(); 24 | } 25 | 26 | function afterInteractions() { 27 | return Promise.resolve(); 28 | } 29 | -------------------------------------------------------------------------------- /tests/e2e/coverage/test-cases/panes/pane-size.js: -------------------------------------------------------------------------------- 1 | function simpleData() { 2 | return [ 3 | { time: 1663740000, value: 10 }, 4 | { time: 1663750000, value: 20 }, 5 | { time: 1663760000, value: 30 }, 6 | ]; 7 | } 8 | 9 | function interactionsToPerform() { 10 | return []; 11 | } 12 | 13 | let chart; 14 | function beforeInteractions(container) { 15 | chart = LightweightCharts.createChart(container); 16 | 17 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries); 18 | const secondSeries = chart.addSeries(LightweightCharts.LineSeries, {}, 1); 19 | 20 | mainSeries.setData(simpleData()); 21 | secondSeries.setData(simpleData()); 22 | 23 | return Promise.resolve(); 24 | } 25 | 26 | function afterInteractions() { 27 | chart.paneSize(1); 28 | return Promise.resolve(); 29 | } 30 | -------------------------------------------------------------------------------- /tests/e2e/coverage/test-cases/panes/panes-sceenshot.js: -------------------------------------------------------------------------------- 1 | function simpleData() { 2 | return [ 3 | { time: 1663740000, value: 10 }, 4 | { time: 1663750000, value: 20 }, 5 | { time: 1663760000, value: 30 }, 6 | ]; 7 | } 8 | 9 | function interactionsToPerform() { 10 | return []; 11 | } 12 | 13 | let chart; 14 | function beforeInteractions(container) { 15 | chart = LightweightCharts.createChart(container); 16 | 17 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries); 18 | const secondSeries = chart.addSeries(LightweightCharts.LineSeries, {}, 1); 19 | 20 | mainSeries.setData(simpleData()); 21 | secondSeries.setData(simpleData()); 22 | 23 | return Promise.resolve(); 24 | } 25 | 26 | function afterInteractions() { 27 | chart.takeScreenshot(); 28 | return Promise.resolve(); 29 | } 30 | -------------------------------------------------------------------------------- /tests/e2e/coverage/test-cases/panes/remove-pane.js: -------------------------------------------------------------------------------- 1 | function simpleData() { 2 | return [ 3 | { time: 1663740000, value: 10 }, 4 | { time: 1663750000, value: 20 }, 5 | { time: 1663760000, value: 30 }, 6 | ]; 7 | } 8 | 9 | function interactionsToPerform() { 10 | return []; 11 | } 12 | 13 | let chart; 14 | function beforeInteractions(container) { 15 | chart = LightweightCharts.createChart(container); 16 | 17 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries); 18 | const secondSeries = chart.addSeries(LightweightCharts.LineSeries, {}, 1); 19 | 20 | mainSeries.setData(simpleData()); 21 | secondSeries.setData(simpleData()); 22 | 23 | return Promise.resolve(); 24 | } 25 | 26 | function afterInteractions() { 27 | chart.removePane(1); 28 | return Promise.resolve(); 29 | } 30 | -------------------------------------------------------------------------------- /tests/e2e/coverage/test-cases/panes/swap-panes.js: -------------------------------------------------------------------------------- 1 | function simpleData() { 2 | return [ 3 | { time: 1663740000, value: 10 }, 4 | { time: 1663750000, value: 20 }, 5 | { time: 1663760000, value: 30 }, 6 | ]; 7 | } 8 | 9 | function interactionsToPerform() { 10 | return []; 11 | } 12 | 13 | let chart; 14 | function beforeInteractions(container) { 15 | chart = LightweightCharts.createChart(container); 16 | 17 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries); 18 | const secondSeries = chart.addSeries(LightweightCharts.LineSeries, {}, 1); 19 | 20 | mainSeries.setData(simpleData()); 21 | secondSeries.setData(simpleData()); 22 | 23 | return Promise.resolve(); 24 | } 25 | 26 | function afterInteractions() { 27 | chart.swapPanes(1, 1); 28 | chart.swapPanes(0, 1); 29 | return Promise.resolve(); 30 | } 31 | -------------------------------------------------------------------------------- /tests/e2e/coverage/test-cases/series/area-inverted.js: -------------------------------------------------------------------------------- 1 | function interactionsToPerform() { 2 | return []; 3 | } 4 | 5 | async function awaitNewFrame() { 6 | return new Promise(resolve => { 7 | requestAnimationFrame(resolve); 8 | }); 9 | } 10 | 11 | async function beforeInteractions(container) { 12 | const chart = LightweightCharts.createChart(container); 13 | 14 | const mainSeries = chart.addSeries(LightweightCharts.AreaSeries, { 15 | invertFilledArea: true, 16 | }); 17 | 18 | mainSeries.setData(generateLineData()); 19 | 20 | await awaitNewFrame(); 21 | 22 | mainSeries.applyOptions({ 23 | invertFilledArea: false, 24 | }); 25 | 26 | return Promise.resolve(); 27 | } 28 | 29 | function afterInteractions() { 30 | return Promise.resolve(); 31 | } 32 | -------------------------------------------------------------------------------- /tests/e2e/coverage/test-cases/series/remove-series.js: -------------------------------------------------------------------------------- 1 | function interactionsToPerform() { 2 | return []; 3 | } 4 | 5 | let series; 6 | let chart; 7 | 8 | function beforeInteractions(container) { 9 | chart = LightweightCharts.createChart(container); 10 | 11 | series = chart.addSeries(LightweightCharts.AreaSeries); 12 | 13 | series.setData(generateLineData()); 14 | 15 | return Promise.resolve(); 16 | } 17 | 18 | function afterInteractions() { 19 | chart.removeSeries(series); 20 | return Promise.resolve(); 21 | } 22 | -------------------------------------------------------------------------------- /tests/e2e/coverage/test-cases/series/title.js: -------------------------------------------------------------------------------- 1 | function interactionsToPerform() { 2 | return []; 3 | } 4 | 5 | let mainSeries; 6 | 7 | function beforeInteractions(container) { 8 | const chart = LightweightCharts.createChart(container); 9 | 10 | mainSeries = chart.addSeries(LightweightCharts.BarSeries, { 11 | title: 'Initial title', 12 | priceFormat: { 13 | type: 'percent', 14 | }, 15 | }); 16 | 17 | mainSeries.setData(generateBars()); 18 | return new Promise(resolve => { 19 | requestAnimationFrame(resolve); 20 | }); 21 | } 22 | 23 | function afterInteractions() { 24 | mainSeries.applyOptions({ 25 | title: 'Updated title', 26 | }); 27 | return new Promise(resolve => { 28 | requestAnimationFrame(resolve); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /tests/e2e/coverage/test-cases/time-scale/hidden.js: -------------------------------------------------------------------------------- 1 | function interactionsToPerform() { 2 | return []; 3 | } 4 | 5 | let chart; 6 | 7 | function beforeInteractions(container) { 8 | chart = LightweightCharts.createChart(container, { 9 | timeScale: { 10 | visible: false, 11 | }, 12 | }); 13 | 14 | const mainSeries = chart.addSeries(LightweightCharts.CandlestickSeries); 15 | 16 | mainSeries.setData(generateBars()); 17 | 18 | return new Promise(resolve => { 19 | requestAnimationFrame(resolve); 20 | }); 21 | } 22 | 23 | function afterInteractions() { 24 | chart.applyOptions({ 25 | timeScale: { 26 | visible: true, 27 | }, 28 | }); 29 | return new Promise(resolve => { 30 | requestAnimationFrame(resolve); 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /tests/e2e/graphics/helpers/compare-screenshots.ts: -------------------------------------------------------------------------------- 1 | import pixelmatch from 'pixelmatch'; 2 | import { PNG } from 'pngjs'; 3 | 4 | export interface CompareResult { 5 | diffPixelsCount: number; 6 | diffImg: PNG; 7 | } 8 | 9 | export function compareScreenshots(leftImg: PNG, rightImg: PNG): CompareResult { 10 | if (leftImg.width !== rightImg.width) { 11 | throw new Error('image widths should be the same'); 12 | } 13 | 14 | if (leftImg.height !== rightImg.height) { 15 | throw new Error('image widths should be the same'); 16 | } 17 | 18 | const diffImg = new PNG({ 19 | width: leftImg.width, 20 | height: rightImg.height, 21 | }); 22 | 23 | const diffPixelsCount = pixelmatch( 24 | leftImg.data, rightImg.data, 25 | diffImg.data, 26 | leftImg.width, leftImg.height, 27 | { threshold: 0.1 } 28 | ); 29 | 30 | return { diffPixelsCount, diffImg }; 31 | } 32 | -------------------------------------------------------------------------------- /tests/e2e/graphics/helpers/testcase-window-type.ts: -------------------------------------------------------------------------------- 1 | import { IChartApi } from '../../../../src/api/create-chart'; 2 | 3 | export interface TestCaseWindow extends Window { 4 | testCaseReady: void | Promise; 5 | chart?: IChartApi; 6 | ignoreMouseMove?: boolean; 7 | checkChartScreenshot?: boolean; 8 | } 9 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | module.exports = { 4 | env: { 5 | browser: true, 6 | node: false, 7 | }, 8 | rules: { 9 | 'no-unused-vars': ['error', { varsIgnorePattern: '^(runTestCase|beforeInteractions|after(Initial|Final)Interactions|(initial|final)InteractionsToPerform)$', args: 'none' }], 10 | }, 11 | globals: { 12 | LightweightCharts: false, 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/add-series-after-time.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 1000; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { 17 | height: 500, width: 600, 18 | layout: { attributionLogo: false }, 19 | }); 20 | 21 | return new Promise(resolve => { 22 | setTimeout(() => { 23 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries); 24 | mainSeries.setData(generateData()); 25 | 26 | resolve(); 27 | }, 1000); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/api/getting-series-price-scale.js: -------------------------------------------------------------------------------- 1 | function runTestCase(container) { 2 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 3 | const series = chart.addSeries(LightweightCharts.AreaSeries); 4 | series.setData([ 5 | { time: '1990-04-24', value: 0 }, 6 | { time: '1990-04-25', value: 1 }, 7 | { time: '1990-04-26', value: 2 }, 8 | { time: '1990-04-28', value: 3 }, 9 | ]); 10 | 11 | console.assert(series.priceScale(), 'should be able to return price scale'); 12 | } 13 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/applying-options/fix-left-edge.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 20; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 17 | 18 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries); 19 | mainSeries.setData(generateData()); 20 | 21 | chart.timeScale().applyOptions({ fixLeftEdge: true }); 22 | 23 | return new Promise(resolve => { 24 | setTimeout(() => resolve(), 1000); 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/applying-options/series-title.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | 13 | return res; 14 | } 15 | 16 | function runTestCase(container) { 17 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 18 | 19 | const firstSeries = chart.addSeries(LightweightCharts.LineSeries, { 20 | title: 'Initial title', 21 | }); 22 | 23 | firstSeries.applyOptions({ 24 | title: 'Applied title', 25 | }); 26 | 27 | firstSeries.setData(generateData()); 28 | } 29 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/attribution-logo-dark.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = (window.chart = LightweightCharts.createChart(container, { 17 | layout: { 18 | attributionLogo: true, 19 | background: { 20 | type: 'solid', 21 | color: 'rgb(50,100,150)', 22 | }, 23 | textColor: 'rgb(255,200,100)', 24 | }, 25 | })); 26 | 27 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries); 28 | 29 | mainSeries.setData(generateData()); 30 | } 31 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/fit-content-with-few-data.js: -------------------------------------------------------------------------------- 1 | function generateData(valueOffset, count) { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < count; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i + valueOffset, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | 13 | return res; 14 | } 15 | 16 | function runTestCase(container) { 17 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 18 | 19 | const series = chart.addSeries(LightweightCharts.LineSeries); 20 | 21 | series.setData(generateData(0, 10)); 22 | 23 | chart.timeScale().fitContent(); 24 | } 25 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/initial-options/crosshair-label-color-black.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { 17 | crosshair: { 18 | vertLine: { 19 | labelBackgroundColor: '#000000', 20 | }, 21 | horzLine: { 22 | labelBackgroundColor: '#000000', 23 | }, 24 | }, 25 | layout: { attributionLogo: false }, 26 | }); 27 | 28 | const mainSeries = chart.addSeries(LightweightCharts.AreaSeries); 29 | 30 | mainSeries.setData(generateData()); 31 | } 32 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/initial-options/crosshair-label-color-greys.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { 17 | crosshair: { 18 | vertLine: { 19 | labelBackgroundColor: '#222222', 20 | }, 21 | horzLine: { 22 | labelBackgroundColor: '#DDDDDD', 23 | }, 24 | }, 25 | layout: { attributionLogo: false }, 26 | }); 27 | 28 | const mainSeries = chart.addSeries(LightweightCharts.AreaSeries); 29 | 30 | mainSeries.setData(generateData()); 31 | } 32 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/initial-options/crosshair-label-color-named.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { 17 | crosshair: { 18 | vertLine: { 19 | labelBackgroundColor: 'orangered', 20 | }, 21 | horzLine: { 22 | labelBackgroundColor: 'hotpink', 23 | }, 24 | }, 25 | layout: { attributionLogo: false }, 26 | }); 27 | 28 | const mainSeries = chart.addSeries(LightweightCharts.AreaSeries); 29 | 30 | mainSeries.setData(generateData()); 31 | } 32 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/initial-options/crosshair-label-color-white.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { 17 | crosshair: { 18 | vertLine: { 19 | labelBackgroundColor: '#ffffff', 20 | }, 21 | horzLine: { 22 | labelBackgroundColor: '#ffffff', 23 | }, 24 | }, 25 | layout: { attributionLogo: false }, 26 | }); 27 | 28 | const mainSeries = chart.addSeries(LightweightCharts.AreaSeries); 29 | 30 | mainSeries.setData(generateData()); 31 | } 32 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/initial-options/custom-date-format.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { 17 | localization: { 18 | dateFormat: 'Year: yy (MM-yyyy)', 19 | }, 20 | layout: { attributionLogo: false }, 21 | }); 22 | 23 | const mainSeries = chart.addSeries(LightweightCharts.AreaSeries); 24 | 25 | mainSeries.setData(generateData()); 26 | } 27 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/initial-options/date-format.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { 17 | localization: { 18 | dateFormat: 'MMM dd, yyyy', 19 | }, 20 | layout: { attributionLogo: false }, 21 | }); 22 | 23 | const mainSeries = chart.addSeries(LightweightCharts.AreaSeries); 24 | 25 | mainSeries.setData(generateData()); 26 | } 27 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/initial-options/draw-price-ticks.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { 17 | rightPriceScale: { 18 | ticksVisible: true, 19 | }, 20 | layout: { attributionLogo: false }, 21 | }); 22 | 23 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries); 24 | 25 | mainSeries.setData(generateData()); 26 | } 27 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/initial-options/draw-time-ticks.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { 17 | timeScale: { 18 | ticksVisible: true, 19 | }, 20 | layout: { attributionLogo: false }, 21 | }); 22 | 23 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries); 24 | 25 | mainSeries.setData(generateData()); 26 | } 27 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/initial-options/fix-left-edge.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 20; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { 17 | timeScale: { 18 | fixLeftEdge: true, 19 | }, 20 | layout: { attributionLogo: false }, 21 | }); 22 | 23 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries); 24 | mainSeries.setData(generateData()); 25 | } 26 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/initial-options/left-price-scale.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | 13 | return res; 14 | } 15 | 16 | function runTestCase(container) { 17 | const chart = window.chart = LightweightCharts.createChart(container, { 18 | rightPriceScale: { visible: false }, 19 | leftPriceScale: { visible: true }, 20 | layout: { attributionLogo: false }, 21 | }); 22 | 23 | const firstSeries = chart.addSeries(LightweightCharts.LineSeries, { 24 | title: 'AAPL', 25 | }); 26 | 27 | firstSeries.setData(generateData()); 28 | } 29 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/initial-options/no-autoscale.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { 17 | rightPriceScale: { 18 | autoScale: false, 19 | }, 20 | layout: { attributionLogo: false }, 21 | }); 22 | 23 | const mainSeries = chart.addSeries(LightweightCharts.AreaSeries); 24 | 25 | mainSeries.setData(generateData()); 26 | } 27 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/initial-options/no-base-line.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 1; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | 13 | return res; 14 | } 15 | 16 | function runTestCase(container) { 17 | const chart = window.chart = LightweightCharts.createChart(container, { 18 | rightPriceScale: { 19 | mode: LightweightCharts.PriceScaleMode.Percentage, 20 | }, 21 | layout: { attributionLogo: false }, 22 | }); 23 | 24 | const firstSeries = chart.addSeries(LightweightCharts.LineSeries, { 25 | baseLineVisible: false, 26 | }); 27 | 28 | firstSeries.setData(generateData()); 29 | } 30 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/initial-options/no-price-line.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 1; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | 13 | return res; 14 | } 15 | 16 | function runTestCase(container) { 17 | const chart = window.chart = LightweightCharts.createChart(container, { 18 | rightPriceScale: { 19 | mode: LightweightCharts.PriceScaleMode.Percentage, 20 | }, 21 | layout: { attributionLogo: false }, 22 | }); 23 | 24 | const firstSeries = chart.addSeries(LightweightCharts.LineSeries, { 25 | priceLineVisible: false, 26 | }); 27 | 28 | firstSeries.setData(generateData()); 29 | } 30 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/initial-options/no-price-scale.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | 13 | return res; 14 | } 15 | 16 | function runTestCase(container) { 17 | const chart = window.chart = LightweightCharts.createChart(container, { 18 | rightPriceScale: { visible: false }, 19 | leftPriceScale: { visible: false }, 20 | layout: { attributionLogo: false }, 21 | }); 22 | 23 | const firstSeries = chart.addSeries(LightweightCharts.LineSeries); 24 | 25 | firstSeries.setData(generateData()); 26 | } 27 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/initial-options/price-format.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 17 | 18 | // see issue#55 19 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries, { 20 | priceFormat: { 21 | minMove: 0.00001, 22 | precision: 5, 23 | }, 24 | }); 25 | 26 | mainSeries.setData(generateData()); 27 | } 28 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/initial-options/price-line-source-default.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 17 | 18 | const series = chart.addSeries(LightweightCharts.LineSeries); 19 | 20 | series.setData(generateData()); 21 | 22 | chart.timeScale().scrollToPosition(-10); 23 | } 24 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/initial-options/price-line-source-last-visible.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 17 | 18 | const series = chart.addSeries(LightweightCharts.LineSeries, { 19 | priceLineSource: LightweightCharts.PriceLineSource.LastVisible, 20 | }); 21 | 22 | series.setData(generateData()); 23 | 24 | chart.timeScale().scrollToPosition(-10); 25 | } 26 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/initial-options/zero-precision.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 17 | 18 | // see issue#59 19 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries, { 20 | priceFormat: { 21 | minMove: 1, 22 | precision: 0, 23 | }, 24 | }); 25 | 26 | mainSeries.setData(generateData()); 27 | } 28 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/months-chart.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 12; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCMonth(time.getUTCMonth() + 1); 11 | } 12 | 13 | return res; 14 | } 15 | 16 | function runTestCase(container) { 17 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 18 | 19 | const firstSeries = chart.addSeries(LightweightCharts.LineSeries); 20 | firstSeries.setData(generateData()); 21 | chart.timeScale().fitContent(); 22 | } 23 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/options-chart.js: -------------------------------------------------------------------------------- 1 | function runTestCase(container) { 2 | const chart = (window.chart = LightweightCharts.createOptionsChart( 3 | container, 4 | { layout: { attributionLogo: false }, localization: { precision: 1 } } 5 | )); 6 | 7 | const lineSeries = chart.addSeries(LightweightCharts.LineSeries, { color: 'blue' }); 8 | 9 | const data = []; 10 | for (let i = 0; i < 1000; i++) { 11 | data.push({ 12 | time: i * 0.25, 13 | value: Math.sin(i / 100) + i / 500, 14 | }); 15 | } 16 | 17 | lineSeries.setData(data); 18 | 19 | chart.timeScale().fitContent(); 20 | } 21 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/price-scale/edge-marks-logarithmic.js: -------------------------------------------------------------------------------- 1 | function runTestCase(container) { 2 | const chartOptions = { 3 | rightPriceScale: { 4 | mode: LightweightCharts.PriceScaleMode.Logarithmic, 5 | scaleMargins: { 6 | top: 0, 7 | bottom: 0, 8 | }, 9 | ensureEdgeTickMarksVisible: true, 10 | }, 11 | }; 12 | 13 | const chart = (window.chart = LightweightCharts.createChart(container, chartOptions)); 14 | const series = chart.addSeries(LightweightCharts.CandlestickSeries); 15 | 16 | series.setData([ 17 | { time: '2018-12-22', open: 75.16, high: 82.84, low: 36.16, close: 45.72 }, 18 | { time: '2018-12-29', open: 131.33, high: 151.17, low: 77.68, close: 96.43 }, 19 | ]); 20 | 21 | chart.timeScale().fitContent(); 22 | } 23 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/price-scale/edge-marks.js: -------------------------------------------------------------------------------- 1 | function runTestCase(container) { 2 | const chartOptions = { 3 | rightPriceScale: { 4 | mode: LightweightCharts.PriceScaleMode.Normal, 5 | scaleMargins: { 6 | top: 0, 7 | bottom: 0, 8 | }, 9 | ensureEdgeTickMarksVisible: true, 10 | }, 11 | }; 12 | 13 | const chart = (window.chart = LightweightCharts.createChart(container, chartOptions)); 14 | const series = chart.addSeries(LightweightCharts.CandlestickSeries); 15 | 16 | series.setData([ 17 | { time: '2018-12-22', open: 75.16, high: 82.84, low: 36.16, close: 45.72 }, 18 | { time: '2018-12-29', open: 131.33, high: 151.17, low: 77.68, close: 96.43 }, 19 | ]); 20 | 21 | chart.timeScale().fitContent(); 22 | } 23 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/price-scale/logarithmic-scale-on-small-values.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const result = []; 3 | for (let i = 0; i < 100; ++i) { 4 | result.push({ 5 | time: i, 6 | value: 0.000000001 * i, 7 | }); 8 | } 9 | 10 | return result; 11 | } 12 | 13 | function runTestCase(container) { 14 | const chart = window.chart = LightweightCharts.createChart(container, { 15 | rightPriceScale: { 16 | mode: LightweightCharts.PriceScaleMode.Logarithmic, 17 | }, 18 | layout: { attributionLogo: false }, 19 | }); 20 | 21 | const mainSeries = chart.addSeries(LightweightCharts.AreaSeries, { 22 | priceFormat: { 23 | minMove: 1e-9, 24 | precision: 9, 25 | }, 26 | }); 27 | 28 | mainSeries.setData(generateData()); 29 | 30 | chart.timeScale().fitContent(); 31 | } 32 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/price-scale/set-visible-range.js: -------------------------------------------------------------------------------- 1 | function runTestCase(container) { 2 | const chart = (window.chart = LightweightCharts.createChart(container)); 3 | const series = chart.addSeries(LightweightCharts.CandlestickSeries); 4 | 5 | series.setData([ 6 | { time: '2018-12-22', open: 75.16, high: 82.84, low: 36.16, close: 45.72 }, 7 | { time: '2018-12-29', open: 131.33, high: 151.17, low: 77.68, close: 96.43 }, 8 | ]); 9 | 10 | chart.timeScale().fitContent(); 11 | chart.priceScale('right').setVisibleRange({ from: 10, to: 100 }); 12 | } 13 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/price-scale/toggle-auto-scale.js: -------------------------------------------------------------------------------- 1 | function runTestCase(container) { 2 | const chart = (window.chart = LightweightCharts.createChart(container)); 3 | const series = chart.addSeries(LightweightCharts.CandlestickSeries); 4 | 5 | series.setData([ 6 | { time: '2018-12-22', open: 75.16, high: 82.84, low: 36.16, close: 45.72 }, 7 | { time: '2018-12-29', open: 131.33, high: 151.17, low: 77.68, close: 96.43 }, 8 | ]); 9 | 10 | chart.timeScale().fitContent(); 11 | chart.priceScale('right').setVisibleRange({ from: 10, to: 100 }); 12 | chart.priceScale('right').setAutoScale(true); 13 | } 14 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/rtl-page.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 100; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | window.document.dir = 'rtl'; 17 | 18 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 19 | 20 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries); 21 | 22 | mainSeries.setData(generateData()); 23 | } 24 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/add-series-after-volume.js: -------------------------------------------------------------------------------- 1 | // fix the first case from 2 | // https://github.com/tradingview/lightweight-charts/issues/110 3 | 4 | function runTestCase(container) { 5 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 6 | 7 | const areaSeries = chart.addSeries(LightweightCharts.AreaSeries); // or any other series type 8 | const volumeSeries = chart.addSeries(LightweightCharts.HistogramSeries); 9 | 10 | volumeSeries.setData([ 11 | { time: '2019-05-24', value: 23714686.00, color: 'red' }, 12 | ]); 13 | 14 | areaSeries.setData([ 15 | { time: '2019-05-24', value: 179.66 }, 16 | ]); 17 | } 18 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/area-inverted.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 17 | 18 | const mainSeries = chart.addSeries(LightweightCharts.AreaSeries, { 19 | invertFilledArea: true, 20 | }); 21 | 22 | mainSeries.setData(generateData()); 23 | } 24 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/area-relative-gradient-inverted.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 17 | 18 | const mainSeries = chart.addSeries(LightweightCharts.AreaSeries, { 19 | relativeGradient: true, 20 | invertFilledArea: true, 21 | }); 22 | 23 | mainSeries.setData(generateData()); 24 | } 25 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/area-relative-gradient.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 17 | 18 | const mainSeries = chart.addSeries(LightweightCharts.AreaSeries, { 19 | relativeGradient: true, 20 | }); 21 | 22 | mainSeries.setData(generateData()); 23 | } 24 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/area-with-point-markers.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = (window.chart = LightweightCharts.createChart(container, { 17 | timeScale: { 18 | barSpacing: 12, 19 | }, 20 | layout: { attributionLogo: false }, 21 | })); 22 | 23 | const mainSeries = chart.addSeries(LightweightCharts.AreaSeries, { pointMarkersVisible: true }); 24 | 25 | mainSeries.setData(generateData()); 26 | } 27 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/area-with-whitespaces.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | const item = { 6 | time: time.getTime() / 1000, 7 | }; 8 | 9 | if (i % 3 !== 0) { 10 | item.value = i; 11 | } 12 | 13 | res.push(item); 14 | time.setUTCDate(time.getUTCDate() + 1); 15 | } 16 | return res; 17 | } 18 | 19 | function runTestCase(container) { 20 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 21 | 22 | const mainSeries = chart.addSeries(LightweightCharts.AreaSeries); 23 | 24 | mainSeries.setData(generateData()); 25 | } 26 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/area.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 17 | 18 | const mainSeries = chart.addSeries(LightweightCharts.AreaSeries); 19 | 20 | mainSeries.setData(generateData()); 21 | } 22 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/curved-line-2-points.js: -------------------------------------------------------------------------------- 1 | function runTestCase(container) { 2 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 3 | 4 | const lineSeries = chart.addSeries(LightweightCharts.LineSeries, { 5 | lineType: LightweightCharts.LineType.Curved, 6 | }); 7 | 8 | const data = [ 9 | { time: new Date(2000, 0, 1).getTime() / 1000, value: 5 }, 10 | { time: new Date(2000, 1, 1).getTime() / 1000, value: 15 }, 11 | ]; 12 | 13 | lineSeries.setData(data); 14 | 15 | chart.timeScale().fitContent(); 16 | } 17 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/curved-line-3-points.js: -------------------------------------------------------------------------------- 1 | function runTestCase(container) { 2 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 3 | 4 | const lineSeries = chart.addSeries(LightweightCharts.LineSeries, { 5 | lineType: LightweightCharts.LineType.Curved, 6 | }); 7 | 8 | const data = [ 9 | { time: new Date(2000, 0, 1).getTime() / 1000, value: 5 }, 10 | { time: new Date(2000, 1, 1).getTime() / 1000, value: 15 }, 11 | { time: new Date(2000, 2, 1).getTime() / 1000, value: 7.5 }, 12 | ]; 13 | 14 | lineSeries.setData(data); 15 | 16 | chart.timeScale().fitContent(); 17 | } 18 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/curved-line-area.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2000, 0, 1, 0, 0, 0, 0)); 4 | 5 | for (let i = 0; i < 25; ++i) { 6 | const item = { 7 | time: time.getTime() / 1000, 8 | value: Math.sin(i), 9 | }; 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | 12 | res.push(item); 13 | } 14 | return res; 15 | } 16 | 17 | function runTestCase(container) { 18 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 19 | 20 | const areaSeries = chart.addSeries(LightweightCharts.AreaSeries, { 21 | lineType: LightweightCharts.LineType.Curved, 22 | }); 23 | 24 | const data = generateData(); 25 | areaSeries.setData(data); 26 | 27 | chart.timeScale().fitContent(); 28 | } 29 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/curved-line-baseline.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2000, 0, 1, 0, 0, 0, 0)); 4 | 5 | for (let i = 0; i < 25; ++i) { 6 | const item = { 7 | time: time.getTime() / 1000, 8 | value: Math.sin(i), 9 | }; 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | 12 | res.push(item); 13 | } 14 | return res; 15 | } 16 | 17 | function runTestCase(container) { 18 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 19 | 20 | const baselineSeries = chart.addSeries(LightweightCharts.BaselineSeries, { 21 | lineType: LightweightCharts.LineType.Curved, 22 | }); 23 | 24 | const data = generateData(); 25 | baselineSeries.setData(data); 26 | 27 | chart.timeScale().fitContent(); 28 | } 29 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/hidden-series-and-autoscale.js: -------------------------------------------------------------------------------- 1 | function generateData(mul = 1) { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i * mul, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | 13 | return res; 14 | } 15 | 16 | function runTestCase(container) { 17 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 18 | 19 | const lineSeries = chart.addSeries(LightweightCharts.LineSeries, { visible: false }); 20 | lineSeries.setData(generateData()); 21 | 22 | const alwaysVisibleSeries = chart.addSeries(LightweightCharts.LineSeries); 23 | alwaysVisibleSeries.setData(generateData(1.5)); 24 | } 25 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/histogram-change-default-color.js: -------------------------------------------------------------------------------- 1 | function runTestCase(container) { 2 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 3 | 4 | const series = chart.addSeries(LightweightCharts.HistogramSeries, { 5 | color: 'blue', 6 | }); 7 | 8 | series.setData([ 9 | { time: '2019-05-22', value: 35 }, 10 | { time: '2019-05-23', value: 10, color: 'red' }, 11 | { time: '2019-05-24', value: 20, color: 'green' }, 12 | { time: '2019-05-28', value: 30 }, 13 | ]); 14 | 15 | series.applyOptions({ 16 | color: 'orange', 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/histogram-update-without-set-data.js: -------------------------------------------------------------------------------- 1 | // fix the second case from 2 | // https://github.com/tradingview/lightweight-charts/issues/110 3 | 4 | function runTestCase(container) { 5 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 6 | 7 | const areaSeries = chart.addSeries(LightweightCharts.AreaSeries); 8 | const volumeSeries = chart.addSeries(LightweightCharts.HistogramSeries); 9 | 10 | volumeSeries.update( 11 | { time: '2019-05-24', value: 23714686.00 } 12 | ); 13 | 14 | areaSeries.setData([ 15 | { time: '2019-05-24', value: 179.66 }, 16 | ]); 17 | } 18 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/histogram-with-whitespaces.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | const item = { 6 | time: time.getTime() / 1000, 7 | }; 8 | 9 | if (i % 3 !== 0) { 10 | item.value = i; 11 | } 12 | 13 | res.push(item); 14 | time.setUTCDate(time.getUTCDate() + 1); 15 | } 16 | return res; 17 | } 18 | 19 | function runTestCase(container) { 20 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 21 | 22 | const mainSeries = chart.addSeries(LightweightCharts.HistogramSeries); 23 | 24 | mainSeries.setData(generateData()); 25 | } 26 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/histogram.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const colors = [ 3 | '#013370', 4 | '#3a9656', 5 | undefined, // default color should be used 6 | ]; 7 | 8 | const res = []; 9 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 10 | for (let i = 0; i < 500; ++i) { 11 | res.push({ 12 | time: time.getTime() / 1000, 13 | value: i, 14 | color: colors[i % colors.length], 15 | }); 16 | 17 | time.setUTCDate(time.getUTCDate() + 1); 18 | } 19 | return res; 20 | } 21 | 22 | function runTestCase(container) { 23 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 24 | 25 | const mainSeries = chart.addSeries(LightweightCharts.HistogramSeries, { 26 | lineWidth: 1, 27 | color: '#ff0000', 28 | }); 29 | 30 | mainSeries.setData(generateData()); 31 | } 32 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/line-dotted.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 17 | 18 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries, { 19 | lineWidth: 1, 20 | lineStyle: LightweightCharts.LineStyle.Dotted, 21 | color: '#ff0000', 22 | }); 23 | 24 | mainSeries.setData(generateData()); 25 | } 26 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/line-series-with-point-markers-and-hidden-line.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = (window.chart = LightweightCharts.createChart(container, { 17 | timeScale: { 18 | barSpacing: 12, 19 | }, 20 | layout: { attributionLogo: false }, 21 | })); 22 | 23 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries, { 24 | lineWidth: 1, 25 | color: '#ff0000', 26 | pointMarkersVisible: true, 27 | lineVisible: false, 28 | }); 29 | 30 | mainSeries.setData(generateData()); 31 | } 32 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/line-with-custom-color-change-color-later.js: -------------------------------------------------------------------------------- 1 | function runTestCase(container) { 2 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 3 | 4 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries); 5 | 6 | mainSeries.setData([ 7 | { time: 1, value: 1, color: 'red' }, 8 | { time: 2, value: 2, color: 'green' }, 9 | { time: 3, value: 3 }, 10 | { time: 4, value: 3 }, 11 | { time: 5, value: 3 }, 12 | { time: 6, value: 3 }, 13 | { time: 7, value: 3 }, 14 | { time: 8, value: 3 }, 15 | { time: 9, value: 1, color: 'blue' }, 16 | ]); 17 | 18 | chart.timeScale().fitContent(); 19 | 20 | return new Promise(resolve => { 21 | setTimeout(() => { 22 | mainSeries.applyOptions({ color: 'red' }); 23 | resolve(); 24 | }, 300); 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/line-with-custom-color.js: -------------------------------------------------------------------------------- 1 | function runTestCase(container) { 2 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 3 | 4 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries); 5 | 6 | mainSeries.setData([ 7 | { time: 1, value: 1, color: 'red' }, 8 | { time: 2, value: 2, color: 'green' }, 9 | { time: 3, value: 1, color: 'blue' }, 10 | ]); 11 | 12 | chart.timeScale().fitContent(); 13 | } 14 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/line-with-point-markers.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = (window.chart = LightweightCharts.createChart(container, { 17 | timeScale: { 18 | barSpacing: 12, 19 | }, 20 | layout: { attributionLogo: false }, 21 | })); 22 | 23 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries, { 24 | lineWidth: 1, 25 | color: '#ff0000', 26 | pointMarkersVisible: true, 27 | }); 28 | 29 | mainSeries.setData(generateData()); 30 | } 31 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/line-with-steps-with-custom-color.js: -------------------------------------------------------------------------------- 1 | function runTestCase(container) { 2 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 3 | 4 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries, { 5 | lineType: LightweightCharts.LineType.WithSteps, 6 | }); 7 | 8 | mainSeries.setData([ 9 | { time: 1, value: 1, color: 'red' }, 10 | { time: 2, value: 2, color: 'green' }, 11 | { time: 3, value: 1, color: 'blue' }, 12 | ]); 13 | 14 | chart.timeScale().fitContent(); 15 | } 16 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/line-with-whitespaces.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | const item = { 6 | time: time.getTime() / 1000, 7 | }; 8 | 9 | if (i % 3 !== 0) { 10 | item.value = i; 11 | } 12 | 13 | res.push(item); 14 | time.setUTCDate(time.getUTCDate() + 1); 15 | } 16 | return res; 17 | } 18 | 19 | function runTestCase(container) { 20 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 21 | 22 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries); 23 | 24 | mainSeries.setData(generateData()); 25 | } 26 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/line.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 17 | 18 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries, { 19 | lineWidth: 1, 20 | color: '#ff0000', 21 | }); 22 | 23 | mainSeries.setData(generateData()); 24 | } 25 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/no-last-price-animation-on-adding-whitespace.js: -------------------------------------------------------------------------------- 1 | function runTestCase(container) { 2 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 3 | 4 | const series = chart.addSeries(LightweightCharts.LineSeries, { 5 | lastPriceAnimation: LightweightCharts.LastPriceAnimationMode.OnDataUpdate, 6 | }); 7 | series.setData([ 8 | { time: '1990-04-24', value: 0 }, 9 | { time: '1990-04-25', value: 1 }, 10 | { time: '1990-04-26', value: 2 }, 11 | { time: '1990-04-28', value: 3 }, 12 | ]); 13 | 14 | series.update({ time: '1990-04-29' }); 15 | series.update({ time: '1990-04-29' }); 16 | 17 | return new Promise(resolve => setTimeout(resolve, 500)); 18 | } 19 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/no-last-price-animation-on-first-set-data-after-reseting-data.js: -------------------------------------------------------------------------------- 1 | function runTestCase(container) { 2 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 3 | 4 | const series = chart.addSeries(LightweightCharts.LineSeries, { 5 | lastPriceAnimation: LightweightCharts.LastPriceAnimationMode.OnDataUpdate, 6 | }); 7 | series.setData([ 8 | { time: '1990-04-24', value: 0 }, 9 | { time: '1990-04-25', value: 1 }, 10 | { time: '1990-04-26', value: 2 }, 11 | ]); 12 | 13 | series.setData([]); 14 | 15 | series.setData([ 16 | { time: '1990-04-24', value: 0 }, 17 | { time: '1990-04-25', value: 1 }, 18 | { time: '1990-04-26', value: 2 }, 19 | { time: '1990-04-28', value: 3 }, 20 | ]); 21 | 22 | return new Promise(resolve => setTimeout(resolve, 500)); 23 | } 24 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/no-last-price-animation-on-first-set-data.js: -------------------------------------------------------------------------------- 1 | function runTestCase(container) { 2 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 3 | 4 | const series = chart.addSeries(LightweightCharts.LineSeries, { 5 | lastPriceAnimation: LightweightCharts.LastPriceAnimationMode.OnDataUpdate, 6 | }); 7 | series.setData([ 8 | { time: '1990-04-24', value: 0 }, 9 | { time: '1990-04-25', value: 1 }, 10 | { time: '1990-04-26', value: 2 }, 11 | { time: '1990-04-28', value: 3 }, 12 | ]); 13 | 14 | return new Promise(resolve => setTimeout(resolve, 500)); 15 | } 16 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/price-line-line-visibility.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 1; i < 60; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 17 | 18 | const series = chart.addSeries(LightweightCharts.LineSeries); 19 | series.setData(generateData()); 20 | 21 | series.createPriceLine({ price: 10, lineVisible: false }); 22 | } 23 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/price-line-overlapping-series-title.js: -------------------------------------------------------------------------------- 1 | function runTestCase(container) { 2 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 3 | 4 | const series = chart.addSeries(LightweightCharts.LineSeries, { title: 'TITLE' }); 5 | 6 | series.setData([ 7 | { time: '1990-04-24', value: 0 }, 8 | { time: '1990-04-25', value: 1 }, 9 | { time: '1990-04-26', value: 2 }, 10 | ]); 11 | 12 | series.createPriceLine({ color: 'red', title: 'price line', price: 2 }); 13 | } 14 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/price-line-with-percentage-scale-mode.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 1; i < 60; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { 17 | rightPriceScale: { 18 | mode: LightweightCharts.PriceScaleMode.Percentage, 19 | }, 20 | layout: { attributionLogo: false }, 21 | }); 22 | 23 | const series = chart.addSeries(LightweightCharts.LineSeries); 24 | series.setData(generateData()); 25 | 26 | series.createPriceLine({ price: 10 }); 27 | } 28 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/several-non-regular-series.js: -------------------------------------------------------------------------------- 1 | function runTestCase(container) { 2 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 3 | 4 | const lineSeries1 = chart.addSeries(LightweightCharts.LineSeries); 5 | const lineSeries2 = chart.addSeries(LightweightCharts.LineSeries); 6 | 7 | lineSeries1.setData([ 8 | { time: '2020-01-02', value: 2 }, 9 | { time: '2020-01-04', value: 4 }, 10 | { time: '2020-01-06', value: 6 }, 11 | ]); 12 | 13 | lineSeries2.setData([ 14 | { time: '2020-01-01', value: 11 }, 15 | { time: '2020-01-03', value: 13 }, 16 | { time: '2020-01-05', value: 15 }, 17 | { time: '2020-01-07', value: 17 }, 18 | ]); 19 | 20 | chart.timeScale().scrollToPosition(-5); 21 | } 22 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/single-point-area.js: -------------------------------------------------------------------------------- 1 | function runTestCase(container) { 2 | const chart = window.chart = LightweightCharts.createChart(container, { 3 | timeScale: { 4 | rightOffset: 7, 5 | barSpacing: 50, 6 | }, 7 | layout: { attributionLogo: false }, 8 | }); 9 | 10 | const mainSeries = chart.addSeries(LightweightCharts.AreaSeries, { 11 | lineWidth: 1, 12 | color: '#ff0000', 13 | priceLineVisible: false, 14 | lastValueVisible: false, 15 | autoscaleInfoProvider: () => ({ 16 | priceRange: { 17 | minValue: 0, 18 | maxValue: 200, 19 | }, 20 | }), 21 | }); 22 | 23 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)).getTime() / 1000; 24 | mainSeries.setData([{ time: time, value: 100 }]); 25 | } 26 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/single-point-line.js: -------------------------------------------------------------------------------- 1 | function runTestCase(container) { 2 | const chart = window.chart = LightweightCharts.createChart(container, { 3 | timeScale: { 4 | rightOffset: 7, 5 | barSpacing: 50, 6 | }, 7 | layout: { attributionLogo: false }, 8 | }); 9 | 10 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries, { 11 | lineWidth: 1, 12 | color: '#ff0000', 13 | priceLineVisible: false, 14 | lastValueVisible: false, 15 | autoscaleInfoProvider: () => ({ 16 | priceRange: { 17 | minValue: 0, 18 | maxValue: 200, 19 | }, 20 | }), 21 | }); 22 | 23 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)).getTime() / 1000; 24 | mainSeries.setData([{ time: time, value: 100 }]); 25 | } 26 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/step-line-area.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 17 | 18 | const mainSeries = chart.addSeries(LightweightCharts.AreaSeries, { 19 | lineType: LightweightCharts.LineType.WithSteps, 20 | }); 21 | 22 | mainSeries.setData(generateData()); 23 | } 24 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/step-line-baseline.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2000, 0, 1, 0, 0, 0, 0)); 4 | 5 | for (let i = 0; i < 25; ++i) { 6 | const item = { 7 | time: time.getTime() / 1000, 8 | value: Math.sin(i), 9 | }; 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | 12 | res.push(item); 13 | } 14 | return res; 15 | } 16 | 17 | function runTestCase(container) { 18 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 19 | 20 | const baselineSeries = chart.addSeries(LightweightCharts.BaselineSeries, { 21 | lineType: LightweightCharts.LineType.WithSteps, 22 | }); 23 | 24 | const data = generateData(); 25 | baselineSeries.setData(data); 26 | 27 | chart.timeScale().fitContent(); 28 | } 29 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/series/step-line.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 17 | 18 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries, { 19 | lineWidth: 1, 20 | lineType: LightweightCharts.LineType.WithSteps, 21 | color: '#ff0000', 22 | }); 23 | 24 | mainSeries.setData(generateData()); 25 | } 26 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/set-crosshair-position.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2023, 0, 1, 0, 0, 0, 0)); 4 | 5 | for (let i = 0; i < 12; ++i) { 6 | res.push({ 7 | time: time.getTime() / 1000, 8 | value: i, 9 | }); 10 | 11 | time.setUTCMonth(time.getUTCMonth() + 1); 12 | } 13 | 14 | return res; 15 | } 16 | 17 | function runTestCase(container) { 18 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 19 | 20 | const series = chart.addSeries(LightweightCharts.LineSeries); 21 | series.setData(generateData()); 22 | chart.timeScale().fitContent(); 23 | 24 | chart.setCrosshairPosition(Date.UTC(2023, 2, 1, 0, 0, 0, 0) / 1000, 10, series); 25 | } 26 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/time-scale/disable-bold-labels.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 17 | chart.timeScale().applyOptions({ allowBoldLabels: false }); 18 | 19 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries); 20 | 21 | mainSeries.setData(generateData()); 22 | } 23 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/time-scale/realign-partially-hidden-time-scale-marks.js: -------------------------------------------------------------------------------- 1 | function runTestCase(container) { 2 | const chart = window.chart = LightweightCharts.createChart(container, { 3 | handleScroll: false, 4 | handleScale: false, 5 | layout: { attributionLogo: false }, 6 | }); 7 | 8 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries); 9 | 10 | const data = [ 11 | { time: 1639037531, value: 41.162 }, 12 | { time: 1639040640, value: 41.366 }, 13 | { time: 1639040700, value: 41.37 }, 14 | { time: 1639040760, value: 41.372 }, 15 | { time: 1639040940, value: 41.341 }, 16 | { time: 1639041000, value: 41.334 }, 17 | ]; 18 | 19 | mainSeries.setData(data); 20 | 21 | chart.timeScale().setVisibleLogicalRange({ from: 0.5, to: data.length - 1.5 }); 22 | } 23 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/time-scale/set-and-clear-series.js: -------------------------------------------------------------------------------- 1 | function runTestCase(container) { 2 | const data = [ 3 | { time: 1609459200, value: 500 }, 4 | { time: 1609545600, value: 600 }, 5 | { time: 1609632000, value: 700 }, 6 | ]; 7 | 8 | const chart = window.chart = LightweightCharts.createChart(container, { 9 | timeScale: { 10 | barSpacing: 40, 11 | }, 12 | layout: { attributionLogo: false }, 13 | }); 14 | 15 | const series = chart.addSeries(LightweightCharts.LineSeries); 16 | series.setData(data); 17 | series.setData([]); 18 | series.setData(data); 19 | } 20 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/time-scale/set-visible-range.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 1000; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 17 | 18 | const mainSeries = chart.addSeries(LightweightCharts.LineSeries); 19 | 20 | mainSeries.setData(generateData()); 21 | 22 | chart.timeScale().setVisibleRange({ 23 | from: (new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0))).getTime() / 1000, 24 | to: (new Date(Date.UTC(2018, 1, 1, 0, 0, 0, 0))).getTime() / 1000, 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /tests/e2e/graphics/test-cases/transparent-color.js: -------------------------------------------------------------------------------- 1 | function generateData() { 2 | const res = []; 3 | const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); 4 | for (let i = 0; i < 500; ++i) { 5 | res.push({ 6 | time: time.getTime() / 1000, 7 | value: i, 8 | }); 9 | 10 | time.setUTCDate(time.getUTCDate() + 1); 11 | } 12 | return res; 13 | } 14 | 15 | function runTestCase(container) { 16 | const chart = window.chart = LightweightCharts.createChart(container, { layout: { attributionLogo: false } }); 17 | 18 | const mainSeries = chart.addSeries(LightweightCharts.HistogramSeries, { 19 | color: 'transparent', 20 | }); 21 | 22 | mainSeries.setData(generateData()); 23 | } 24 | -------------------------------------------------------------------------------- /tests/e2e/helpers/page-timeout.ts: -------------------------------------------------------------------------------- 1 | import { Page } from 'puppeteer'; 2 | 3 | // await a setTimeout delay evaluated within page context 4 | export async function pageTimeout(page: Page, delay: number): Promise { 5 | return page.evaluate( 6 | (ms: number) => new Promise( 7 | (resolve: () => void) => setTimeout(resolve, ms) 8 | ), 9 | delay 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /tests/e2e/helpers/retry-tests.ts: -------------------------------------------------------------------------------- 1 | type TestFn = () => void | Promise; 2 | export async function retryTest(retries: number, fn: TestFn): Promise { 3 | for (let i = 0; i <= retries; i++) { 4 | try { 5 | await fn(); 6 | return; 7 | } catch (err) { 8 | if (i === retries) { 9 | console.log('Test failed after retries'); 10 | throw err; 11 | } 12 | console.log(`Retrying test... (${i + 1}/${retries})`); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/e2e/helpers/zoom-action.ts: -------------------------------------------------------------------------------- 1 | import { Page } from 'puppeteer'; 2 | 3 | export async function doZoomInZoomOut(page: Page): Promise { 4 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 5 | const prevViewport = page.viewport()!; 6 | await page.setViewport({ 7 | ...prevViewport, 8 | deviceScaleFactor: 2, 9 | }); 10 | 11 | await page.setViewport(prevViewport); 12 | } 13 | -------------------------------------------------------------------------------- /tests/e2e/interactions/helpers/get-interaction-test-cases.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { dirname, join } from 'node:path'; 3 | import { fileURLToPath } from 'node:url'; 4 | 5 | const currentFilePath = fileURLToPath(import.meta.url); 6 | const currentDirectory = dirname(currentFilePath); 7 | 8 | import { getTestCases as getTestCasesImpl, TestCase } from '../../helpers/get-test-cases'; 9 | 10 | const testCasesDir = join(currentDirectory, '..', 'test-cases'); 11 | 12 | export function getTestCases(): Record { 13 | return getTestCasesImpl(testCasesDir); 14 | } 15 | -------------------------------------------------------------------------------- /tests/e2e/interactions/helpers/test-page-dummy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Test case page 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 17 | 18 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /tests/e2e/interactions/test-cases/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | module.exports = { 4 | env: { 5 | browser: true, 6 | node: false, 7 | }, 8 | rules: { 9 | 'no-unused-vars': ['error', { varsIgnorePattern: '^(beforeInteractions|after(Initial|Final)Interactions|(initial|final)InteractionsToPerform)$', args: 'none' }], 10 | }, 11 | globals: { 12 | LightweightCharts: false, 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /tests/e2e/memleaks/helpers/test-page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Test case page 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /tests/e2e/memleaks/test-cases/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | module.exports = { 4 | env: { 5 | browser: true, 6 | node: false, 7 | }, 8 | rules: { 9 | 'no-unused-vars': ['error', { varsIgnorePattern: '^runTestCase$', args: 'none' }], 10 | }, 11 | globals: { 12 | LightweightCharts: false, 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /tests/e2e/tsconfig.composite.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.composite.base.json", 3 | "compilerOptions": { 4 | "skipLibCheck": true, 5 | "esModuleInterop": true, 6 | "lib": [ 7 | "dom", 8 | "es2020" 9 | ], 10 | "moduleResolution": "bundler", 11 | "module": "Preserve", 12 | }, 13 | "include": [ 14 | "./coverage/**/*.ts", 15 | "./graphics/graphics-test-cases.ts", 16 | "./graphics/generate-golden-content.ts", 17 | "./graphics/generate-test-cases.ts", 18 | "./graphics/utils.ts", 19 | "./graphics/helpers/**/*.ts", 20 | "./memleaks/**/*.ts", 21 | "./interactions/**/*.ts", 22 | "./helpers/**/*.ts", 23 | "../../src/**/*.ts", 24 | "./*.ts" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /tests/setup.mjs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/tsconfig.composite.json: -------------------------------------------------------------------------------- 1 | { 2 | "references": [ 3 | { "path": "./e2e/tsconfig.composite.json" }, 4 | { "path": "./unittests/tsconfig.composite.json" } 5 | ], 6 | "include": [] 7 | } 8 | -------------------------------------------------------------------------------- /tests/type-checks/tsconfig.composite.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.composite.base.json", 3 | "compilerOptions": { 4 | "strict": true, 5 | }, 6 | "references": [ 7 | { "path": "../../src/tsconfig.composite.json" } 8 | ], 9 | "include": [ 10 | "./**/*.ts" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /tests/type-checks/watermarks.ts: -------------------------------------------------------------------------------- 1 | import { createChart, createImageWatermark, createTextWatermark, LineSeries } from '../../src'; 2 | 3 | const chart = createChart('anything'); 4 | 5 | const mainSeries = chart.addSeries(LineSeries); 6 | mainSeries.setData([]); 7 | 8 | const imageWatermark = createImageWatermark(chart.panes()[0], '/debug/image.svg', { 9 | alpha: 0.5, 10 | padding: 50, 11 | maxHeight: 400, 12 | maxWidth: 400, 13 | }); 14 | 15 | imageWatermark.applyOptions({ 16 | alpha: 0.75, 17 | }); 18 | 19 | const watermarkPlugin = createTextWatermark(chart.panes()[1], { 20 | horzAlign: 'center', 21 | vertAlign: 'center', 22 | lines: [ 23 | { 24 | text: 'Hello', 25 | color: 'rgba(255,0,0,0.5)', 26 | fontSize: 100, 27 | fontStyle: 'bold', 28 | }, 29 | ], 30 | }); 31 | 32 | watermarkPlugin.applyOptions({ 33 | horzAlign: 'left', 34 | }); 35 | -------------------------------------------------------------------------------- /tests/unittests/delegate.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-floating-promises */ 2 | import { expect } from 'chai'; 3 | import { describe, it } from 'node:test'; 4 | 5 | import { Delegate } from '../../src/helpers/delegate'; 6 | 7 | describe('Delegate', () => { 8 | it('unsubscribeAll', () => { 9 | const linkedObjectOne = {}; 10 | const linkedObjectTwo = {}; 11 | const eventDelegate: Delegate = new Delegate(); 12 | eventDelegate.subscribe(() => {}, linkedObjectOne); 13 | 14 | expect(eventDelegate.hasListeners()).to.be.equal(true); 15 | 16 | eventDelegate.unsubscribeAll(linkedObjectTwo); 17 | expect(eventDelegate.hasListeners()).to.be.equal(true); 18 | 19 | eventDelegate.unsubscribeAll(linkedObjectOne); 20 | expect(eventDelegate.hasListeners()).to.be.equal(false); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /tests/unittests/series-options.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-floating-promises */ 2 | import { expect } from 'chai'; 3 | import { describe, it } from 'node:test'; 4 | 5 | import { precisionByMinMove } from '../../src/model/series-options'; 6 | 7 | describe('SeriesOptions', () => { 8 | it('precisionByMinMove', () => { 9 | expect(precisionByMinMove(0.001)).to.be.equal(3); 10 | expect(precisionByMinMove(0.01)).to.be.equal(2); 11 | expect(precisionByMinMove(0.1)).to.be.equal(1); 12 | expect(precisionByMinMove(1)).to.be.equal(0); 13 | expect(precisionByMinMove(10)).to.be.equal(0); 14 | expect(precisionByMinMove(0.25)).to.be.equal(2); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /tests/unittests/setup.units.mjs: -------------------------------------------------------------------------------- 1 | import '../setup.mjs'; 2 | -------------------------------------------------------------------------------- /tests/unittests/tsconfig.composite.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.composite.base.json", 3 | "compilerOptions": { 4 | "module": "ESNext", 5 | "types": ["node"] 6 | }, 7 | "references": [ 8 | { "path": "../../src/tsconfig.composite.json" } 9 | ], 10 | "include": [ 11 | "./**/*.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /tsconfig.composite.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.options.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | "outDir": "lib/composite" 6 | }, 7 | "references": [], 8 | "include": [] 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.composite.json: -------------------------------------------------------------------------------- 1 | { 2 | "references": [ 3 | { "path": "./src/tsconfig.composite.json" }, 4 | { "path": "./tests/tsconfig.composite.json" }, 5 | { "path": "./website/tsconfig.json" }, 6 | ], 7 | "include": [] 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.options.json", 3 | "compilerOptions": { 4 | "noEmit": true 5 | }, 6 | "include": [ 7 | "src/**/*.ts", 8 | "tests/**/*.ts" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "declaration": true, 5 | "importHelpers": true, 6 | "lib": [ 7 | "dom", 8 | "es2020" 9 | ], 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "noEmitOnError": true, 13 | "noFallthroughCasesInSwitch": true, 14 | "noImplicitOverride": true, 15 | "noImplicitReturns": true, 16 | "noUnusedLocals": true, 17 | "preserveConstEnums": true, 18 | "resolveJsonModule": true, 19 | "rootDir": "./", 20 | "strict": true, 21 | "stripInternal": false, 22 | "target": "es2020", 23 | "typeRoots": [ 24 | "./src/typings", 25 | "node_modules/@types" 26 | ], 27 | "types": [ 28 | "_global-types", 29 | "dom-not-standarted" 30 | ] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.options.json", 3 | "compilerOptions": { 4 | "outDir": "lib/prod", 5 | "plugins": [ 6 | { "transform": "ts-transformer-strip-const-enums", "entrySourceFiles": ["./src/index.ts"] }, 7 | { "transform": "ts-transformer-properties-rename", "entrySourceFiles": ["./src/index.ts"] } 8 | ], 9 | "stripInternal": true 10 | }, 11 | "include": [ 12 | "src/**/*.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /tsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", 3 | "tagDefinitions": [ 4 | { 5 | "tagName": "@template", 6 | "syntaxKind": "block" 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /website/.browserslistrc: -------------------------------------------------------------------------------- 1 | [production] 2 | >0.5% 3 | not dead 4 | not op_mini all 5 | 6 | [development] 7 | last 1 chrome version 8 | last 1 firefox version 9 | last 1 safari version -------------------------------------------------------------------------------- /website/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | globals: { 3 | CHART_BACKGROUND_COLOR: true, 4 | CHART_BACKGROUND_RGB_COLOR: true, 5 | LINE_LINE_COLOR: true, 6 | LINE_LINE2_COLOR: true, 7 | LINE_LINE3_COLOR: true, 8 | LINE_LINE4_COLOR: true, 9 | LINE_LINE5_COLOR: true, 10 | AREA_TOP_COLOR: true, 11 | AREA_BOTTOM_COLOR: true, 12 | BAR_UP_COLOR: true, 13 | BAR_DOWN_COLOR: true, 14 | BASELINE_TOP_LINE_COLOR: true, 15 | BASELINE_TOP_FILL_COLOR1: true, 16 | BASELINE_TOP_FILL_COLOR2: true, 17 | BASELINE_BOTTOM_LINE_COLOR: true, 18 | BASELINE_BOTTOM_FILL_COLOR1: true, 19 | BASELINE_BOTTOM_FILL_COLOR2: true, 20 | HISTOGRAM_COLOR: true, 21 | CHART_TEXT_COLOR: true, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /website/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false # @docusaurus/faster installs platform specific dependencies which don't work on the CI 2 | -------------------------------------------------------------------------------- /website/.previous-typings-cache/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "typeRoots": [ 4 | "./node_modules/@types" 5 | ], 6 | "lib": [ 7 | "dom", 8 | "es2020" 9 | ] 10 | }, 11 | "include": [ 12 | "./*.d.ts", 13 | "../../dist/typings.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /website/docs/migrations/_category_.yml: -------------------------------------------------------------------------------- 1 | label: "Migration guides" 2 | position: 5 3 | -------------------------------------------------------------------------------- /website/docs/plugins/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | globals: { 3 | document: false, 4 | createChart: false, 5 | LineSeries: false, 6 | AreaSeries: false, 7 | BarSeries: false, 8 | BaselineSeries: false, 9 | CandlestickSeries: false, 10 | HistogramSeries: false, 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /website/docs/plugins/pixel-perfect-rendering/_category_.yml: -------------------------------------------------------------------------------- 1 | label: "Pixel Perfect Rendering" 2 | position: 5 3 | -------------------------------------------------------------------------------- /website/docs/plugins/pixel-perfect-rendering/widths/_category_.yml: -------------------------------------------------------------------------------- 1 | label: "Default Widths" 2 | position: 0 3 | -------------------------------------------------------------------------------- /website/plugins/enhanced-codeblock/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = function chartCodeBlockPlugin(context, options) { 4 | return { 5 | name: 'enhanced-codeblock', 6 | 7 | getThemePath: () => path.resolve(__dirname, './theme'), 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /website/plugins/enhanced-codeblock/theme/CodeBlock/styles.module.css: -------------------------------------------------------------------------------- 1 | .iframe { 2 | width: 100%; 3 | height: 250px; 4 | } 5 | -------------------------------------------------------------------------------- /website/plugins/enhanced-codeblock/theme/CodeBlock/use-id.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | 3 | const randomId = () => `${Math.random().toString(36).slice(2, 11)}`; 4 | 5 | function useClientId(): string { 6 | const [uuid, setUuid] = useState(''); 7 | useEffect(() => setUuid(randomId()), []); 8 | 9 | return uuid; 10 | } 11 | 12 | export function useId(staticId?: string): string { 13 | return typeof staticId === 'string' ? staticId : useClientId(); 14 | } 15 | -------------------------------------------------------------------------------- /website/src/components/CardLinkList/index.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import React from 'react'; 3 | 4 | import { default as CardLink, type CardLinkItem } from '../CardLink'; 5 | 6 | interface Props { 7 | className: string; 8 | items: CardLinkItem[]; 9 | frontPage?: boolean; 10 | } 11 | 12 | export default function DocCardList({ items, className, frontPage = false }: Props): React.JSX.Element { 13 | return ( 14 |
15 | {items.map((item: CardLinkItem, index: number) => ( 16 |
17 | 18 |
19 | ))} 20 |
21 | ); 22 | } 23 | 24 | -------------------------------------------------------------------------------- /website/src/components/InstantDetails/index.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import React, { type ReactNode } from 'react'; 3 | 4 | import styles from './styles.module.css'; 5 | 6 | export interface Props { 7 | children: ReactNode; 8 | } 9 | 10 | /* 11 | * Simple Details Component which is not animated. 12 | * Default Docusaurus implementation of `details` element includes 13 | * an animation with a duration based on the revealed contents height. 14 | * This results in a very long animation if revealing a lot of content 15 | * such as a code block. 16 | */ 17 | export default function InstantDetails({ children }: Props): React.JSX.Element { 18 | return
{children}
; 19 | } 20 | -------------------------------------------------------------------------------- /website/src/components/landing-page/Banner/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './banner.module.css'; 4 | 5 | export default function Banner(props: { 6 | text: string; 7 | link: string; 8 | linkText: string; 9 | }): React.JSX.Element { 10 | return ( 11 | 12 |
{props.text}
13 |
{props.linkText}
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /website/src/img/cog.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/src/img/react.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /website/src/img/shapes.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/src/img/tradingview-logo-small.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /website/src/img/vuejs.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /website/src/pages/chart.module.css: -------------------------------------------------------------------------------- 1 | /* This is used by React charts across the site, but not on index (see HeroChart folder) */ 2 | .ChartContainer * { 3 | overflow: hidden; 4 | } 5 | 6 | .ChartContainer table td, 7 | .ChartContainer table tr { 8 | border: none; 9 | } 10 | -------------------------------------------------------------------------------- /website/src/theme/Footer/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Footer from '@theme-original/Footer'; 3 | import AnalyticsWrapper from '../analytics-wrapper'; 4 | 5 | // eslint-disable-next-line import/no-default-export 6 | export default function FooterWrapper(props) { 7 | return ( 8 | 9 |