UNPKG

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