I want to dynamically expand/shrink the size of Node depending upon the length of characters being entered in the textBlock of Node. Currently when I focus textblock, an HTML kind of text element appear over the Node and as I type larger number of characters, the textblock expands out of the Node. And only when I focus out of the textBlock does the Node itself increase in size.
I want to expand the Node’s size as I enter the text in textBlock and not just after the focus is out.
OK, here’s an updated extensions/TextEditor.js file that has been customized to keep the actual TextBlock.text updated with the text being entered into the HTML textarea. I have named this file TextEditorRealtime.js.
Note that your requirement of having the edited TextBlock continuously be changing means that the undo/redo behavior will be different than with normal editing. With TextEditorRealtime.js each entered character will modify the diagram; whereas normally nothing in the diagram is modified until the text is accepted. Furthermore, text validation won’t happen until after all of the changes have already been applied to the edited TextBlock.
But I assume these are acceptable compromises in order to get your required behavior, or you wouldn’t be asking for it.
"use strict";
/*
* Copyright (C) 1998-2017 by Northwoods Software Corporation. All Rights Reserved.
*/
// HTML + JavaScript text editor menu, made with HTMLInfo
// This is a re-implementation of the default text editor
// This file exposes one instance of HTMLInfo, window.TextEditor
// See also TextEditor.html
(function(window) {
var textarea = document.createElement('textarea');
textarea.id = "myTextArea";
textarea.addEventListener('input', function(e) {
var tool = TextEditor.tool;
if (tool.textBlock === null) return;
tool.diagram.startTransaction();
tool.textBlock.text = this.value;
tool.diagram.commitTransaction("input text");
var tempText = tool.measureTemporaryTextBlock(this.value);
var scale = this.textScale;
this.style.width = 20 + tempText.measuredBounds.width * scale + 'px';
this.style.height = 10 + tempText.measuredBounds.height * scale + "px";
this.rows = tempText.lineCount;
}, false);
textarea.addEventListener('keydown', function(e) {
var tool = TextEditor.tool;
if (tool.textBlock === null) return;
var keynum = e.which;
if (keynum === 13) { // Enter
if (tool.textBlock.isMultiline === false) e.preventDefault();
tool.acceptText(go.TextEditingTool.Enter);
return;
} else if (keynum === 9) { // Tab
tool.acceptText(go.TextEditingTool.Tab);
e.preventDefault();
return;
} else if (keynum === 27) { // Esc
var tb = tool.textBlock;
tool.doCancel();
tb.diagram.startTransaction();
tb.text = TextEditor.originalString;
tb.diagram.commitTransaction("cancel text edit");
if (tool.diagram !== null) tool.diagram.doFocus();
}
}, false);
// handle focus:
textarea.addEventListener('focus', function(e) {
var tool = TextEditor.tool;
if (tool.currentTextEditor === null) return;
if (tool.state === go.TextEditingTool.StateActive) {
tool.state = go.TextEditingTool.StateEditing;
}
if (tool.selectsTextOnActivate) {
textarea.select();
textarea.setSelectionRange(0, 9999);
}
}, false);
// Disallow blur.
// If the textEditingTool blurs and the text is not valid,
// we do not want focus taken off the element just because a user clicked elsewhere.
textarea.addEventListener('blur', function(e) {
var tool = TextEditor.tool;
if (tool.currentTextEditor === null) return;
textarea.focus();
if (tool.selectsTextOnActivate) {
textarea.select();
textarea.setSelectionRange(0, 9999);
}
}, false);
var TextEditor = new go.HTMLInfo();
TextEditor.valueFunction = function() { return textarea.value; }
TextEditor.mainElement = textarea; // to reference it more easily
// used to be in doActivate
TextEditor.show = function(textBlock, diagram, tool) {
if (!(textBlock instanceof go.TextBlock)) return;
TextEditor.tool = tool; // remember the TextEditingTool for use by listeners
TextEditor.originalString = textBlock.text;
// This is called during validation, if validation failed:
if (tool.state === go.TextEditingTool.StateInvalid) {
textarea.style.border = '3px solid red';
textarea.focus();
return;
}
// This part is called during initalization:
var loc = textBlock.getDocumentPoint(go.Spot.Center);
var pos = diagram.position;
var sc = diagram.scale;
var textscale = textBlock.getDocumentScale() * sc;
if (textscale < tool.minimumEditorScale) textscale = tool.minimumEditorScale;
// Add slightly more width/height to stop scrollbars and line wrapping on some browsers
// +6 is firefox minimum, otherwise lines will be wrapped improperly
var textwidth = (textBlock.naturalBounds.width * textscale) + 6;
var textheight = (textBlock.naturalBounds.height * textscale) + 2;
var left = (loc.x - pos.x) * sc;
var top = (loc.y - pos.y) * sc;
textarea.value = textBlock.text;
// the only way you can mix font and fontSize is if the font inherits and the fontSize overrides
// in the future maybe have textarea contained in its own div
diagram.div.style['font'] = textBlock.font;
var paddingsize = 1;
textarea.style.cssText =
'position: absolute;' +
'z-index: 100;' +
'font: inherit;' +
'fontSize: ' + (textscale * 100) + '%;' +
'lineHeight: normal;' +
'width: ' + (textwidth) + 'px;' +
'height: ' + (textheight) + 'px;' +
'left: ' + ((left - (textwidth / 2) | 0) - paddingsize) + 'px;' +
'top: ' + ((top - (textheight / 2) | 0) - paddingsize) + 'px;' +
'textAlign: ' + textBlock.textAlign + ';' +
'margin: 0;' +
'padding: ' + paddingsize + 'px;' +
'border: 0;' +
'outline: none;' +
'white-space: pre-wrap;' +
'overflow: hidden;' // for proper IE wrap
textarea.textScale = textscale; // attach a value to the textarea, for convenience
// Show:
diagram.div.appendChild(textarea);
// After adding, focus:
textarea.focus();
if (tool.selectsTextOnActivate) {
textarea.select();
textarea.setSelectionRange(0, 9999);
}
};
TextEditor.hide = function(diagram, tool) {
diagram.div.removeChild(textarea);
TextEditor.tool = null; // forget reference to TextEditingTool
}
window.TextEditor = TextEditor;
})(window);
Error: TextEditingTool.defaultTextEditor value is not an instance of Element: undefined
at Object.k (https://cdnjs.cloudflare.com/ajax/libs/gojs/1.6.7/go-debug.js:31:489)
at Object.jc (https://cdnjs.cloudflare.com/ajax/libs/gojs/1.6.7/go-debug.js:33:441)
at Object.l (https://cdnjs.cloudflare.com/ajax/libs/gojs/1.6.7/go-debug.js:32:204)
at hi.<anonymous> (https://cdnjs.cloudflare.com/ajax/libs/gojs/1.6.7/go-debug.js:669:87)
at init (http://localhost:10697/app/components/Orgchart/OrgchartController2.js:90:69)
at activate (http://localhost:10697/app/components/Orgchart/OrgchartController2.js:1327:21)
at new OrgchartController (http://localhost:10697/app/components/Orgchart/OrgchartController2.js:1335:9)
at e (https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js:39:394)
at Object.instantiate (https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js:40:9)
at https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js:80:442 <div flex="" layout="row" ui-view="" class="ng-cloak ng-scope" data-ng-animate="1">
Yes, the Diagram does not know about the HTML textarea element. Even if it did, that does not automatically mean that it should scroll the element, since some HTML elements would be expected not to scroll or zoom.
I suggest that you only start editing until a “LayoutCompleted” DiagramEvent occurs after you have added the new node and committed the transaction.
An other issue I’m facing now is the call to this function:
myDiagram.commandHandler.doKeyDown = function ()
var e = myDiagram.lastInput;
console.log(e.gr.code);
if ((e.gr.code === 'Enter' || e.gr.code === 'NumpadEnter') && myDiagram.selection.count == 1) {
When I press a key, it shows this error:
Uncaught TypeError: Cannot read property 'code' of undefined
at ta.myDiagram.commandHandler.doKeyDown (OrgchartController2.js:977)
at Kh.doKeyDown (go-debug.js:721)
at C.doKeyDown (go-debug.js:794)
at HTMLCanvasElement.C.mJ (go-debug.js:819)
And this error only appears after I include the TextEditorRealtime.js file in the script.
Or maybe it’s to do with the 1.7 version of gojs.
What I understand is that the ‘keydown’ event is now associated with the HTML textarea.
So what is the safe work around for this?
I’m able to receive events for the most keys using this
myDiagram.lastInput.e.key
But many of my functionality depend upon the keys ‘Enter’ and ‘Tab’ and in these cases I only receive the input event as blank. So I can’'t differentiate which key is pressed between Tab and Enter.
When a parent Node is large in width and as we type text in the defaultTextEditor of child node, the child while expanding, centers itself with respect to the parent, and in that case the sub-node grows in both direction while expanding but defaultTextEditor over it remain fixated on the left side and only grow on the right side, which results in defaultTextEditor going out of the node.
One solution is to keep the sub-node fixated at its position and move the rest of diagram to center the parent node with respect to its child. The other solution is to expand the defaultTextEditor in both directions just as its node does.
What do you think, which of the solution is possible to do?
One more thing I want to ask.
When the diagram is zoomed out a little and then if the text is edited, the textarea appear the same size and does not shrink with the node. I know it’s not supposed to do that.
But is it possible to shrink and expand it with the node size when the diagram is zoom in or out?
Thanks
Edit…
One way that is coming to my mind is to programmatically change the minSize of textBlock upon zooming in and out but I want to have your word on it.