2

I would like to have svg shape scale based on text content of text area or text-input. As the text content increases, the size of the underlying svg element should increase as well

This is what I have so far:

    var graph = new joint.dia.Graph;
                var paper = new joint.dia.Paper({
                    el: $('#myholder'),
                    width: 1330,
                    height: 660,
                    model: graph,
                    gridSize: 1,
                    defaultLink: new joint.dia.Link({
                        attrs: {'.marker-target': {d: 'M 10 0 L 0 5 L 10 10 z'}}
                    }),
                    validateConnection: function (cellViewS, magnetS, cellViewT, magnetT, end, linkView) {
                        // Prevent linking from input ports.
                        if (magnetS && magnetS.getAttribute('type') === 'input')
                            return false;
                        // Prevent linking from output ports to input ports within one element.
                        if (cellViewS === cellViewT)
                            return false;
                        // Prevent loop linking
                        return (magnetS !== magnetT);
                        // Prevent linking to input ports.
                        return magnetT && magnetT.getAttribute('type') === 'input';
                    },
                    // Enable marking available cells & magnets
                    markAvailable: true,
                    //Enable link snapping within 75px lookup radius
//                    snapLinks: {radius: 75},
                    interactive: function (cellView, methodName)
                    {
                        if (cellView.model.get('isInteractive') === false)
                            return false;
//                        return true;
                    }
                });

                joint.shapes.devs.CircleModel = joint.shapes.devs.Model.extend({
                    markup: '<g class="rotatable"><g class="scalable"><circle class="body"/></g><text class="label"/><g class="inPorts"/><g class="outPorts"/></g>',
//                    portMarkup: '<g class="port port<%=1%>"><rect class="port-body"/><text class="port-label"/></g>',
                    defaults: joint.util.deepSupplement({
                        type: 'devs.CircleModel',
                        attrs: {
                            '.body': {r: 50, cx: 50, stroke: '', fill: 'white'},
                            '.label': {text: '', 'ref-y': 0.5, 'y-alignment': 'middle'},
                            '.port-body': {r: 3, width: 10, height: 10, x: -5, stroke: 'gray', fill: 'lightgray', magnet: 'active'}
                        }

                    }, joint.shapes.devs.Model.prototype.defaults)
                });
                joint.shapes.devs.CircleModelView = joint.shapes.devs.ModelView;

                var rect = new joint.shapes.basic.Rect({
                    isInteractive: false,
                    position: {x: 10, y: 50},
                    size: {width: 51, height: 41},
                    attrs: {rect: {fill: '#D6F2FC', stroke: '#7E7E7E'}, '.': {magnet: false}}
                });

                // Create a custom element.
// ------------------------
                joint.shapes.html = {};
                joint.shapes.html.Element = joint.shapes.basic.Rect.extend({
                    defaults: joint.util.deepSupplement({
                        type: 'html.Element',
                        attrs: {
                            rect: {stroke: 'none', 'fill-opacity': 0}
                        }
                    }, joint.shapes.basic.Rect.prototype.defaults)
                });

// Create a custom view for that element that displays an HTML div above it.
// -------------------------------------------------------------------------

                joint.shapes.html.ElementView = joint.dia.ElementView.extend({
                    template: [
                        '<div class="html-element">',
                         '<button class="delete">x</button>',
                        '<span></span>', '<br/>',
//                        '<input type="text" value="" />',
                        '<textarea id="txt" type="text" rows="10" value="Start writing"></textarea>',
                        '</div>'
                    ].join(''),
                    initialize: function () {
                        _.bindAll(this, 'updateBox');
                        joint.dia.ElementView.prototype.initialize.apply(this, arguments);

                        this.$box = $(_.template(this.template)());
                        // Prevent paper from handling pointerdown.
                        this.$box.find('input,select').on('mousedown click', function (evt) {
                            evt.stopPropagation();
                        });

                        this.$ruler = $('<span>', {style: 'visibility: hidden; white-space: pre'});
                        $(document.body).append(this.$ruler);

                        // This is an example of reacting on the input change and storing the input data in the cell model.
                        this.$box.find('textarea').on('input', _.bind(function (evt) {

                            var val = $(evt.target).val();
                            this.model.set('textarea', val);
                            this.$ruler.html(val);
                            var width = this.$ruler[0].offsetWidth;
                            var height = this.$ruler[0].offsetHeight;
                            var area = width * height;
                            height = area / 150;
                            width = 150;
                            if ((area > 9000)) 
                            {
                                this.model.set('size', {width: width + 50, height: height + 80});
                                this.$box.find('textarea').css({width: width, height: height + 30});
//                                this.$box.find('.color-edit').css({width: width + 50, height: height + 80});
                                this.$box.find('.in').css({top: height + 75});
                            }
                        }, this));

                        this.$box.find('textarea').on('click', _.bind(function () {
                            this.$box.find('.delete').css({opacity: 1});
                            this.$box.find('textarea').css({opacity: 1});
                        }, this));

                        this.$box.find('textarea').on('blur', _.bind(function () {
                            this.$box.find('.delete').css({opacity: 0});
                            this.$box.find('textarea').css({opacity: 0});
                        }, this));

                        this.$box.find('.delete').on('click', _.bind(this.model.remove, this.model));
                        // Update the box position whenever the underlying model changes.
                        this.model.on('change', this.updateBox, this);
                        // Remove the box when the model gets removed from the graph.
                        this.model.on('remove', this.removeBox, this);

                        this.updateBox();
                        this.listenTo(this.model, 'process:ports', this.update);
                        joint.dia.ElementView.prototype.initialize.apply(this, arguments);
                    },
                    render: function () {
                        joint.dia.ElementView.prototype.render.apply(this, arguments);
                        this.paper.$el.prepend(this.$box);
                        this.updateBox();
                        return this;
                    },
                    updateBox: function ()
                    {
                        // Set the position and dimension of the box so that it covers the JointJS element.
                        var bbox = this.model.getBBox();
                        // Example of updating the HTML with a data stored in the cell model.
                        this.$box.find('label').text(this.model.get('label'));
                        this.$box.find('span').text(this.model.get('select'));
                        this.$box.css({width: bbox.width + 6, height: bbox.height, left: bbox.x, top: bbox.y, transform: 'rotate(' + (this.model.get('angle') || 0) + 'deg)'});
                    },
                    removeBox: function (evt) {
                        this.$ruler.remove();
                        this.$box.remove();
                    }
                });

                paper.on('cell:pointerdblclick', function (cellView, evt, x, y)
                {
                    var clone = cellView.model.clone();
                    if (rect.id === cellView.model.id)
                    {
                        clone = new joint.shapes.html.Element({
                            position: {x: 100, y: 60},
                            size: {width: 81, height: 69},
                            inPorts: [''],
                            outPorts: [''],
                            attrs: {
                                '.': {magnet: true},
                                '.label': {text: '', 'ref-x': .4, 'ref-y': .2},
                                '.inPorts circle': {type: 'input'},
                                '.outPorts circle': {type: 'output'},
                                '.port-body': {r: 3}
                            }
                        });
//                        clone.resize(2*81,2*39)
                        graph.addCell(clone);
                    }
                });

//                // First, unembed the cell that has just been grabbed by the user.
                paper.on('cell:pointerdown', function (cellView, evt, x, y) {

                    var cell = cellView.model;
                    if (!cell.get('embeds') || cell.get('embeds').length === 0) {
                        // Show the dragged element above all the other cells (except when the
                        // element is a parent).
                        cell.toFront();
                        _.invoke(graph.getConnectedLinks(cell), 'toFront');
                    }

                    if (cell.get('parent')) {
                        graph.getCell(cell.get('parent')).unembed(cell);
                    }
                });
                // When the dragged cell is dropped over another cell, let it become a child of the
                //element below.
                paper.on('cell:pointerup', function (cellView, evt, x, y) {

                    if (cellView.model.isLink())
                        return;

                    var cell = cellView.model;
                    var cellViewsBelow = paper.findViewsFromPoint(cell.getBBox().center());
                    if (cellViewsBelow.length) {
                        // Note that the findViewsFromPoint() returns the view for the `cell` itself.
                        var cellViewBelow = _.find(cellViewsBelow, function (c) {
                            return c.model.id !== cell.id;
                        });
                        // Prevent recursive embedding.
                        if (cellViewBelow && cellViewBelow.model.get('parent') !== cell.id) {
                            cellViewBelow.model.embed(cell);
                        }
                    }
                });
                graph.addCells([rect]);

Could not find a solution elsewhere. Any help would be appreciated. thanks

1 Answers1

1
  1. You have to make the HTML Input resize based on the text inside.

Auto-scaling input[type=text] to width of value?

  1. The ElementView has to listen to the HTML Input changes (input event) and update the size of the model based on the width and height of the HTML Input.

Example:

function onTextInput(evt) {    
     var $input = $(evt.target);
     // 1. auto-scaling the input based on the text inside.
     $input.attr('size', Math.max($input.val().length, 10));    
     // 2. resizing the model to the size of the input + padding.
     model.resize($input.outerWidth() + 5, $input.outerHeight() + 40);
}

$('input').on('input', onTextInput);

JS Fiddle: http://jsfiddle.net/kumilingus/Lrffgvqn/

Similar with HTML TextArea, where the only difference will be the way how you auto-scale it based on the text inside.

Community
  • 1
  • 1
Roman
  • 1,652
  • 11
  • 15
  • Thank you but I am trying to expand the element vertically as above. Updated the code in question. Please have a look once –  Jul 29 '15 at 11:46
  • Also I would like to keep the resize option as in your answer which makes more sense in practical –  Jul 29 '15 at 11:49
  • Fixed it! but unable to drag the element –  Jul 29 '15 at 12:18
  • I want to increase size vertically and horizontally. Its not working :/ Here's the fiddle: http://jsfiddle.net/Lrffgvqn/4/ –  Jul 29 '15 at 13:38