Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugin creation from scratch #1713

Open
vishnuc opened this issue Oct 19, 2024 · 8 comments
Open

Plugin creation from scratch #1713

vishnuc opened this issue Oct 19, 2024 · 8 comments

Comments

@vishnuc
Copy link

vishnuc commented Oct 19, 2024

Hi , I can see various example of different plugins created on tradingview , It is mind blowing.

now I want to create my own plugin , but there is nowhere how to create a plugin from scratch , step by step tutorial.It will be great if there is mini how to on how to create our own plugins in two types: custom series and drawing primitives.

like one example of what should be there inside

class MyCustomSeries {
/* Class implementing the ICustomSeriesPaneView interface */
}

I was running npm create lwc-plugin@latest and created plugin and it uses vite , typescript , fancy canvas etc

is it possible to create this plugin via vanilla js ? I dont want to use nodejs or typescript

@Skiv1989
Copy link

You can find examples in the repo, for example start with sinple one https://github.com/tradingview/lightweight-charts/tree/master/plugin-examples/src/plugins/anchored-text and then you will get how it works

@vishnuc
Copy link
Author

vishnuc commented Oct 21, 2024

Hi , yea was working on it in weekend and was able to replicate it via vanilla js using below code , my question is in some example in library it uses/importing fancy-canvas , but here my code in vanilla working fine without it , Am I missing something ? or tv library has built in fancy canvas ?

  <script type="module">
        import { createChart } from '/tv1.mjs';

        // Renderer for the Lollipop series
        class LollipopSeriesRenderer {
            constructor() {
                this._data = null;
                this._options = null;
            }

            draw(target, priceConverter) {
                target.useBitmapCoordinateSpace(scope => this._drawImpl(scope, priceConverter));
            }

            update(data, options) {
                this._data = data;
                this._options = options;
            }

            _drawImpl(scope, priceToCoordinate) {
                if (!this._data || !this._options || this._data.bars.length === 0 || !this._data.visibleRange) {
                    return;
                }

                const bars = this._data.bars.map(bar => ({
                    x: bar.x,
                    y: priceToCoordinate(bar.originalData.value) ?? 0,
                }));

                const lineWidth = Math.min(this._options.lineWidth, this._data.barSpacing)*3;
                const radius = Math.min(Math.floor(this._data.barSpacing / 2), 5);  // Max radius for the circles
                const zeroY = priceToCoordinate(0);

                for (let i = this._data.visibleRange.from; i < this._data.visibleRange.to; i++) {
                    const bar = bars[i];
                    const xPosition = bar.x * scope.horizontalPixelRatio;
                    const yPosition = bar.y * scope.verticalPixelRatio;

                    scope.context.beginPath();
                    scope.context.fillStyle = this._options.color;
                    
                    // Draw the line from zero to the value
                    scope.context.fillRect(xPosition - lineWidth / 2, zeroY * scope.verticalPixelRatio, lineWidth, yPosition - zeroY * scope.verticalPixelRatio);
                    
                    // Draw the circle on top
                    scope.context.arc(xPosition, yPosition, radius * scope.horizontalPixelRatio, 0, Math.PI * 2);
                    scope.context.fill();
                }
            }
        }

        // Lollipop Series class
        class LollipopSeries {
            constructor() {
                this._renderer = new LollipopSeriesRenderer();
            }

            priceValueBuilder(plotRow) {
                return [0, plotRow.value];
            }

            isWhitespace(data) {
                return data.value === undefined;
            }

            renderer() {
                return this._renderer;
            }

            update(data, options) {
                this._renderer.update(data, options);
            }

            defaultOptions() {
                return {
                    lineWidth: 2,
                    color: 'rgb(0, 100, 255)',
                };
            }
        }

        // Create the chart
        const chart = createChart(document.getElementById('chart'), {
            width: window.innerWidth,
            height: 500,
        });

        // Add Lollipop series to chart
        const customSeriesView = new LollipopSeries();
        const myCustomSeries = chart.addCustomSeries(customSeriesView, {
            lineWidth: 1,   // Make lines thinner
            color: 'rgb(0, 200, 255)',  // Lollipop color
        });

        // Generate random data for the Lollipop series
        function generateLollipopData() {
            const data = [];
            let baseTime = new Date('2021-01-01').getTime() / 1000; // Unix timestamp in seconds

            for (let i = 0; i < 50; i++) {
                const value = Math.random() * 300;  // Use a wider value range
                data.push({
                    time: baseTime + i * 60 * 60 * 24, // Increment by 1 day
                    value: value,
                });
            }
            return data;
        }

        // Set Lollipop data
        const lollipopData = generateLollipopData();
        myCustomSeries.setData(lollipopData);

        // Update chart size on window resize
        window.addEventListener('resize', () => {
            chart.applyOptions({ width: window.innerWidth });
        });
    </script>

@vishnuc
Copy link
Author

vishnuc commented Oct 23, 2024

@SlicedSilver : hi can you please tell is this right way to do in vanilla js ? how does it works without fancy-canvas ? does lightweight library i build contains fancy-canvas already ?

@SlicedSilver
Copy link
Contributor

There are a few different builds of the library. The 'standalone' version includes fancy-canvas bundled and you should use this if you aren't using a build or bundle tool.

Plugins themselves don't specifically require fancy-canvas. Some example might use imports from fancy canvas but this would be for the 'types' and these imports wouldn't be in the actual generated JS code.

@vishnuc
Copy link
Author

vishnuc commented Oct 23, 2024

Thanks I am using normal 160kb version and everything works fine..when can we expect next version .. any roadmaps ?

@SlicedSilver
Copy link
Contributor

SlicedSilver commented Oct 24, 2024

We don't share timelines but we are actively working on the next release. There is a 5.0 milestone.

@vishnuc
Copy link
Author

vishnuc commented Oct 24, 2024

so next release is 5 directly or something like 4.5

@VRciF
Copy link

VRciF commented Jan 4, 2025

Sorry for hijacking this thread, but I wanted to share how I compiled the available plugins and how to use them in the browser using vanilla js and I think this would fit here.

This is for the current github master version 4.2.2 and is a brief summary of the required steps from the readme.

  1. Checkout the repo in some tmp dir git clone https://github.com/tradingview/lightweight-charts.git
  2. cd lightweight-charts
  3. Make sure to use node 20. It did not work with latest node 22: nvm install 20
  4. Just in case, as this was mentioned in some github issues, force flush the node cache: npm cache clean --force
  5. Build lightweight charts, without tests also mentioned in some other issues, using skip puppeteer: PUPPETEER_SKIP_DOWNLOAD=true npm install && npm run build:prod
  6. cd plugin-examples
  7. Install required packages npm install
  8. Build plugins npm run compile

Now you have a directory compiled in your plugin-examples directory, which also contains a js file, e.g. compiled/vertical-line/vertical-line.js. Copy the js files (or the whole compiled directory) to your webroot to make it accessible for the browser.
As an example I loaded lightweight charts and the vertical line plugin like this

    <script src="res/lightweight-charts.standalone.production.js"></script>
    <script src="res/lightweight-charts-4.4.1-plugins/vertical-line/vertical-line.js" type="module"></script>
    <script type="module">
        import { VertLine } from "/res/lightweight-charts-4.2.2-plugins/vertical-line/vertical-line.js";
        window.VertLine = VertLine
    </script>

On how to use those plugins, have a look at the typescript examples, for example for vertical-line. In browser vanilla js this is done via

  let vertLine = new VertLine(chart, lineSeries, someUnixTimestampInSeconds, {
     showLabel: true,
          labelText: 'Hello',
          color: 'red',
          width: 2,
  })
  lineSeries.attachPrimitive(vertLine);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants