angularsvggraphchatbotjointjs

Joint JS custom Rect


Using JoinJS free version I would like to be able to create a simple Rect that holds 2 buttons and a not pre-defined amount of input fields, some of which also has an output node. Could anyone point me towards the right direction?

I have tried to add HTML elements but the transformation of them (position especially) does not update on change. (probably because JointJS elements are in SVGs). Are HTML elements the solution or is there any way to do this without them? I am using Angular.

const inputField = document.createElement('input');
  inputField.setAttribute('placeholder', 'Type something')
  inputField.setAttribute('type', 'text');
  inputField.style.position = 'absolute';
  inputField.style.borderRadius = '5px';
  inputField.style.width = 170 + 'px';
  inputField.style.backgroundColor = "white"
  inputField.style.height = 30 + 'px';
  inputField.style.left = x + 'px';
  inputField.style.top = y + 'px';

  // Append the input field to the HTML body or a container element
  document.body.appendChild(inputField); // or specify a container element
  // Event handler to handle input changes
  inputField.addEventListener('input', (event) => {
    const inputValue = (event.target as HTMLInputElement).value;
    console.log('Input Value:', inputValue);
  });

  // Event handler to remove the input field when the rectangle is removed
  rect.on('remove', () => {
    if (inputField.parentNode) {
      inputField.parentNode.removeChild(inputField);
    }
  });

  rect.on('change:position', (element, newPosition) => {
    const { x, y } = newPosition;
    inputField.style.left = x + 'px';
    inputField.style.top = y + 'px';
  });

I have edited my post so it shows the initial setup, but it basically stops working as soon as I drag the paper.


Solution

  • You could use the svg foreignObject element in JointJS instead of trying to use the HTML overlay approach. This should resolve your issues with position and removing.

    The latest jointjs version improves support for foreignObject, but the documentation isn't updated yet.

    <!DOCTYPE html>
    <html>
    <head>
        <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jointjs/3.7.1/joint.css" />
    </head>
    <body>
        <!-- content -->
        <div id="myholder"></div>
    
        <!-- dependencies -->
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.4/jquery.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.4.1/backbone.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jointjs/3.7.1/joint.js"></script>
    
        <!-- code -->
        <script type="text/javascript">
    
        const namespace = joint.shapes;
    
        const graph = new joint.dia.Graph({}, { cellNamespace: namespace });
    
        const paper = new joint.dia.Paper({
            el: document.getElementById('myholder'),
            model: graph,
            width: 500,
            height: 350,
            async: true,
            frozen: true,
            cellViewNamespace: namespace,
            background: {
                color: '#F7F7F7'
            },
        });
    
        paper.on('blank:pointerdown cell:pointerdown', () => {
            document.activeElement.blur();
        });
    
        const Form = joint.dia.Element.define('example.Form', {
            attrs: {
                foreignObject: {
                    width: 'calc(w)',
                    height: 'calc(h)'
                }
            }
        }, {
            markup: joint.util.svg/* xml */`
                <foreignObject @selector="foreignObject">
                    <div
                        xmlns="http://www.w3.org/1999/xhtml"
                        style="background:white;border: 1px solid black;width:calc(100% - 2px);height:calc(100% - 2px);display:flex;flex-direction:column;justify-content:center;align-items:center;"
                    >
                        <input 
                            @selector="name" 
                            type="text" 
                            name="name" 
                            placeholder="Type something"
                            style="border-radius:5px;width:170px;height:30px;margin-bottom:2px;"
                        />
                        <button style="border-radius:5px;width:178px;height:36px;">
                            <span>Submit</span>
                        </button>
                    </div>
                </foreignObject>
            `
        });
    
        joint.shapes.example.FormView = joint.dia.ElementView.extend({
    
            events: {
                // Name of event + css selector : custom view method name
                'input input': 'onInput'
            },
    
            onInput: function(evt) {
                console.log('Input Value:', evt.target.value);
                this.model.attr('name/props/value', evt.target.value);
            }
        });
    
        const form = new Form();
        form.position(72, 70);
        form.resize(355, 200);
        form.addTo(graph);
     
        paper.unfreeze();
    
        </script>
    </body>
    </html>