x4js 2.0.25 → 2.0.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/x4.css +1 -1
- package/lib/cjs/x4.js +2 -2
- package/lib/esm/x4.css +1 -1
- package/lib/esm/x4.mjs +2 -2
- package/lib/styles/x4.css +1 -1
- package/lib/types/x4js.d.ts +29 -4
- package/package.json +1 -1
- package/src/components/components.ts +1 -0
- package/src/components/gauge/gauge.module.scss +40 -0
- package/src/components/gauge/gauge.ts +153 -0
- package/src/components/gridview/gridview.ts +2 -2
- package/src/components/input/input.ts +9 -2
- package/src/components/keyboard/keyboard.module.scss +4 -2
- package/src/components/keyboard/keyboard.ts +10 -8
- package/src/components/listbox/listbox.ts +2 -3
- package/src/components/propgrid/propgrid.ts +12 -2
- package/src/components/spreadsheet/spreadsheet.module.scss +308 -0
- package/src/components/spreadsheet/spreadsheet.ts +1176 -0
- package/src/core/core_svg.ts +39 -4
|
@@ -0,0 +1,1176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ___ ___ __
|
|
3
|
+
* \ \/ / / _
|
|
4
|
+
* \ / /_| |_
|
|
5
|
+
* / \____ _|
|
|
6
|
+
* /__/\__\ |_|
|
|
7
|
+
*
|
|
8
|
+
* @file spreadsheet.ts
|
|
9
|
+
* @author Etienne Cochard
|
|
10
|
+
*
|
|
11
|
+
* @copyright (c) 2024 R-libre ingenierie
|
|
12
|
+
*
|
|
13
|
+
* Use of this source code is governed by an MIT-style license
|
|
14
|
+
* that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.
|
|
15
|
+
**/
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
import { Component, ComponentContent, ComponentEvents, ComponentProps, EvClick, EvContextMenu, EvDblClick, EvSelectionChange, componentFromDOM } from '../../core/component';
|
|
19
|
+
import { GridColumn } from '../gridview/gridview'
|
|
20
|
+
|
|
21
|
+
import { class_ns, isNumber, isString, setWaitCursor } from '../../core/core_tools';
|
|
22
|
+
import { CoreEvent, EventCallback, EventMap } from '../../core/core_events';
|
|
23
|
+
import { kbNav } from '../../core/core_tools';
|
|
24
|
+
|
|
25
|
+
import { Icon } from '../icon/icon';
|
|
26
|
+
import { Image } from '../image/image'
|
|
27
|
+
import { Box } from '../boxes/boxes';
|
|
28
|
+
import { CSizer } from '../sizers/sizer'
|
|
29
|
+
import { Viewport } from '../viewport/viewport';
|
|
30
|
+
import { SimpleText } from '../label/label';
|
|
31
|
+
|
|
32
|
+
import check_icon from "../checkbox/check.svg";
|
|
33
|
+
import "./spreadsheet.module.scss"
|
|
34
|
+
import { CoreElement, EvViewChange } from '../../x4.js';
|
|
35
|
+
|
|
36
|
+
interface CellRef {
|
|
37
|
+
col: number;
|
|
38
|
+
row: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type CellRenderer = (row: number, col: number, content: any) => Component;
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
function mkid(row: number, col: number) {
|
|
45
|
+
return ((row & 0xfffff) << 12) | (col & 0xfff);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
*
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
export interface EvChange extends CoreEvent {
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface StoreEvents extends EventMap {
|
|
56
|
+
changed: EvChange;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export class Store extends CoreElement<StoreEvents> {
|
|
60
|
+
private _maxrows: number;
|
|
61
|
+
private _data: Map<number, any>;
|
|
62
|
+
private _lock: number; // lock
|
|
63
|
+
private _change: boolean;
|
|
64
|
+
|
|
65
|
+
constructor() {
|
|
66
|
+
super();
|
|
67
|
+
|
|
68
|
+
this._data = new Map();
|
|
69
|
+
this._lock = 0;
|
|
70
|
+
this._change = false;
|
|
71
|
+
this._maxrows = 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
setMaxRowCount(rows: number) {
|
|
75
|
+
|
|
76
|
+
if (this._maxrows == rows) {
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (rows < this._maxrows) {
|
|
81
|
+
const n = new Map<number, any>();
|
|
82
|
+
this._data.forEach((v, k) => {
|
|
83
|
+
const row = k >> 12;
|
|
84
|
+
if (row <= rows) {
|
|
85
|
+
n.set(k, v);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
this._maxrows = rows;
|
|
91
|
+
this._changed()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
getRowCount(): number {
|
|
95
|
+
return this._maxrows;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
setData(row: number, col: number, data: any) {
|
|
99
|
+
this._data.set(mkid(row, col), data);
|
|
100
|
+
if (row > this._maxrows) {
|
|
101
|
+
this._maxrows = row;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
this._changed();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
getData(row: number, col: number) {
|
|
108
|
+
return this._data.get(mkid(row, col));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
lock() {
|
|
112
|
+
this._lock++;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
unlock() {
|
|
116
|
+
if (this._lock) {
|
|
117
|
+
this._lock--;
|
|
118
|
+
if (!this._lock && this._change) {
|
|
119
|
+
this._changed();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private _changed() {
|
|
125
|
+
if (!this._lock) {
|
|
126
|
+
this.fire("changed", {});
|
|
127
|
+
this._change = false;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
this._change = true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
*
|
|
138
|
+
*/
|
|
139
|
+
|
|
140
|
+
const SCROLL_LIMIT = 200;
|
|
141
|
+
|
|
142
|
+
export interface SpreadsheetEvents extends ComponentEvents {
|
|
143
|
+
click?: EvClick;
|
|
144
|
+
dblClick?: EvDblClick;
|
|
145
|
+
contextMenu?: EvContextMenu;
|
|
146
|
+
selectionChange?: EvSelectionChange;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface SpreadsheetProps extends ComponentProps {
|
|
150
|
+
footer?: boolean;
|
|
151
|
+
store: Store;
|
|
152
|
+
columns: GridColumn[];
|
|
153
|
+
|
|
154
|
+
click?: EventCallback<EvClick>;
|
|
155
|
+
dblClick?: EventCallback<EvDblClick>;
|
|
156
|
+
contextMenu?: EventCallback<EvContextMenu>;
|
|
157
|
+
selectionChange?: EventCallback<EvSelectionChange>;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* we can handle
|
|
162
|
+
* 4_095 cols and (1_048_575-1)/2 rows (this is a chrome limitation max pixels of scrollbars )
|
|
163
|
+
*/
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
*
|
|
167
|
+
*/
|
|
168
|
+
|
|
169
|
+
@class_ns("x4")
|
|
170
|
+
export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extends SpreadsheetEvents = SpreadsheetEvents> extends Component<P, E> {
|
|
171
|
+
|
|
172
|
+
private _columns: GridColumn[];
|
|
173
|
+
private _store: Store;
|
|
174
|
+
|
|
175
|
+
private _lock: number;
|
|
176
|
+
private _dirty: number;
|
|
177
|
+
|
|
178
|
+
private _row_height: number;
|
|
179
|
+
|
|
180
|
+
private _left: number;
|
|
181
|
+
private _top: number;
|
|
182
|
+
|
|
183
|
+
private _body: Component;
|
|
184
|
+
private _viewport: Component;
|
|
185
|
+
|
|
186
|
+
private _fheader: Box; // fixed col header
|
|
187
|
+
private _hheader: Box; // col header
|
|
188
|
+
private _vheader: Box; // vertical row header
|
|
189
|
+
private _ffooter: Box; // fixed footer
|
|
190
|
+
private _footer: Box; // footer
|
|
191
|
+
|
|
192
|
+
private _vis_rows: Map<number, { h: Component, r: Component }>;
|
|
193
|
+
private _start: number;
|
|
194
|
+
private _end: number;
|
|
195
|
+
|
|
196
|
+
private _selection: Set<number>;
|
|
197
|
+
private _num_fmt = new Intl.NumberFormat('fr-FR');
|
|
198
|
+
private _mny_fmt = new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR' });
|
|
199
|
+
private _dte_fmt = new Intl.DateTimeFormat('fr-FR', {});
|
|
200
|
+
|
|
201
|
+
private _has_fixed: boolean;
|
|
202
|
+
private _has_footer: boolean;
|
|
203
|
+
|
|
204
|
+
constructor(props: P) {
|
|
205
|
+
super(props);
|
|
206
|
+
|
|
207
|
+
this._lock = 0;
|
|
208
|
+
this._dirty = 0;
|
|
209
|
+
|
|
210
|
+
this._row_height = 32;
|
|
211
|
+
|
|
212
|
+
this._left = 0;
|
|
213
|
+
this._top = 0;
|
|
214
|
+
|
|
215
|
+
this._vis_rows = new Map();
|
|
216
|
+
this._selection = new Set();
|
|
217
|
+
this._has_fixed = false;
|
|
218
|
+
this._has_footer = props.footer;
|
|
219
|
+
|
|
220
|
+
this._columns = props.columns.map(x => x);
|
|
221
|
+
|
|
222
|
+
this.mapPropEvents(props, "click", "dblClick", "contextMenu", "selectionChange");
|
|
223
|
+
|
|
224
|
+
this.lock(true);
|
|
225
|
+
this.setAttribute("tabindex", 0);
|
|
226
|
+
|
|
227
|
+
this.addDOMEvent("created", () => {
|
|
228
|
+
this._init();
|
|
229
|
+
this._dirty = 1;
|
|
230
|
+
this.lock(false);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
this.addDOMEvent("resized", () => {
|
|
234
|
+
this._updateFlexs();
|
|
235
|
+
this._computeFullSize();
|
|
236
|
+
this._update(true);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
this.addDOMEvent("keydown", (e) => {
|
|
240
|
+
this._on_key(e);
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
if (props.store) {
|
|
244
|
+
this.setStore(props.store);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
*
|
|
251
|
+
*/
|
|
252
|
+
|
|
253
|
+
private _on_key(ev: KeyboardEvent) {
|
|
254
|
+
|
|
255
|
+
if (this.isDisabled()) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
switch (ev.key) {
|
|
260
|
+
case "ArrowDown": {
|
|
261
|
+
this.navigate(kbNav.next);
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
case "ArrowUp": {
|
|
266
|
+
this.navigate(kbNav.prev);
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
case "ArrowLeft": {
|
|
271
|
+
this.navigate(kbNav.left);
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
case "ArrowRight": {
|
|
276
|
+
this.navigate(kbNav.right);
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
case "Home": {
|
|
281
|
+
this.navigate(kbNav.first);
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
case "End": {
|
|
286
|
+
this.navigate(kbNav.last);
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
case "PageDown": {
|
|
291
|
+
this.navigate(kbNav.pgdn);
|
|
292
|
+
break;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
case "PageUp": {
|
|
296
|
+
this.navigate(kbNav.pgup);
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
default:
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
ev.preventDefault();
|
|
305
|
+
ev.stopPropagation();
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
*
|
|
310
|
+
*/
|
|
311
|
+
|
|
312
|
+
navigate(sens: kbNav) {
|
|
313
|
+
if (!this._selection.size) {
|
|
314
|
+
if (sens == kbNav.next || sens == kbNav.pgdn) {
|
|
315
|
+
sens = kbNav.first;
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
sens = kbNav.last;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const getLineSel = ( top: boolean ) => {
|
|
323
|
+
let m: number, M: number;
|
|
324
|
+
let col: number;
|
|
325
|
+
|
|
326
|
+
this._selection.forEach( x => {
|
|
327
|
+
const row = x>>12;
|
|
328
|
+
if( m===undefined || m>row ) { m = row; col=x&0xff; }
|
|
329
|
+
if( M===undefined || M<row ) { M = row; col=x&0xff; }
|
|
330
|
+
} );
|
|
331
|
+
|
|
332
|
+
return [top ? m : M, col]
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (sens == kbNav.first || sens == kbNav.last) {
|
|
336
|
+
let nline = sens == kbNav.first ? 0 : this._store.getRowCount() - 1;
|
|
337
|
+
this._clearSelection(false);
|
|
338
|
+
this._addSelection(mkid(nline,0), true);
|
|
339
|
+
this._scrollToIndex(nline);
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
342
|
+
else if (sens == kbNav.prev || sens == kbNav.next) {
|
|
343
|
+
|
|
344
|
+
const [fline,col] = getLineSel( sens == kbNav.prev );
|
|
345
|
+
|
|
346
|
+
let nline = sens == kbNav.next ? fline + 1 : fline - 1;
|
|
347
|
+
if (nline >= 0 && nline < this._store.getRowCount()) {
|
|
348
|
+
this._clearSelection(false);
|
|
349
|
+
this._addSelection( mkid(nline,col), true);
|
|
350
|
+
this._scrollToIndex( nline );
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
else if (sens == kbNav.pgdn || sens == kbNav.pgup) {
|
|
355
|
+
const pgh = this._vis_rows.size;
|
|
356
|
+
const [fline,col] = getLineSel( sens == kbNav.pgup );
|
|
357
|
+
|
|
358
|
+
let sby = sens == kbNav.pgdn ? pgh : -pgh;
|
|
359
|
+
let nline = fline + sby;
|
|
360
|
+
|
|
361
|
+
if (nline < 0) {
|
|
362
|
+
nline = 0;
|
|
363
|
+
}
|
|
364
|
+
else if (nline >= this._store.getRowCount()) {
|
|
365
|
+
nline = this._store.getRowCount() - 1;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (nline != fline) {
|
|
369
|
+
this._clearSelection( false );
|
|
370
|
+
this._addSelection(mkid(nline,col), true);
|
|
371
|
+
|
|
372
|
+
if (this._store.getRowCount() < SCROLL_LIMIT) {
|
|
373
|
+
sby *= this._row_height;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
this._viewport.dom.scrollBy(0, sby);
|
|
377
|
+
return true;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
else if( sens==kbNav.left || sens==kbNav.right ) {
|
|
381
|
+
const [fline,col] = getLineSel( sens == kbNav.left );
|
|
382
|
+
|
|
383
|
+
let ncol = sens == kbNav.right ? col+1 : col-1;
|
|
384
|
+
if (ncol >= 0 && ncol < this._columns.length ) {
|
|
385
|
+
this._clearSelection(false);
|
|
386
|
+
this._addSelection( mkid(fline,ncol), true);
|
|
387
|
+
//this._scrollToIndex( nline );
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
*
|
|
397
|
+
*/
|
|
398
|
+
|
|
399
|
+
private _scrollToIndex(index: number, block = 'nearest') {
|
|
400
|
+
|
|
401
|
+
// is it already visible ?
|
|
402
|
+
let ref = mkid(index,0);
|
|
403
|
+
let rows = this.queryAll(`.cell[data-ref="${ref}"]`);
|
|
404
|
+
if (rows.length) {
|
|
405
|
+
rows[0].scrollIntoView({ block: block as any });
|
|
406
|
+
}
|
|
407
|
+
// nope, refill
|
|
408
|
+
else {
|
|
409
|
+
let top = index;
|
|
410
|
+
if (this._store.getRowCount() < SCROLL_LIMIT) {
|
|
411
|
+
top *= this._row_height;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
this._viewport.dom.scrollTo(0, top);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
*
|
|
420
|
+
*/
|
|
421
|
+
|
|
422
|
+
setStore(store: Store) {
|
|
423
|
+
|
|
424
|
+
const on_change = (ev: EvChange) => {
|
|
425
|
+
if (!this._viewport) {
|
|
426
|
+
// not created
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
//if (ev.change_type == 'change') {
|
|
431
|
+
this._selection.clear();
|
|
432
|
+
//}
|
|
433
|
+
|
|
434
|
+
this._updateFlexs();
|
|
435
|
+
this._computeFullSize();
|
|
436
|
+
this._update(true);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// unlink previous observer
|
|
440
|
+
if (this._store) {
|
|
441
|
+
this._store.off('changed', on_change);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (store) {
|
|
445
|
+
this._store = store;
|
|
446
|
+
this._store.on('changed', on_change);
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
this._store = null;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
*
|
|
455
|
+
*/
|
|
456
|
+
|
|
457
|
+
lock(lock: boolean) {
|
|
458
|
+
if (lock) {
|
|
459
|
+
this._lock++;
|
|
460
|
+
}
|
|
461
|
+
else {
|
|
462
|
+
if (--this._lock == 0 && this._dirty) {
|
|
463
|
+
this._update(true);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
private _getColCount() {
|
|
469
|
+
return this._columns.length;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
private _getCol(index: number) {
|
|
473
|
+
return this._columns[index];
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
*
|
|
478
|
+
*/
|
|
479
|
+
|
|
480
|
+
private _buildColHeader(fixed: boolean) {
|
|
481
|
+
// row header
|
|
482
|
+
const els: Component[] = [];
|
|
483
|
+
|
|
484
|
+
const count = this._getColCount();
|
|
485
|
+
for (let col = 0; col < count; col++) {
|
|
486
|
+
const cdata = this._getCol(col);
|
|
487
|
+
if ((!!cdata.fixed) != fixed) {
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const sizer = new CSizer("right");
|
|
492
|
+
|
|
493
|
+
sizer.on("stop", () => {
|
|
494
|
+
this._updateFlexs();
|
|
495
|
+
})
|
|
496
|
+
|
|
497
|
+
sizer.on("resize", (ev) => {
|
|
498
|
+
cdata.width = ev.size;
|
|
499
|
+
cdata.flex = 0;
|
|
500
|
+
|
|
501
|
+
const cols = this.queryAll(`[data-col="${col}"]`)
|
|
502
|
+
cols.forEach(c => {
|
|
503
|
+
c.setStyleValue("width", ev.size + "px");
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
const rh = header.getBoundingRect();
|
|
507
|
+
|
|
508
|
+
if (!fixed) {
|
|
509
|
+
this._body.setStyleValue("width", rh.width + "px");
|
|
510
|
+
}
|
|
511
|
+
else {
|
|
512
|
+
this.setStyleVariable("--fixed-width", rh.width + "px");
|
|
513
|
+
}
|
|
514
|
+
})
|
|
515
|
+
|
|
516
|
+
const cell = new Component({
|
|
517
|
+
cls: `cell`,
|
|
518
|
+
attrs: { "data-col": col },
|
|
519
|
+
style: { width: cdata.width ? cdata.width + "px" : undefined },
|
|
520
|
+
content: [
|
|
521
|
+
new SimpleText({ text: cdata.title, align: cdata.header_align ?? "left" }),
|
|
522
|
+
new Component({ cls: "sorter" }),
|
|
523
|
+
sizer
|
|
524
|
+
]
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
/*
|
|
528
|
+
cell.addDOMEvent("touchend", () => {
|
|
529
|
+
const last = cell.getInternalData("touchend");
|
|
530
|
+
const now = Date.now();
|
|
531
|
+
const delta = last ? now - last : 0;
|
|
532
|
+
if (delta > 30 && delta < 300) {
|
|
533
|
+
this._sortCol(col);
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
cell.setInternalData("touchend", now);
|
|
537
|
+
}
|
|
538
|
+
})
|
|
539
|
+
|
|
540
|
+
cell.addDOMEvent("dblclick", () => {
|
|
541
|
+
this._sortCol(col);
|
|
542
|
+
});
|
|
543
|
+
*/
|
|
544
|
+
|
|
545
|
+
els.push(cell);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
if (fixed && els.length == 0) {
|
|
549
|
+
return null;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const header = new Box({ cls: "col-header", content: els });
|
|
553
|
+
header.setClass("fixed", fixed);
|
|
554
|
+
|
|
555
|
+
return header;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
*
|
|
560
|
+
*/
|
|
561
|
+
|
|
562
|
+
private _buildColFooter(fixed: boolean) {
|
|
563
|
+
// row header
|
|
564
|
+
const els: Component[] = [];
|
|
565
|
+
|
|
566
|
+
const count = this._getColCount();
|
|
567
|
+
for (let col = 0; col < count; col++) {
|
|
568
|
+
const cdata = this._getCol(col);
|
|
569
|
+
if ((!!cdata.fixed) != fixed) {
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const cell = new Component({
|
|
574
|
+
cls: `cell`,
|
|
575
|
+
attrs: { "data-col": col },
|
|
576
|
+
style: { width: cdata.width ? cdata.width + "px" : undefined },
|
|
577
|
+
content: [
|
|
578
|
+
new SimpleText({ text: cdata.footer_val }),
|
|
579
|
+
]
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
/*
|
|
583
|
+
cell.addDOMEvent("dblclick", () => {
|
|
584
|
+
this._sortCol(col);
|
|
585
|
+
});
|
|
586
|
+
*/
|
|
587
|
+
|
|
588
|
+
els.push(cell);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
if (fixed && els.length == 0) {
|
|
592
|
+
return null;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
const header = new Box({ cls: "col-footer", content: els });
|
|
596
|
+
header.setClass("fixed", fixed);
|
|
597
|
+
|
|
598
|
+
return header;
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
*
|
|
602
|
+
*/
|
|
603
|
+
|
|
604
|
+
private _renderCell(row: number, column: GridColumn, extra_cls: string[]): ComponentContent {
|
|
605
|
+
|
|
606
|
+
const col = column.id;
|
|
607
|
+
const type = column.type;
|
|
608
|
+
|
|
609
|
+
let data = this._store.getData( row, col );
|
|
610
|
+
if (data === undefined || data === null) {
|
|
611
|
+
return null;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
let cls = "";
|
|
615
|
+
//if( column.classifier ) {
|
|
616
|
+
// extra_cls.push( column.classifier( data, rec, col ) );
|
|
617
|
+
//}
|
|
618
|
+
|
|
619
|
+
//if (data instanceof Function) {
|
|
620
|
+
// return data(rec, col);
|
|
621
|
+
//}
|
|
622
|
+
|
|
623
|
+
if (column.formatter) {
|
|
624
|
+
return column.formatter(data);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
switch (type) {
|
|
628
|
+
case "checkbox": {
|
|
629
|
+
if (data) {
|
|
630
|
+
return new Icon({ cls: "cell-check" + cls, iconId: check_icon });
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
return undefined;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
case "image": {
|
|
637
|
+
if (isString(data)) {
|
|
638
|
+
return new Image({ cls, src: data, fit: "scale-down" });
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
return undefined;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
case "number": {
|
|
645
|
+
if (!isNumber(data)) {
|
|
646
|
+
return "NaN";
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
data = this._num_fmt.format(data as number);
|
|
650
|
+
break;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
case "money": {
|
|
654
|
+
if (!isNumber(data)) {
|
|
655
|
+
return "NaN";
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
data = this._mny_fmt.format(data as number);
|
|
659
|
+
break;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
case "percent": {
|
|
663
|
+
return new Box({
|
|
664
|
+
cls: "percent" + cls,
|
|
665
|
+
content: new Component({ cls: "bar", width: data + "%" })
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
case "icon": {
|
|
670
|
+
return new Icon({ cls, iconId: data + "" });
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
case "date": {
|
|
674
|
+
data = this._dte_fmt.format(data as Date);
|
|
675
|
+
break;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
default: {
|
|
679
|
+
data = data + "";
|
|
680
|
+
break;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
return new Component({ tag: "span", cls, content: data });
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
*
|
|
689
|
+
*/
|
|
690
|
+
|
|
691
|
+
private _buildRow(rowid: number, top: number) {
|
|
692
|
+
|
|
693
|
+
const els: Component[] = [];
|
|
694
|
+
const count = this._getColCount();
|
|
695
|
+
|
|
696
|
+
for (let col = 0; col < count; col++) {
|
|
697
|
+
const cdata = this._getCol(col);
|
|
698
|
+
if (cdata.fixed) {
|
|
699
|
+
continue;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
const extra: string[] = []
|
|
703
|
+
const content = this._renderCell(rowid, cdata, extra);
|
|
704
|
+
|
|
705
|
+
const el = new Component({
|
|
706
|
+
cls: "cell",
|
|
707
|
+
attrs: { "data-col": col },
|
|
708
|
+
style: { width: cdata?.width ? cdata.width + "px" : undefined },
|
|
709
|
+
content
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
switch (cdata.align) {
|
|
713
|
+
case "center": el.addClass( "align-center" ); break;
|
|
714
|
+
case "right": el.addClass( "align-right" ); break;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
if (extra.length) {
|
|
718
|
+
el.addClass(extra.join(' '));
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
if (cdata.type) {
|
|
722
|
+
el.addClass(cdata.type);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
const ref = mkid(rowid, col);
|
|
726
|
+
if (this._selection.has(ref)) {
|
|
727
|
+
el.addClass("selected");
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
el.setInternalData("col", col);
|
|
731
|
+
el.setInternalData("row", rowid)
|
|
732
|
+
el.setData("ref", ref + "");
|
|
733
|
+
|
|
734
|
+
els.push(el);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
return new Box({ cls: "row", style: { top: top.toFixed(2) + "px" }, content: els });
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
*
|
|
742
|
+
*/
|
|
743
|
+
|
|
744
|
+
private _buildRowHeader(rowid: number, top: number) {
|
|
745
|
+
|
|
746
|
+
const cols: Component[] = [];
|
|
747
|
+
const count = this._getColCount();
|
|
748
|
+
|
|
749
|
+
for (let col = 0; col < count; col++) {
|
|
750
|
+
const cdata = this._getCol(col);
|
|
751
|
+
if (!cdata?.fixed) {
|
|
752
|
+
continue;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
const content = this._renderCell(rowid, cdata, [cdata.type]);
|
|
756
|
+
|
|
757
|
+
let align = "start";
|
|
758
|
+
switch (cdata.align) {
|
|
759
|
+
default: align = "start"; break;
|
|
760
|
+
case "center": align = "center"; break;
|
|
761
|
+
case "right": align = "end"; break;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
const el = new Component({
|
|
765
|
+
cls: "cell",
|
|
766
|
+
style: { width: cdata?.width ? cdata.width + "px" : undefined, justifyContent: align },
|
|
767
|
+
content
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
if (cdata.type) {
|
|
771
|
+
el.addClass(cdata.type);
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
el.setInternalData("col", col);
|
|
775
|
+
el.setInternalData("row", rowid);
|
|
776
|
+
el.setData("ref", mkid(rowid, col) + "");
|
|
777
|
+
|
|
778
|
+
if (this._selection.has(mkid(col, rowid))) {
|
|
779
|
+
el.addClass("selected");
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
cols.push(el);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
return new Box({ cls: "row", style: { top: top + "px" }, content: cols });
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
*
|
|
792
|
+
*/
|
|
793
|
+
|
|
794
|
+
private _updateFlexs() {
|
|
795
|
+
let maxw = 0;
|
|
796
|
+
let flexc = 0;
|
|
797
|
+
|
|
798
|
+
const ccount = this._getColCount();
|
|
799
|
+
|
|
800
|
+
for (let x = 0; x < ccount; x++) {
|
|
801
|
+
const cdata = this._getCol(x);
|
|
802
|
+
|
|
803
|
+
if (!cdata.fixed && cdata.flex) {
|
|
804
|
+
flexc += cdata.flex;
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
maxw += cdata.width;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
if (flexc) {
|
|
812
|
+
const width = this._viewport.dom.clientWidth;
|
|
813
|
+
const delta = width - maxw;
|
|
814
|
+
const fw = delta / flexc;
|
|
815
|
+
|
|
816
|
+
for (let col = 0; col < ccount; col++) {
|
|
817
|
+
const cdata = this._getCol(col);
|
|
818
|
+
if (!cdata.fixed && cdata.flex) {
|
|
819
|
+
cdata.width = Math.max(cdata.flex * fw, 32);
|
|
820
|
+
|
|
821
|
+
const cols = this.queryAll(`[data-col="${col}"]`)
|
|
822
|
+
cols.forEach(c => {
|
|
823
|
+
c.setStyleValue("width", cdata.width + "px");
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
/**
|
|
831
|
+
*
|
|
832
|
+
*/
|
|
833
|
+
|
|
834
|
+
private _computeFullSize() {
|
|
835
|
+
|
|
836
|
+
let maxw = 0;
|
|
837
|
+
let maxfw = 0;
|
|
838
|
+
|
|
839
|
+
const ccount = this._getColCount();
|
|
840
|
+
|
|
841
|
+
for (let x = 0; x < ccount; x++) {
|
|
842
|
+
const cdata = this._getCol(x);
|
|
843
|
+
let w = 0;
|
|
844
|
+
|
|
845
|
+
if (cdata.fixed) {
|
|
846
|
+
this._has_fixed = true;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
if (cdata.width) {
|
|
850
|
+
w += cdata.width;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
if (cdata.fixed) {
|
|
854
|
+
maxfw += w;
|
|
855
|
+
}
|
|
856
|
+
else {
|
|
857
|
+
maxw += w;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
const maxr = this._store ? this._store.getRowCount() : 0;
|
|
862
|
+
let maxh = maxr;
|
|
863
|
+
|
|
864
|
+
if (maxr < SCROLL_LIMIT) {
|
|
865
|
+
maxh *= this._row_height;
|
|
866
|
+
}
|
|
867
|
+
else {
|
|
868
|
+
const height = this._body.dom.parentElement.clientHeight;
|
|
869
|
+
const npage = height / this._row_height;
|
|
870
|
+
maxh = maxr - Math.floor(npage) + npage * this._row_height;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
this.setStyleVariable("--fixed-width", maxfw + "px");
|
|
874
|
+
this._body.setStyleValue("height", maxh + "px");
|
|
875
|
+
this._body.setStyleValue("width", maxw + "px");
|
|
876
|
+
this._vheader.setStyleValue("height", maxh + "px");
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/**
|
|
880
|
+
*
|
|
881
|
+
*/
|
|
882
|
+
|
|
883
|
+
private _init() {
|
|
884
|
+
this._body = new Component({ cls: "body" });
|
|
885
|
+
|
|
886
|
+
this._viewport = new Viewport({ content: this._body });
|
|
887
|
+
|
|
888
|
+
if (!this._has_footer) {
|
|
889
|
+
this.setStyleVariable("--footer-height", "0");
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// SCROLL
|
|
893
|
+
this._viewport.addDOMEvent("scroll", (ev) => {
|
|
894
|
+
// sync horz & vert elements
|
|
895
|
+
this._left = this._viewport.dom.scrollLeft;
|
|
896
|
+
this.setStyleVariable("--left", -this._left + "px");
|
|
897
|
+
|
|
898
|
+
this._top = this._viewport.dom.scrollTop;
|
|
899
|
+
this.setStyleVariable("--top", -this._top + "px");
|
|
900
|
+
|
|
901
|
+
//this.setTimeout( "update", 0, ( ) => this._update( ) );
|
|
902
|
+
this._update()
|
|
903
|
+
});
|
|
904
|
+
|
|
905
|
+
// WHEEL
|
|
906
|
+
this.addDOMEvent("wheel", (ev: WheelEvent) => {
|
|
907
|
+
if (ev.deltaY && this._store && this._store.getRowCount() >= SCROLL_LIMIT) {
|
|
908
|
+
this._viewport.dom.scrollBy(0, ev.deltaY < 0 ? -1 : 1);
|
|
909
|
+
ev.stopPropagation();
|
|
910
|
+
ev.preventDefault();
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
if (this._has_fixed && ev.deltaY) {
|
|
914
|
+
// wheel on fixed part
|
|
915
|
+
// fixed part do not have scrollbar, so we need to handle it by hand
|
|
916
|
+
let t = ev.target as Node;
|
|
917
|
+
while (t != this.dom) {
|
|
918
|
+
if (t == this._vheader.dom) {
|
|
919
|
+
this._viewport.dom.scrollBy(0, ev.deltaY < 0 ? -this._row_height : this._row_height);
|
|
920
|
+
ev.stopPropagation();
|
|
921
|
+
ev.preventDefault();
|
|
922
|
+
break;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
t = t.parentNode;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
})
|
|
929
|
+
|
|
930
|
+
const targetCell = (e: MouseEvent) => {
|
|
931
|
+
let el = e.target as Element;
|
|
932
|
+
while (el && !el.classList.contains("cell")) {
|
|
933
|
+
el = el.parentElement;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
if (el) {
|
|
937
|
+
const cel = componentFromDOM(el);
|
|
938
|
+
return {
|
|
939
|
+
ref: cel.getIntData("ref"),
|
|
940
|
+
row: cel.getInternalData("row"),
|
|
941
|
+
col: cel.getInternalData("col"),
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
return undefined;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
|
|
949
|
+
// CLICK
|
|
950
|
+
this.addDOMEvent("click", (e) => {
|
|
951
|
+
const ref = targetCell(e);
|
|
952
|
+
if (ref) {
|
|
953
|
+
//TODO: multiselection
|
|
954
|
+
if (!this._selection.has(ref.ref)) {
|
|
955
|
+
this._clearSelection( false );
|
|
956
|
+
this._addSelection(ref.ref,true);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
// DBLCLICK
|
|
962
|
+
this.addDOMEvent("dblclick", (e) => {
|
|
963
|
+
const ref = targetCell(e);
|
|
964
|
+
if (ref) {
|
|
965
|
+
//TODO: multiselection
|
|
966
|
+
if (!this._selection.has(ref.ref)) {
|
|
967
|
+
this._clearSelection(false);
|
|
968
|
+
this._addSelection(ref.ref,true);
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
this._on_dblclk(e, ref.row, ref.col);
|
|
972
|
+
|
|
973
|
+
debugger;
|
|
974
|
+
//const rec = this._dataview.getByIndex( row );
|
|
975
|
+
//this.fire( "dblClick", { context: rec } );
|
|
976
|
+
}
|
|
977
|
+
});
|
|
978
|
+
|
|
979
|
+
// CONTEXT
|
|
980
|
+
this.addDOMEvent("contextmenu", (e) => {
|
|
981
|
+
const ref = targetCell(e);
|
|
982
|
+
if (ref) {
|
|
983
|
+
//TODO: multiselection
|
|
984
|
+
if (!this._selection.has(ref.ref)) {
|
|
985
|
+
this._clearSelection( false );
|
|
986
|
+
this._addSelection(ref.ref, true );
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
debugger;
|
|
990
|
+
//const rec = this._dataview.getByIndex( row );
|
|
991
|
+
//this.fire( "contextMenu", { uievent: e, context: rec } );
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
e.preventDefault();
|
|
995
|
+
e.stopPropagation();
|
|
996
|
+
});
|
|
997
|
+
|
|
998
|
+
this._updateFlexs();
|
|
999
|
+
|
|
1000
|
+
this._fheader = this._buildColHeader(true);
|
|
1001
|
+
this._hheader = this._buildColHeader(false);
|
|
1002
|
+
this._vheader = new Box({ cls: "row-header" })
|
|
1003
|
+
|
|
1004
|
+
if (this._has_footer) {
|
|
1005
|
+
this._ffooter = this._buildColFooter(true);
|
|
1006
|
+
this._footer = this._buildColFooter(false);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
this.setContent([this._viewport, this._fheader, this._hheader, this._ffooter, this._footer, this._vheader]);
|
|
1010
|
+
|
|
1011
|
+
// compute misc variables
|
|
1012
|
+
{
|
|
1013
|
+
const rh = this.getStyleVariable("--row-height");
|
|
1014
|
+
this._row_height = parseInt(rh);
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
this._computeFullSize();
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
/**
|
|
1021
|
+
*
|
|
1022
|
+
*/
|
|
1023
|
+
|
|
1024
|
+
protected _on_dblclk(e: UIEvent, row: number, col: number) {
|
|
1025
|
+
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
/**
|
|
1029
|
+
*
|
|
1030
|
+
*/
|
|
1031
|
+
|
|
1032
|
+
private _update(force = false) {
|
|
1033
|
+
|
|
1034
|
+
if (!this._lock) {
|
|
1035
|
+
const rc = this.getBoundingRect();
|
|
1036
|
+
|
|
1037
|
+
// rows
|
|
1038
|
+
const rowc = this._store ? this._store.getRowCount() : 0;
|
|
1039
|
+
const mul = rowc < SCROLL_LIMIT ? this._row_height : 1;
|
|
1040
|
+
|
|
1041
|
+
const start = Math.floor(this._top / mul);
|
|
1042
|
+
const end = start + Math.ceil(rc.height / this._row_height);
|
|
1043
|
+
const hasFixed = this._has_fixed;
|
|
1044
|
+
|
|
1045
|
+
if (this._start != start || this._end != end || force) {
|
|
1046
|
+
|
|
1047
|
+
const rows: Component[] = [];
|
|
1048
|
+
const headers: Component[] = [];
|
|
1049
|
+
|
|
1050
|
+
if (force) {
|
|
1051
|
+
this._vis_rows.clear();
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
let newvis: typeof this._vis_rows = new Map();
|
|
1055
|
+
|
|
1056
|
+
let y = start * mul;
|
|
1057
|
+
|
|
1058
|
+
for (let row = start; row < end && row < rowc; row++, y += this._row_height) {
|
|
1059
|
+
|
|
1060
|
+
let el = this._vis_rows.get(row);
|
|
1061
|
+
//const rec = this._store.getByIndex(row);
|
|
1062
|
+
|
|
1063
|
+
if (hasFixed) {
|
|
1064
|
+
if (!el) {
|
|
1065
|
+
el = {
|
|
1066
|
+
h: this._buildRowHeader(row, y),
|
|
1067
|
+
r: this._buildRow(row, y),
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
else {
|
|
1071
|
+
el.h.setStyleValue("top", y + "px");
|
|
1072
|
+
el.r.setStyleValue("top", y + "px");
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
headers.push(el.h);
|
|
1076
|
+
}
|
|
1077
|
+
else {
|
|
1078
|
+
if (!el) {
|
|
1079
|
+
el = { h: null, r: this._buildRow(row, y), };
|
|
1080
|
+
}
|
|
1081
|
+
else {
|
|
1082
|
+
el.r.setStyleValue("top", y + "px");
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
rows.push(el.r);
|
|
1087
|
+
newvis.set(row, el);
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
if (hasFixed) {
|
|
1091
|
+
headers.push(new Component({ cls: "cell-out", style: { top: y + "px" } }));
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
this._vis_rows = newvis;
|
|
1095
|
+
this._start = start;
|
|
1096
|
+
this._end = end;
|
|
1097
|
+
|
|
1098
|
+
this._body.setContent(rows);
|
|
1099
|
+
|
|
1100
|
+
if (hasFixed) {
|
|
1101
|
+
this._vheader.removeClass("@hidden");
|
|
1102
|
+
this._vheader.setContent(headers);
|
|
1103
|
+
}
|
|
1104
|
+
else {
|
|
1105
|
+
this._vheader.addClass("@hidden");
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
/**
|
|
1111
|
+
*
|
|
1112
|
+
*/
|
|
1113
|
+
|
|
1114
|
+
private _clearSelection(notify = true) {
|
|
1115
|
+
for (const ref of this._selection.keys()) {
|
|
1116
|
+
const els = this.queryAll(`.cell[data-ref="${ref}"]`)
|
|
1117
|
+
els.forEach(el => {
|
|
1118
|
+
el.removeClass("selected");
|
|
1119
|
+
})
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
this._selection.clear();
|
|
1123
|
+
|
|
1124
|
+
if (notify) {
|
|
1125
|
+
this.fire("selectionChange", { selection: [], empty: true });
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
/**
|
|
1130
|
+
*
|
|
1131
|
+
*/
|
|
1132
|
+
|
|
1133
|
+
private _addSelection(ref: number, notify = true) {
|
|
1134
|
+
this._selection.add(ref)
|
|
1135
|
+
|
|
1136
|
+
const els = this.queryAll(`.cell[data-ref="${ref}"]`)
|
|
1137
|
+
els.forEach(el => {
|
|
1138
|
+
el.addClass("selected");
|
|
1139
|
+
});
|
|
1140
|
+
|
|
1141
|
+
if (notify) {
|
|
1142
|
+
const selection = this.getSelection();
|
|
1143
|
+
this.fire("selectionChange", { selection, empty: selection.length != 0 });
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
/**
|
|
1148
|
+
*
|
|
1149
|
+
*/
|
|
1150
|
+
|
|
1151
|
+
getSelection(): CellRef[] {
|
|
1152
|
+
const selection: CellRef[] = [];
|
|
1153
|
+
|
|
1154
|
+
this._selection.forEach(x => {
|
|
1155
|
+
selection.push({
|
|
1156
|
+
row: x >> 12,
|
|
1157
|
+
col: x & 0xfff,
|
|
1158
|
+
})
|
|
1159
|
+
});
|
|
1160
|
+
|
|
1161
|
+
return selection;
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
/**
|
|
1165
|
+
*
|
|
1166
|
+
*/
|
|
1167
|
+
|
|
1168
|
+
selectItem(row: number, col: number, append = false) {
|
|
1169
|
+
if (!append) {
|
|
1170
|
+
this._clearSelection(false);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
this._addSelection(mkid(row, col), true);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
|