UNPKG

6.32 kBJavaScriptView Raw
1/*
2* Copyright (C) 1998-2021 by Northwoods Software Corporation. All Rights Reserved.
3*/
4// This is the definitions of the predefined text editor used by TextEditingTool
5// when you set or bind TextBlock.editable to true.
6// You do not need to load this file in order to use in-place text editing.
7import * as go from '../release/go-module.js';
8// HTML + JavaScript text editor menu, made with HTMLInfo
9// This is a re-implementation of the default text editor
10// This file exposes one instance of HTMLInfo, window.TextEditor
11// See also TextEditor.html
12((window) => {
13 const TextEditor = new go.HTMLInfo();
14 const textarea = document.createElement('textarea');
15 textarea.id = 'myTextArea';
16 textarea.addEventListener('input', (e) => {
17 const tool = TextEditor.tool;
18 if (tool.textBlock === null)
19 return;
20 const tempText = tool.measureTemporaryTextBlock(textarea.value);
21 const scale = textarea.textScale;
22 textarea.style.width = 20 + tempText.measuredBounds.width * scale + 'px';
23 textarea.rows = tempText.lineCount;
24 }, false);
25 textarea.addEventListener('keydown', (e) => {
26 const tool = TextEditor.tool;
27 if (tool.textBlock === null)
28 return;
29 const key = e.key;
30 if (key === "Enter") { // Enter
31 if (tool.textBlock.isMultiline === false)
32 e.preventDefault();
33 tool.acceptText(go.TextEditingTool.Enter);
34 return;
35 }
36 else if (key === "Tab") { // Tab
37 tool.acceptText(go.TextEditingTool.Tab);
38 e.preventDefault();
39 return;
40 }
41 else if (key === "Escape") { // Esc
42 tool.doCancel();
43 if (tool.diagram !== null)
44 tool.diagram.doFocus();
45 }
46 }, false);
47 // handle focus:
48 textarea.addEventListener('focus', (e) => {
49 const tool = TextEditor.tool;
50 if (!tool || tool.currentTextEditor === null)
51 return;
52 if (tool.state === go.TextEditingTool.StateActive) {
53 tool.state = go.TextEditingTool.StateEditing;
54 }
55 if (tool.selectsTextOnActivate) {
56 textarea.select();
57 textarea.setSelectionRange(0, 9999);
58 }
59 }, false);
60 // Disallow blur.
61 // If the textEditingTool blurs and the text is not valid,
62 // we do not want focus taken off the element just because a user clicked elsewhere.
63 textarea.addEventListener('blur', (e) => {
64 const tool = TextEditor.tool;
65 if (!tool || tool.currentTextEditor === null || tool.state === go.TextEditingTool.StateNone)
66 return;
67 textarea.focus();
68 if (tool.selectsTextOnActivate) {
69 textarea.select();
70 textarea.setSelectionRange(0, 9999);
71 }
72 }, false);
73 TextEditor.valueFunction = () => textarea.value;
74 TextEditor.mainElement = textarea; // to reference it more easily
75 TextEditor.tool = null; // Initialize
76 // used to be in doActivate
77 TextEditor.show = (textBlock, diagram, tool) => {
78 if (!diagram || !diagram.div)
79 return;
80 if (!(textBlock instanceof go.TextBlock))
81 return;
82 if (TextEditor.tool !== null)
83 return; // Only one at a time.
84 TextEditor.tool = tool; // remember the TextEditingTool for use by listeners
85 // This is called during validation, if validation failed:
86 if (tool.state === go.TextEditingTool.StateInvalid) {
87 textarea.style.border = '3px solid red';
88 textarea.focus();
89 return;
90 }
91 // This part is called during initalization:
92 const loc = textBlock.getDocumentPoint(go.Spot.Center);
93 const pos = diagram.position;
94 const sc = diagram.scale;
95 let textscale = textBlock.getDocumentScale() * sc;
96 if (textscale < tool.minimumEditorScale)
97 textscale = tool.minimumEditorScale;
98 // Add slightly more width/height to stop scrollbars and line wrapping on some browsers
99 // +6 is firefox minimum, otherwise lines will be wrapped improperly
100 const textwidth = (textBlock.naturalBounds.width * textscale) + 6;
101 const textheight = (textBlock.naturalBounds.height * textscale) + 2;
102 const left = (loc.x - pos.x) * sc;
103 const top = (loc.y - pos.y) * sc;
104 textarea.value = textBlock.text;
105 // the only way you can mix font and fontSize is if the font inherits and the fontSize overrides
106 // in the future maybe have textarea contained in its own div
107 diagram.div.style['font'] = textBlock.font;
108 const paddingsize = 1;
109 textarea.style['position'] = 'absolute';
110 textarea.style['zIndex'] = '100';
111 textarea.style['font'] = 'inherit';
112 textarea.style['fontSize'] = (textscale * 100) + '%';
113 textarea.style['lineHeight'] = 'normal';
114 textarea.style['width'] = (textwidth) + 'px';
115 textarea.style['left'] = ((left - (textwidth / 2) | 0) - paddingsize) + 'px';
116 textarea.style['top'] = ((top - (textheight / 2) | 0) - paddingsize) + 'px';
117 textarea.style['textAlign'] = textBlock.textAlign;
118 textarea.style['margin'] = '0';
119 textarea.style['padding'] = paddingsize + 'px';
120 textarea.style['border'] = '0';
121 textarea.style['outline'] = 'none';
122 textarea.style['whiteSpace'] = 'pre-wrap';
123 textarea.style['overflow'] = 'hidden'; // for proper IE wrap
124 textarea.rows = textBlock.lineCount;
125 textarea.textScale = textscale; // attach a value to the textarea, for convenience
126 textarea.className = 'goTXarea';
127 // Show:
128 diagram.div.appendChild(textarea);
129 // After adding, focus:
130 textarea.focus();
131 if (tool.selectsTextOnActivate) {
132 textarea.select();
133 textarea.setSelectionRange(0, 9999);
134 }
135 };
136 TextEditor.hide = (diagram, tool) => {
137 TextEditor.tool = null; // forget reference to TextEditingTool
138 if (diagram.div)
139 diagram.div.removeChild(textarea);
140 };
141 window.TextEditor = TextEditor;
142})(window);