JSComponent add a <div>

Is there a utility function to insert add a

element to el in JSComponent
and wait for it to be fully integrated before trying to use it?

Below what I am trying to do, but const board = JXG.JSXGraph.initBoard(el.id, {} gives me errors no matter what I tried!

import panel as pn
from panel.custom import JSComponent

# Define the JSXGraph Custom Component
class JSXGraphComponent(JSComponent):
    _esm = """
    import jsxgraph from 'https://cdn.jsdelivr.net/npm/jsxgraph@1.4.6/+esm';

    export function render({model, el}) {
        // Ensure the container element has proper dimensions and ID
        el.style.width = model.width + 'px';
        el.style.height = model.height + 'px';
        el.style.border = '1px solid black';

        if (!el.id) {
            el.id = 'jsxgraph-container-' + Math.random().toString(36).substr(2, 9);
        }

        // Wait for DOM to render before initializing JSXGraph
        requestAnimationFrame(() => {
            if (!el.parentElement) return; // Ensure container exists

            // Initialize JSXGraph board
            const board = JXG.JSXGraph.initBoard(el.id, {
                boundingbox: [-5, 5, 5, -5],
                showCopyright: false,
                showNavigation: false,
                keepAspectRatio: true,
                axis: true
            });

            // Add points and arrows
            const p = board.create('point', [-3, -3], { name: '\\\\(p\\\\)' });
            const q = board.create('point', [0, 2], { name: '\\\\(q\\\\)' });
            const r = board.create('point', [3, -3], { name: '\\\\(r\\\\)' });

            board.create('arrow', [p, q], {
                withLabel: true,
                name: '\\\\(\\\\vec{pq}\\\\)',
                label: { position: 'top' },
                lastArrow: { type: 4, size: 8 }
            });
            board.create('arrow', [q, r], {
                withLabel: true,
                name: '\\\\(\\\\vec{qr}\\\\)',
                label: { position: 'top' },
                lastArrow: { type: 4, size: 8 }
            });
            board.create('arrow', [p, r], {
                withLabel: true,
                name: '\\\\(\\\\vec{pr}\\\\)',
                label: { position: 'top' },
                lastArrow: { type: 4, size: 8 }
            });

            // Cleanup logic
            model._board = board;
        });

        return () => {
            if (model._board) {
                JXG.JSXGraph.freeBoard(model._board);
                model._board = null;
            }
        };
    }
    """

# Create the component instance
jsxgraph = JSXGraphComponent(width=500, height=500)

# Serve using Panel
pn.Column("# JSXGraph Embedded in Panel", jsxgraph).servable()

The guide Import JSXGraph as ES6 module | JSXGraph share helped me create the below.

import panel as pn
import param

from panel.custom import JSComponent

class JSXGraphComponent(JSComponent):

    _esm = """
    import JXG from 'https://cdn.jsdelivr.net/npm/jsxgraph/distrib/jsxgraphcore.mjs';

    export function render({ model, el }) {
      // Create a div element to hold the JSXGraph board
      let boardDiv = document.createElement("div");
      boardDiv.style.width = "500px";
      boardDiv.style.height = "500px";
      el.appendChild(boardDiv);
      
      // Initialize the JSXGraph board
      let board = JXG.JSXGraph.initBoard(boardDiv, {
        boundingbox: [-5, 5, 5, -5],
        axis: true
      });

      // Add a point to the board
      let p = board.create('point', [1, 1], {name:'A'});

      // Add a line through the point
      let line = board.create('line', [[0, 0], p]);
    }
    """

JSXGraphComponent().servable()

You can try a live example here.

Good grief, the whole time I misread the initBoard function to require an ID! Thanks Marc!

1 Like