Skip to content

Commit ee61ed6

Browse files
chieveitjkirschner
chieveit
authored andcommitted
Improve formula editing process within cells
1 parent 549f5cf commit ee61ed6

File tree

6 files changed

+394
-115
lines changed

6 files changed

+394
-115
lines changed

src/component/editor.js

Lines changed: 86 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -4,45 +4,17 @@ import Suggest from './suggest';
44
import Datepicker from './datepicker';
55
import { cssPrefix } from '../config';
66
// import { mouseMoveUp } from '../event';
7-
8-
function resetTextareaSize() {
9-
const { inputText } = this;
10-
if (!/^\s*$/.test(inputText)) {
11-
const {
12-
textlineEl, textEl, areaOffset,
13-
} = this;
14-
const txts = inputText.split('\n');
15-
const maxTxtSize = Math.max(...txts.map(it => it.length));
16-
const tlOffset = textlineEl.offset();
17-
const fontWidth = tlOffset.width / inputText.length;
18-
const tlineWidth = (maxTxtSize + 1) * fontWidth + 5;
19-
const maxWidth = this.viewFn().width - areaOffset.left - fontWidth;
20-
let h1 = txts.length;
21-
if (tlineWidth > areaOffset.width) {
22-
let twidth = tlineWidth;
23-
if (tlineWidth > maxWidth) {
24-
twidth = maxWidth;
25-
h1 += parseInt(tlineWidth / maxWidth, 10);
26-
h1 += (tlineWidth % maxWidth) > 0 ? 1 : 0;
27-
}
28-
textEl.css('width', `${twidth}px`);
29-
}
30-
h1 *= this.rowHeight;
31-
if (h1 > areaOffset.height) {
32-
textEl.css('height', `${h1}px`);
33-
}
34-
}
35-
}
7+
import Formula from './formula';
8+
import { setCaretPosition, saveCaretPosition } from '../core/caret';
369

3710
function insertText({ target }, itxt) {
3811
const { value, selectionEnd } = target;
3912
const ntxt = `${value.slice(0, selectionEnd)}${itxt}${value.slice(selectionEnd)}`;
4013
target.value = ntxt;
41-
target.setSelectionRange(selectionEnd + 1, selectionEnd + 1);
42-
4314
this.inputText = ntxt;
44-
this.textlineEl.html(ntxt);
45-
resetTextareaSize.call(this);
15+
this.render();
16+
17+
setCaretPosition(target, selectionEnd + 1);
4618
}
4719

4820
function keydownEventHandler(evt) {
@@ -55,72 +27,36 @@ function keydownEventHandler(evt) {
5527
if (keyCode === 13 && !altKey) evt.preventDefault();
5628
}
5729

58-
function inputEventHandler(evt) {
59-
const v = evt.target.value;
30+
function inputEventHandler() {
31+
// save caret position
32+
const restore = saveCaretPosition(this.textEl.el);
33+
34+
const text = this.textEl.el.textContent;
35+
this.inputText = text;
6036
// console.log(evt, 'v:', v);
61-
const { suggest, textlineEl, validator } = this;
62-
const { cell } = this;
63-
if (cell !== null) {
64-
if (('editable' in cell && cell.editable === true) || (cell.editable === undefined)) {
65-
this.inputText = v;
66-
if (validator) {
67-
if (validator.type === 'list') {
68-
suggest.search(v);
69-
} else {
70-
suggest.hide();
71-
}
72-
} else {
73-
const start = v.lastIndexOf('=');
74-
if (start !== -1) {
75-
suggest.search(v.substring(start + 1));
76-
} else {
77-
suggest.hide();
78-
}
79-
}
80-
textlineEl.html(v);
81-
resetTextareaSize.call(this);
82-
this.change('input', v);
37+
38+
const { suggest, validator } = this;
39+
40+
if (validator) {
41+
if (validator.type === 'list') {
42+
suggest.search(text);
8343
} else {
84-
evt.target.value = cell.text;
44+
suggest.hide();
8545
}
8646
} else {
87-
this.inputText = v;
88-
if (validator) {
89-
if (validator.type === 'list') {
90-
suggest.search(v);
91-
} else {
92-
suggest.hide();
93-
}
47+
const start = text.lastIndexOf('=');
48+
if (start !== -1) {
49+
suggest.search(text.substring(start + 1));
9450
} else {
95-
const start = v.lastIndexOf('=');
96-
if (start !== -1) {
97-
suggest.search(v.substring(start + 1));
98-
} else {
99-
suggest.hide();
100-
}
51+
suggest.hide();
10152
}
102-
textlineEl.html(v);
103-
resetTextareaSize.call(this);
104-
this.change('input', v);
10553
}
106-
}
54+
this.render();
55+
this.change('input', text);
10756

108-
function setTextareaRange(position) {
109-
const { el } = this.textEl;
110-
setTimeout(() => {
111-
el.focus();
112-
el.setSelectionRange(position, position);
113-
}, 0);
114-
}
115-
116-
function setText(text, position) {
117-
const { textEl, textlineEl } = this;
118-
// firefox bug
119-
textEl.el.blur();
120-
121-
textEl.val(text);
122-
textlineEl.html(text);
123-
setTextareaRange.call(this, position);
57+
// restore caret postion
58+
// to avoid caret postion missing when this.el.innerHTML changed
59+
restore();
12460
}
12561

12662
function suggestItemClick(it) {
@@ -143,7 +79,8 @@ function suggestItemClick(it) {
14379
position = this.inputText.length;
14480
this.inputText += `)${eit}`;
14581
}
146-
setText.call(this, this.inputText, position);
82+
this.render();
83+
setCaretPosition(this.textEl.el, position);
14784
}
14885

14986
function resetSuggestItems() {
@@ -159,9 +96,10 @@ function dateFormat(d) {
15996
}
16097

16198
export default class Editor {
162-
constructor(formulas, viewFn, rowHeight) {
99+
constructor(formulas, viewFn, data) {
100+
this.data = data;
163101
this.viewFn = viewFn;
164-
this.rowHeight = rowHeight;
102+
this.rowHeight = data.rows.height;
165103
this.formulas = formulas;
166104
this.suggest = new Suggest(formulas, (it) => {
167105
suggestItemClick.call(this, it);
@@ -172,27 +110,34 @@ export default class Editor {
172110
this.setText(dateFormat(d));
173111
this.clear();
174112
});
113+
this.composing = false;
175114
this.areaEl = h('div', `${cssPrefix}-editor-area`)
176115
.children(
177-
this.textEl = h('textarea', '')
116+
this.textEl = h('div', 'textarea')
117+
.attr('contenteditable', 'true')
178118
.on('input', evt => inputEventHandler.call(this, evt))
179-
.on('paste.stop', () => {})
180-
.on('keydown', evt => keydownEventHandler.call(this, evt)),
119+
.on('paste.stop', () => { })
120+
.on('keydown', evt => keydownEventHandler.call(this, evt))
121+
.on('compositionstart.stop', () => this.composing = true)
122+
.on('compositionend.stop', () => this.composing = false),
181123
this.textlineEl = h('div', 'textline'),
182124
this.suggest.el,
183125
this.datepicker.el,
184126
)
185-
.on('mousemove.stop', () => {})
186-
.on('mousedown.stop', () => {});
127+
.on('mousemove.stop', () => { })
128+
.on('mousedown.stop', () => { });
187129
this.el = h('div', `${cssPrefix}-editor`)
188-
.child(this.areaEl).hide();
130+
.children(this.areaEl).hide();
131+
this.cellEl = h('div', `${cssPrefix}-formula-cell`)
189132
this.suggest.bindInputEvents(this.textEl);
190133

191134
this.areaOffset = null;
192135
this.freeze = { w: 0, h: 0 };
193136
this.cell = null;
194137
this.inputText = '';
195-
this.change = () => {};
138+
this.change = () => { };
139+
140+
this.formula = new Formula(this);
196141
}
197142

198143
setFreezeLengths(width, height) {
@@ -212,13 +157,19 @@ export default class Editor {
212157
this.el.hide();
213158
this.textEl.val('');
214159
this.textlineEl.html('');
160+
this.formula.clear();
215161
resetSuggestItems.call(this);
216162
this.datepicker.hide();
217163
}
218164

165+
resetData(data) {
166+
this.data = data;
167+
this.rowHeight = data.rows.height;
168+
}
169+
219170
setOffset(offset, suggestPosition = 'top') {
220171
const {
221-
textEl, areaEl, suggest, freeze, el,
172+
textEl, areaEl, suggest, freeze, el, formula
222173
} = this;
223174
if (offset) {
224175
this.areaOffset = offset;
@@ -240,11 +191,13 @@ export default class Editor {
240191
}
241192
el.offset(elOffset);
242193
areaEl.offset({ left: left - elOffset.left - 0.8, top: top - elOffset.top - 0.8 });
243-
textEl.offset({ width: width - 9 + 0.8, height: height - 3 + 0.8 });
194+
textEl.css('min-width', `${width - 9 + 0.8}px`);
195+
textEl.css('min-height', `${height - 3 + 0.8}px`);
244196
const sOffset = { left: 0 };
245197
sOffset[suggestPosition] = height;
246198
suggest.setOffset(sOffset);
247199
suggest.hide();
200+
formula.renderCells();
248201
}
249202
}
250203

@@ -275,7 +228,35 @@ export default class Editor {
275228
setText(text) {
276229
this.inputText = text;
277230
// console.log('text>>:', text);
278-
setText.call(this, text, text.length);
279-
resetTextareaSize.call(this);
231+
232+
// firefox bug
233+
this.textEl.el.blur();
234+
235+
this.render();
236+
setTimeout(() => {
237+
setCaretPosition(this.textEl.el, text.length);
238+
})
239+
}
240+
241+
render() {
242+
if (this.composing) return;
243+
244+
const text = this.inputText;
245+
246+
if (text[0] != '=') {
247+
this.textEl.html(text);
248+
} else {
249+
this.formula.render();
250+
}
251+
252+
this.textlineEl.html(text);
253+
}
254+
255+
formulaCellSelecting() {
256+
return Boolean(this.formula.cell);
257+
}
258+
259+
formulaSelectCell(ri, ci) {
260+
this.formula.selectCell(ri, ci);
280261
}
281262
}

0 commit comments

Comments
 (0)