Skip to content

Commit 5d1787a

Browse files
committed
formula suggest and keys (tab, left, right, up, down)..
1 parent bd7285d commit 5d1787a

File tree

8 files changed

+337
-54
lines changed

8 files changed

+337
-54
lines changed

src/local/base/suggest.d.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Element } from "./element";
2+
export declare class Suggest extends Element {
3+
list: Array<[string, string]>;
4+
width: number;
5+
filterList: Array<Element>;
6+
currentIndex: number;
7+
itemClick: (it: [string, string]) => void;
8+
constructor(list: Array<[string, string]>, width: number);
9+
private documentHandler(e);
10+
private documentKeydownHandler(e);
11+
private hideAndRemoveEvents();
12+
private removeEvents();
13+
private clickItemHandler(it);
14+
search(target: Element, word: string): void;
15+
}

src/local/base/suggest.ts

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { Element, h } from "./element";
2+
import { buildItem } from "./item";
3+
import { buildMenu } from "./menu";
4+
import { bind, unbind } from "../event";
5+
6+
export class Suggest extends Element {
7+
8+
filterList: Array<Element> = [];
9+
currentIndex = 0;
10+
11+
itemClick: (it: [string, string]) => void = (it) => {}
12+
13+
constructor (public list: Array<[string, string]>, public width: number) {
14+
super();
15+
this.class('spreadsheet-suggest').hide()
16+
}
17+
18+
private documentHandler (e: any) {
19+
if (this.el.contains(e.target)) {
20+
return false
21+
}
22+
this.hideAndRemoveEvents()
23+
}
24+
private documentKeydownHandler (e: any) {
25+
console.log('keyCode: ', e)
26+
if (this.filterList.length <= 0 && e.target.type !== 'textarea') return ;
27+
28+
switch (e.keyCode) {
29+
case 37: // left
30+
e.returnValue = false
31+
break;
32+
case 38: // up
33+
this.filterList[this.currentIndex].deactive()
34+
this.currentIndex--
35+
if (this.currentIndex < 0) {
36+
this.currentIndex = this.filterList.length - 1
37+
}
38+
this.filterList[this.currentIndex].active()
39+
e.returnValue = false
40+
break;
41+
case 39: // right
42+
e.returnValue = false
43+
break;
44+
case 40: // down
45+
this.filterList[this.currentIndex].deactive()
46+
this.currentIndex++
47+
if (this.currentIndex > this.filterList.length - 1) {
48+
this.currentIndex = 0
49+
}
50+
this.filterList[this.currentIndex].active()
51+
e.returnValue = false
52+
break;
53+
case 13: // enter
54+
this.filterList[this.currentIndex].el.click()
55+
e.returnValue = false
56+
break;
57+
}
58+
}
59+
60+
private hideAndRemoveEvents () {
61+
this.hide()
62+
this.removeEvents();
63+
}
64+
private removeEvents () {
65+
unbind('click', this.data('_outsidehandler'))
66+
unbind('keydown', this.data('_keydownhandler'))
67+
}
68+
69+
private clickItemHandler (it: [string, string]) {
70+
// console.log('click.it: ', it)
71+
this.itemClick(it)
72+
this.hideAndRemoveEvents()
73+
}
74+
75+
76+
search (target: Element, word: string) {
77+
this.removeEvents()
78+
79+
const { left, top, width, height } = target.offset()
80+
this.styles({left: `${left}px`, top: `${top + height + 2}px`, width: `${this.width}px`})
81+
82+
let lis: any = this.list
83+
if (!/^\s*$/.test(word)) {
84+
lis = this.list.filter(it => it[0].startsWith(word.toUpperCase()))
85+
}
86+
lis = lis.map((it: [string, string]) => {
87+
const item = buildItem().on('click', (evt) => this.clickItemHandler(it)).child(it[0])
88+
if (it[1]) {
89+
item.child(h().class('label').html(it[1]))
90+
}
91+
return item
92+
// return `<div class="spreadsheet-item" it-key="${it[0]}">${it[0]}${it[1] ? '<div class="label">'+it[1]+'</div>' : ''}</div>`
93+
})
94+
95+
this.filterList = lis
96+
this.currentIndex = 0
97+
98+
if (lis.length <= 0) {
99+
lis = [buildItem().child('No Result')] // `<div class="spreadsheet-item disabled">No Result</div>`
100+
} else {
101+
lis[0].active()
102+
103+
// clickoutside
104+
this.data('_outsidehandler', (evt: Event) => {
105+
this.documentHandler(evt)
106+
})
107+
this.data('_keydownhandler', (evt: any) => this.documentKeydownHandler(evt))
108+
setTimeout(() => {
109+
bind('click', this.data('_outsidehandler'))
110+
bind('keydown', this.data('_keydownhandler'))
111+
}, 0)
112+
}
113+
this.html(``)
114+
this.child(buildMenu().children(lis)).show()
115+
}
116+
}

src/local/editor.d.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
11
import { Element } from "./base/element";
2+
import { Suggest } from "./base/suggest";
23
import { Cell } from "../core/cell";
4+
import { Formula } from "../core/formula";
35
export declare class Editor {
46
defaultRowHeight: number;
7+
formulas: Array<Formula>;
58
el: Element;
69
target: HTMLElement | null;
710
value: Cell | null;
811
editor: Element;
912
textarea: Element;
1013
textline: Element;
14+
suggest: Suggest;
1115
change: (v: Cell) => void;
12-
constructor(defaultRowHeight: number);
16+
constructor(defaultRowHeight: number, formulas: Array<Formula>);
1317
onChange(change: (v: Cell) => void): void;
1418
set(target: HTMLElement, value: Cell | null): void;
1519
setValue(value: Cell | null): string;
1620
setStyle(value: Cell | null): void;
1721
clear(): void;
22+
private setTextareaRange(position);
1823
private inputChange(evt);
24+
private autocomplete(v);
1925
reload(): void;
2026
}

src/local/editor.ts

+45-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { Element, h } from "./base/element";
2+
import { Suggest } from "./base/suggest";
23
import { Cell, getStyleFromCell } from "../core/cell"
4+
import { Formula } from "../core/formula";
35

46
export class Editor {
57
el: Element;
@@ -8,15 +10,30 @@ export class Editor {
810
editor: Element;
911
textarea: Element;
1012
textline: Element; // 计算输入文本的宽度用的element
11-
change: (v: Cell) => void = (v) => {};
12-
constructor (public defaultRowHeight: number) {
13+
suggest: Suggest; // autocomplete show
1314

15+
change: (v: Cell) => void = (v) => {};
16+
constructor (public defaultRowHeight: number, public formulas : Array<Formula>) {
17+
const suggestList: any = formulas.map(it => [it.key, it.title])
1418
this.el = h().children([this.editor = h().class('spreadsheet-editor').children([
1519
this.textarea = h('textarea')
1620
.on('input', (evt: Event) => this.inputChange(evt)),
1721
this.textline = h().styles({visibility: 'hidden', overflow: 'hidden', position: 'fixed', top: '0', left: '0'})
1822
])
19-
]).hide()
23+
, this.suggest = new Suggest(suggestList, 180)])
24+
25+
this.suggest.itemClick = (it) => {
26+
console.log('>>>>>>>>>>>>', it)
27+
const text = `=${it[0]}()`;
28+
if (this.value) {
29+
this.value.text = text
30+
}
31+
this.textarea.val(text);
32+
this.textline.html(text);
33+
this.setTextareaRange(text.length - 1)
34+
// (<any>this.textarea.el).setSelectionRange(text.length + 1, text.length + 1);
35+
// setTimeout(() => (<any>this.textarea.el).focus(), 10)
36+
}
2037
}
2138

2239
onChange (change: (v: Cell) => void) {
@@ -28,8 +45,9 @@ export class Editor {
2845
this.target = target;
2946
const text = this.setValue(value)
3047
this.el.show();
31-
(<any>this.textarea.el).setSelectionRange(text.length, text.length);
32-
setTimeout(() => (<any>this.textarea.el).focus(), 10)
48+
this.setTextareaRange(text.length)
49+
// (<any>this.textarea.el).setSelectionRange(text.length, text.length);
50+
// setTimeout(() => (<any>this.textarea.el).focus(), 10)
3351
this.reload();
3452
}
3553

@@ -59,6 +77,13 @@ export class Editor {
5977
this.textline.html('')
6078
}
6179

80+
private setTextareaRange (position: number) {
81+
setTimeout(() => {
82+
(<any>this.textarea.el).setSelectionRange(position, position);
83+
(<any>this.textarea.el).focus()
84+
}, 10)
85+
}
86+
6287
private inputChange (evt: any) {
6388
const v = evt.target.value
6489
if (!/^\s*$/.test(v)) {
@@ -67,11 +92,26 @@ export class Editor {
6792
} else {
6893
this.value = {text: v}
6994
}
95+
this.autocomplete(v);
7096
}
7197
this.textline.html(v);
7298
this.reload()
7399
}
74100

101+
private autocomplete (v: string) {
102+
if (v[0] === '=') {
103+
if (!v.includes('(')) {
104+
const search = v.substring(1)
105+
console.log(':::;search word:', search)
106+
this.suggest.search(this.editor, search);
107+
} else {
108+
this.suggest.hide()
109+
}
110+
} else {
111+
this.suggest.hide()
112+
}
113+
}
114+
75115
reload () {
76116
// setTimeout(() => {
77117
if (this.target) {

src/local/selector.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export declare class Selector {
2424
changeCopy: (evt: any, arrow: 'bottom' | 'top' | 'left' | 'right', startRow: number, startCol: number, stopRow: number, stopCol: number) => void;
2525
constructor(ss: Spreadsheet, table: Table);
2626
mousedown(evt: any): void;
27-
private keydown(evt);
27+
setCurrentTarget(target: HTMLElement): void;
2828
private cornerMousedown(evt);
2929
reload(): void;
3030
private setOffset();

src/local/selector.ts

+6-15
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ export class Selector {
3838
this.cornerEl,
3939
this.copyEl
4040
])
41-
bind('keydown', (evt: Event) => { this.keydown(evt) })
4241
}
4342

4443
mousedown (evt: any) {
@@ -51,8 +50,9 @@ export class Selector {
5150
this.setOffset()
5251
return
5352
}
54-
Object.assign(this, {startTarget: evt.target, endTarget: evt.target})
55-
this.setOffset()
53+
// Object.assign(this, {startTarget: evt.target, endTarget: evt.target})
54+
// this.setOffset()
55+
this.setCurrentTarget(evt.target)
5656

5757
mouseMoveUp((e: any) => {
5858
if (e.buttons === 1 && e.target.getAttribute('type') === 'cell') {
@@ -63,18 +63,9 @@ export class Selector {
6363
}
6464
}
6565

66-
private keydown (evt: any) {
67-
// up, down, left, right
68-
switch (evt.keyCode) {
69-
case 37: // left
70-
break;
71-
case 38: // up
72-
break;
73-
case 39: // right
74-
break;
75-
case 40: // down
76-
break;
77-
}
66+
setCurrentTarget (target: HTMLElement) {
67+
Object.assign(this, {startTarget: target, endTarget: target})
68+
this.setOffset()
7869
}
7970

8071
private cornerMousedown (evt: any) {

0 commit comments

Comments
 (0)