x4js 2.3.17 → 2.3.18
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/aicontext.md +163 -25
- package/package.json +1 -1
- package/src/components/gridview/gridview.ts +350 -175
- package/src/components/keyboard/keyboard.ts +181 -65
- package/src/components/spreadsheet/spreadsheet.ts +27 -1
- package/src/components/tag/tag.ts +1 -2
- package/src/components/textarea/textarea.module.scss +6 -0
- package/src/components/textarea/textarea.ts +48 -10
- package/src/core/component.ts +16 -9
- package/src/core/core_clipboard.ts +239 -0
- package/src/core/core_data.ts +54 -23
- package/src/core/core_dom.ts +27 -5
- package/src/core/core_dragdrop.ts +11 -5
- package/src/core/core_element.ts +1 -1
- package/src/core/core_shortcuts.ts +1 -1
- package/src/x4.d.ts +1 -0
- package/src/x4.ts +1 -0
|
@@ -80,6 +80,12 @@ export interface GridviewProps extends ComponentProps {
|
|
|
80
80
|
columns: GridColumn[];
|
|
81
81
|
emptyMsg?: string;
|
|
82
82
|
|
|
83
|
+
/**
|
|
84
|
+
* several rows can be selected: Ctrl (Cmd on a Mac) + click adds a row or
|
|
85
|
+
* removes it, Shift + click selects up to the row, Ctrl + A selects all
|
|
86
|
+
*/
|
|
87
|
+
multisel?: boolean;
|
|
88
|
+
|
|
83
89
|
click?: EventCallback<EvClick>;
|
|
84
90
|
dblClick?: EventCallback<EvDblClick>;
|
|
85
91
|
contextMenu?: EventCallback<EvContextMenu>;
|
|
@@ -155,7 +161,7 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
|
|
|
155
161
|
private _start: number;
|
|
156
162
|
private _end: number;
|
|
157
163
|
|
|
158
|
-
private _selection: Set<
|
|
164
|
+
private _selection: Set<any>; // the ids of the selected records: a row changes place when the view is sorted
|
|
159
165
|
|
|
160
166
|
|
|
161
167
|
// TODO: that
|
|
@@ -195,11 +201,26 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
|
|
|
195
201
|
this.setAttribute("tabindex", 0);
|
|
196
202
|
|
|
197
203
|
this.addDOMEvent("created", () => {
|
|
204
|
+
// back in the document: the view follows its store again (see "removed")
|
|
205
|
+
if (this._dataview) {
|
|
206
|
+
this._dataview.open( );
|
|
207
|
+
}
|
|
208
|
+
|
|
198
209
|
this._init();
|
|
199
210
|
this._dirty = 1;
|
|
200
211
|
this.lock(false);
|
|
201
212
|
});
|
|
202
213
|
|
|
214
|
+
// out of the document: its store must not keep the grid alive, nor redraw it
|
|
215
|
+
this.addDOMEvent("removed", () => {
|
|
216
|
+
if (this._dataview) {
|
|
217
|
+
this._dataview.close( );
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// as before its first insertion: nothing is drawn out of the document
|
|
221
|
+
this.lock( true );
|
|
222
|
+
});
|
|
223
|
+
|
|
203
224
|
this.addDOMEvent("resized", () => {
|
|
204
225
|
this._updateFlexs( );
|
|
205
226
|
this._computeFullSize( );
|
|
@@ -210,6 +231,200 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
|
|
|
210
231
|
this._on_key( e );
|
|
211
232
|
})
|
|
212
233
|
|
|
234
|
+
// the handlers of the grid itself are put once, here. _init builds what is
|
|
235
|
+
// inside the grid, each time it comes in the document
|
|
236
|
+
|
|
237
|
+
// WHEEL
|
|
238
|
+
this.addDOMEvent("wheel", (ev: WheelEvent) => {
|
|
239
|
+
// many rows: one pixel of the scroll bar is one row, wherever the pointer is
|
|
240
|
+
if (ev.deltaY && this._dataview && this._dataview.getCount() >= SCROLL_LIMIT) {
|
|
241
|
+
this._viewport.dom.scrollBy(0, ev.deltaY < 0 ? -1 : 1);
|
|
242
|
+
ev.stopPropagation();
|
|
243
|
+
ev.preventDefault();
|
|
244
|
+
}
|
|
245
|
+
else if (this._has_fixed && ev.deltaY) {
|
|
246
|
+
// wheel on fixed part
|
|
247
|
+
// fixed part do not have scrollbar, so we need to handle it by hand
|
|
248
|
+
let t = ev.target as Node;
|
|
249
|
+
while (t != this.dom) {
|
|
250
|
+
if (t == this._vheader.dom) {
|
|
251
|
+
this._viewport.dom.scrollBy(0, ev.deltaY < 0 ? -this._row_height : this._row_height);
|
|
252
|
+
ev.stopPropagation();
|
|
253
|
+
ev.preventDefault();
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
t = t.parentNode;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
const targetRow = ( e: MouseEvent ) => {
|
|
263
|
+
let el = Component.parentElement(e.target as HTMLElement, Component);
|
|
264
|
+
while (el && !el.hasClass("row")) {
|
|
265
|
+
el = el.parentElement();
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (el) {
|
|
269
|
+
return el.getIntData("row");
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return undefined;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const inColHeader = ( e: MouseEvent ) => {
|
|
276
|
+
let el = Component.parentElement(e.target as HTMLElement, Component);
|
|
277
|
+
while (el && !el.hasClass("col-header")) {
|
|
278
|
+
el = el.parentElement();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return !!el;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// multisel, Ctrl + click: one row more, or one row less
|
|
285
|
+
const toggleRow = ( row: number ) => {
|
|
286
|
+
const id = this._dataview.getIdByIndex( row );
|
|
287
|
+
if( !this._selection.has( id ) ) {
|
|
288
|
+
this._addSelection( row );
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
this._selection.delete( id );
|
|
293
|
+
this.queryAll(`.row[data-row="${row}"]`).forEach(el => {
|
|
294
|
+
el.removeClass("selected");
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
this.fire("selectionChange", { selection: this.getSelection( ) ?? [], empty: this._selection.size==0 } );
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// multisel, Shift + click: from the row that was selected last to this one
|
|
301
|
+
const selectTo = ( row: number ) => {
|
|
302
|
+
let last: any;
|
|
303
|
+
for (const id of this._selection) {
|
|
304
|
+
last = id;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const from = this._dataview.indexOfId( last );
|
|
308
|
+
|
|
309
|
+
this._clearSelection( false );
|
|
310
|
+
|
|
311
|
+
// the row to start from is added last: the next Shift + click starts from it too
|
|
312
|
+
if( from<0 ) {
|
|
313
|
+
this._addSelection( row, false );
|
|
314
|
+
}
|
|
315
|
+
else if( from<=row ) {
|
|
316
|
+
for( let r=row; r>=from; r-- ) {
|
|
317
|
+
this._addSelection( r, false );
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
for( let r=row; r<=from; r++ ) {
|
|
322
|
+
this._addSelection( r, false );
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
this.fire("selectionChange", { selection: this.getSelection( ), empty: false } );
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
// CLICK
|
|
331
|
+
this.addDOMEvent("click", (e) => {
|
|
332
|
+
const row = targetRow( e );
|
|
333
|
+
if (row!==undefined ) {
|
|
334
|
+
if( this.props.multisel && ( e.ctrlKey || e.metaKey ) ) {
|
|
335
|
+
toggleRow( row );
|
|
336
|
+
}
|
|
337
|
+
else if( this.props.multisel && e.shiftKey && this._selection.size ) {
|
|
338
|
+
selectTo( row );
|
|
339
|
+
}
|
|
340
|
+
// this row alone
|
|
341
|
+
else if( this._selection.size!=1 || !this._selection.has( this._dataview.getIdByIndex( row ) ) ) {
|
|
342
|
+
this._clearSelection( false );
|
|
343
|
+
this._addSelection(row);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// once the selection is what the click made it
|
|
347
|
+
const rec = this._dataview.getByIndex( row );
|
|
348
|
+
this.fire( "click", { context: rec } );
|
|
349
|
+
}
|
|
350
|
+
// a click in a column header sorts or sizes it: what is selected stays
|
|
351
|
+
else if( !inColHeader( e ) ) {
|
|
352
|
+
this._clearSelection( true );
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// DBLCLICK
|
|
357
|
+
this.addDOMEvent("dblclick", (e) => {
|
|
358
|
+
const row = targetRow( e );
|
|
359
|
+
if (row!==undefined ) {
|
|
360
|
+
if( !this._selection.has( this._dataview.getIdByIndex( row ) ) ) {
|
|
361
|
+
this._clearSelection( false );
|
|
362
|
+
this._addSelection(row);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
this._on_dblclk( e, row );
|
|
366
|
+
|
|
367
|
+
const rec = this._dataview.getByIndex( row );
|
|
368
|
+
this.fire( "dblClick", { context: rec } );
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// CONTEXT
|
|
373
|
+
this.addDOMEvent("contextmenu", (e) => {
|
|
374
|
+
const row = targetRow( e );
|
|
375
|
+
if (row!==undefined ) {
|
|
376
|
+
// on a selected row, the menu is for all that is selected
|
|
377
|
+
if( !this._selection.has( this._dataview.getIdByIndex( row ) ) ) {
|
|
378
|
+
this._clearSelection( false );
|
|
379
|
+
this._addSelection(row);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const rec = this._dataview.getByIndex( row );
|
|
383
|
+
this.fire( "contextMenu", { uievent: e, context: rec } );
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
e.preventDefault( );
|
|
387
|
+
e.stopPropagation( );
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
// MOUSE OVER
|
|
391
|
+
this.addDOMEvent("mouseover", (e) => {
|
|
392
|
+
|
|
393
|
+
if (!this._has_fixed) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
let el = Component.parentElement(e.target as HTMLElement, Component);
|
|
398
|
+
while (el && !el.hasClass("row")) {
|
|
399
|
+
el = el.parentElement();
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (el) {
|
|
403
|
+
const data = el.getData("row");
|
|
404
|
+
|
|
405
|
+
this.queryAll(".hover").forEach(x => x.removeClass("hover"));
|
|
406
|
+
|
|
407
|
+
if (data) {
|
|
408
|
+
const rows = this.queryAll(`.row[data-row="${data}"]`);
|
|
409
|
+
rows.forEach(x => x.addClass("hover"));
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
// MOUSE LEAVE
|
|
415
|
+
this.addDOMEvent("mouseleave", (e) => {
|
|
416
|
+
|
|
417
|
+
if (!this._has_fixed) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
this.queryAll(".hover").forEach(x => x.removeClass("hover"));
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
if( props.multisel ) {
|
|
425
|
+
this.addShortcut( "Mod+A", ( ) => this.selectAll( ) );
|
|
426
|
+
}
|
|
427
|
+
|
|
213
428
|
if (props.store) {
|
|
214
429
|
this.setStore(props.store);
|
|
215
430
|
|
|
@@ -272,7 +487,18 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
|
|
|
272
487
|
*/
|
|
273
488
|
|
|
274
489
|
navigate( sens: kbNav ) {
|
|
275
|
-
|
|
490
|
+
// no row to go to
|
|
491
|
+
if( !this._dataview || !this._dataview.getCount( ) ) {
|
|
492
|
+
return false;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// the row to start from: the one of the first selected record
|
|
496
|
+
let fsel = -1;
|
|
497
|
+
if( this._selection.size ) {
|
|
498
|
+
fsel = this._dataview.indexOfId( this._selection.values().next().value );
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if( fsel<0 ) {
|
|
276
502
|
if( sens==kbNav.next || sens==kbNav.pgdn ) {
|
|
277
503
|
sens = kbNav.first;
|
|
278
504
|
}
|
|
@@ -289,7 +515,6 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
|
|
|
289
515
|
return true;
|
|
290
516
|
}
|
|
291
517
|
else if( sens==kbNav.prev || sens==kbNav.next ) {
|
|
292
|
-
const fsel = this._selection.values().next().value;
|
|
293
518
|
let nel = sens==kbNav.next ? fsel+1 : fsel-1;
|
|
294
519
|
if( nel>=0 && nel<this._dataview.getCount() ) {
|
|
295
520
|
this._clearSelection( false );
|
|
@@ -301,8 +526,6 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
|
|
|
301
526
|
else if( sens==kbNav.pgdn || sens==kbNav.pgup ) {
|
|
302
527
|
const pgh = this._vis_rows.size;
|
|
303
528
|
|
|
304
|
-
const fsel = this._selection.values().next().value;
|
|
305
|
-
|
|
306
529
|
let sby = sens==kbNav.pgdn ? pgh : -pgh;
|
|
307
530
|
let nel = fsel+sby;
|
|
308
531
|
|
|
@@ -366,8 +589,20 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
|
|
|
366
589
|
return;
|
|
367
590
|
}
|
|
368
591
|
|
|
369
|
-
|
|
370
|
-
|
|
592
|
+
// the data or the filter changed: a record that left the view leaves the selection
|
|
593
|
+
if (ev.change_type != 'sort') {
|
|
594
|
+
let lost = false;
|
|
595
|
+
|
|
596
|
+
for (const id of this._selection) {
|
|
597
|
+
if (this._dataview.indexOfId( id ) < 0) {
|
|
598
|
+
this._selection.delete( id );
|
|
599
|
+
lost = true;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
if (lost) {
|
|
604
|
+
this.fire("selectionChange", { selection: this.getSelection( ) ?? [], empty: this._selection.size==0 } );
|
|
605
|
+
}
|
|
371
606
|
}
|
|
372
607
|
|
|
373
608
|
this._updateFlexs( );
|
|
@@ -375,9 +610,9 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
|
|
|
375
610
|
this._update(true);
|
|
376
611
|
}
|
|
377
612
|
|
|
378
|
-
//
|
|
613
|
+
// the previous view stops following its store
|
|
379
614
|
if (this._dataview) {
|
|
380
|
-
this._dataview.
|
|
615
|
+
this._dataview.close( );
|
|
381
616
|
}
|
|
382
617
|
|
|
383
618
|
if (store) {
|
|
@@ -497,7 +732,9 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
|
|
|
497
732
|
const sizer = new CSizer("right");
|
|
498
733
|
|
|
499
734
|
sizer.on("stop", ( ) => {
|
|
735
|
+
// the flex columns took what was left: the body gets the width of its columns
|
|
500
736
|
this._updateFlexs( );
|
|
737
|
+
this._computeFullSize( );
|
|
501
738
|
})
|
|
502
739
|
|
|
503
740
|
sizer.on("resize", (ev) => {
|
|
@@ -519,6 +756,24 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
|
|
|
519
756
|
}
|
|
520
757
|
})
|
|
521
758
|
|
|
759
|
+
// a double click on the sizer: the column takes its share of the place
|
|
760
|
+
// that is left (flex 1). To stop taking it, the sizer is dragged
|
|
761
|
+
sizer.addDOMEvent("dblclick", (ev) => {
|
|
762
|
+
// both: the cell under the sizer sorts its column on a double click
|
|
763
|
+
ev.stopPropagation( );
|
|
764
|
+
ev.preventDefault( );
|
|
765
|
+
|
|
766
|
+
// a fixed column is not in the place that is shared
|
|
767
|
+
if (fixed || cdata.flex) {
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
cdata.flex = 1;
|
|
772
|
+
|
|
773
|
+
this._updateFlexs( );
|
|
774
|
+
this._computeFullSize( );
|
|
775
|
+
});
|
|
776
|
+
|
|
522
777
|
const cell = new Component({
|
|
523
778
|
cls: `cell`,
|
|
524
779
|
attrs: { "data-col": col },
|
|
@@ -530,6 +785,11 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
|
|
|
530
785
|
]
|
|
531
786
|
});
|
|
532
787
|
|
|
788
|
+
// the column the grid is sorted by
|
|
789
|
+
if (cdata.sens) {
|
|
790
|
+
cell.addClass( cdata.sens==="up" ? "sorted" : "sorted desc" );
|
|
791
|
+
}
|
|
792
|
+
|
|
533
793
|
cell.addDOMEvent("touchend", () => {
|
|
534
794
|
const last = cell.getInternalData("touchend");
|
|
535
795
|
const now = Date.now();
|
|
@@ -614,34 +874,29 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
|
|
|
614
874
|
this.setTimeout("sort", 50, () => {
|
|
615
875
|
let asc = true;
|
|
616
876
|
|
|
617
|
-
|
|
618
|
-
const scol = this.query(`.col-header .cell[data-col="${col}"]`);
|
|
619
|
-
if( !scol ) {
|
|
620
|
-
return;
|
|
621
|
-
}
|
|
877
|
+
const cdata = this._getCol(col);
|
|
622
878
|
|
|
623
879
|
if( ascending===undefined ) {
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
asc = true;
|
|
627
|
-
}
|
|
628
|
-
else {
|
|
629
|
-
asc = false;
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
else {
|
|
633
|
-
const sorted = this.queryAll(".sorted");
|
|
634
|
-
sorted.forEach(x => x.removeClass("sorted asc desc"));
|
|
635
|
-
}
|
|
880
|
+
// a double click on the sorted column reverses it
|
|
881
|
+
asc = cdata.sens!=="up";
|
|
636
882
|
}
|
|
637
883
|
else {
|
|
638
884
|
asc = ascending;
|
|
639
885
|
}
|
|
640
886
|
|
|
641
|
-
|
|
642
|
-
|
|
887
|
+
// one column is sorted. It is kept in the columns, not in the header: the header
|
|
888
|
+
// may not be built yet, and is built again each time the grid comes in the document
|
|
889
|
+
this._columns.forEach( c => { c.sens = undefined; } );
|
|
890
|
+
cdata.sens = asc ? "up" : "dn";
|
|
643
891
|
|
|
644
|
-
const
|
|
892
|
+
const sorted = this.queryAll(".sorted");
|
|
893
|
+
sorted.forEach(x => x.removeClass("sorted asc desc"));
|
|
894
|
+
|
|
895
|
+
const scol = this.query(`.col-header .cell[data-col="${col}"]`);
|
|
896
|
+
if( scol ) {
|
|
897
|
+
scol.setClass("sorted");
|
|
898
|
+
scol.setClass("desc", !asc);
|
|
899
|
+
}
|
|
645
900
|
|
|
646
901
|
let num = false;
|
|
647
902
|
switch (cdata.type) {
|
|
@@ -664,6 +919,11 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
|
|
|
664
919
|
|
|
665
920
|
this._update(true);
|
|
666
921
|
|
|
922
|
+
// sorted by a double click on a header: what is selected is shown where it went
|
|
923
|
+
if( ascending===undefined && this._selection.size ) {
|
|
924
|
+
this._scrollToIndex( this._dataview.indexOfId( this._selection.values().next().value ) );
|
|
925
|
+
}
|
|
926
|
+
|
|
667
927
|
setWaitCursor(false);
|
|
668
928
|
});
|
|
669
929
|
}
|
|
@@ -689,6 +949,11 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
|
|
|
689
949
|
const col = column.id;
|
|
690
950
|
const type = column.type;
|
|
691
951
|
|
|
952
|
+
// a column that is not a field of the model (actions...) has only its renderer to draw it
|
|
953
|
+
if( column.renderer && isString( col ) && this._datamodel.getFieldIndex( col )<0 ) {
|
|
954
|
+
return column.renderer( rec, col );
|
|
955
|
+
}
|
|
956
|
+
|
|
692
957
|
let data = this._datamodel.getRaw(col, rec);
|
|
693
958
|
if (data === undefined || data === null) {
|
|
694
959
|
return null;
|
|
@@ -837,7 +1102,7 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
|
|
|
837
1102
|
const rowel = new Box({ cls: "row", style: { top: top.toFixed(2) + "px" }, content: els });
|
|
838
1103
|
rowel.setData("row", rowid + "");
|
|
839
1104
|
|
|
840
|
-
if (this._selection.has(
|
|
1105
|
+
if (this._selection.has( this._dataview.getRecId( rec ) )) {
|
|
841
1106
|
rowel.addClass("selected");
|
|
842
1107
|
}
|
|
843
1108
|
|
|
@@ -894,7 +1159,7 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
|
|
|
894
1159
|
const rowel = new Box({ cls: "row", style: { top: top + "px" }, content: cols });
|
|
895
1160
|
rowel.setData("row", rowid + "");
|
|
896
1161
|
|
|
897
|
-
if (this._selection.has(
|
|
1162
|
+
if (this._selection.has( this._dataview.getRecId( rec ) )) {
|
|
898
1163
|
rowel.addClass("selected");
|
|
899
1164
|
}
|
|
900
1165
|
|
|
@@ -913,8 +1178,13 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
|
|
|
913
1178
|
|
|
914
1179
|
for (let x = 0; x < ccount; x++) {
|
|
915
1180
|
const cdata = this._getCol(x);
|
|
916
|
-
|
|
917
|
-
|
|
1181
|
+
|
|
1182
|
+
// the fixed columns are not in the viewport: their width is not taken from its own
|
|
1183
|
+
if( cdata.fixed ) {
|
|
1184
|
+
continue;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
if( cdata.flex ) {
|
|
918
1188
|
flexc += cdata.flex;
|
|
919
1189
|
}
|
|
920
1190
|
else {
|
|
@@ -1016,129 +1286,6 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
|
|
|
1016
1286
|
this._update()
|
|
1017
1287
|
});
|
|
1018
1288
|
|
|
1019
|
-
// WHEEL
|
|
1020
|
-
this.addDOMEvent("wheel", (ev: WheelEvent) => {
|
|
1021
|
-
if (ev.deltaY && this._dataview && this._dataview.getCount() >= SCROLL_LIMIT) {
|
|
1022
|
-
this._viewport.dom.scrollBy(0, ev.deltaY < 0 ? -1 : 1);
|
|
1023
|
-
ev.stopPropagation();
|
|
1024
|
-
ev.preventDefault();
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
if (this._has_fixed && ev.deltaY) {
|
|
1028
|
-
// wheel on fixed part
|
|
1029
|
-
// fixed part do not have scrollbar, so we need to handle it by hand
|
|
1030
|
-
let t = ev.target as Node;
|
|
1031
|
-
while (t != this.dom) {
|
|
1032
|
-
if (t == this._vheader.dom) {
|
|
1033
|
-
this._viewport.dom.scrollBy(0, ev.deltaY < 0 ? -this._row_height : this._row_height);
|
|
1034
|
-
ev.stopPropagation();
|
|
1035
|
-
ev.preventDefault();
|
|
1036
|
-
break;
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
t = t.parentNode;
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
})
|
|
1043
|
-
|
|
1044
|
-
const targetRow = ( e: MouseEvent ) => {
|
|
1045
|
-
let el = Component.parentElement(e.target as HTMLElement, Component);
|
|
1046
|
-
while (el && !el.hasClass("row")) {
|
|
1047
|
-
el = el.parentElement();
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
if (el) {
|
|
1051
|
-
return el.getIntData("row");
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
return undefined;
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
// CLICK
|
|
1059
|
-
this.addDOMEvent("click", (e) => {
|
|
1060
|
-
const row = targetRow( e );
|
|
1061
|
-
if (row!==undefined ) {
|
|
1062
|
-
//TODO: multiselection
|
|
1063
|
-
if( !this._selection.has(row) ) {
|
|
1064
|
-
this._clearSelection( false );
|
|
1065
|
-
this._addSelection(row);
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
else {
|
|
1069
|
-
this._clearSelection( true );
|
|
1070
|
-
}
|
|
1071
|
-
});
|
|
1072
|
-
|
|
1073
|
-
// DBLCLICK
|
|
1074
|
-
this.addDOMEvent("dblclick", (e) => {
|
|
1075
|
-
const row = targetRow( e );
|
|
1076
|
-
if (row!==undefined ) {
|
|
1077
|
-
//TODO: multiselection
|
|
1078
|
-
if( !this._selection.has(row) ) {
|
|
1079
|
-
this._clearSelection( false );
|
|
1080
|
-
this._addSelection(row);
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
this._on_dblclk( e, row );
|
|
1084
|
-
|
|
1085
|
-
const rec = this._dataview.getByIndex( row );
|
|
1086
|
-
this.fire( "dblClick", { context: rec } );
|
|
1087
|
-
}
|
|
1088
|
-
});
|
|
1089
|
-
|
|
1090
|
-
// CONTEXT
|
|
1091
|
-
this.addDOMEvent("contextmenu", (e) => {
|
|
1092
|
-
const row = targetRow( e );
|
|
1093
|
-
if (row!==undefined ) {
|
|
1094
|
-
//TODO: multiselection
|
|
1095
|
-
if( !this._selection.has(row) ) {
|
|
1096
|
-
this._clearSelection( false );
|
|
1097
|
-
this._addSelection(row);
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
|
-
const rec = this._dataview.getByIndex( row );
|
|
1101
|
-
this.fire( "contextMenu", { uievent: e, context: rec } );
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1104
|
-
e.preventDefault( );
|
|
1105
|
-
e.stopPropagation( );
|
|
1106
|
-
});
|
|
1107
|
-
|
|
1108
|
-
// MOUSE OVER
|
|
1109
|
-
this.addDOMEvent("mouseover", (e) => {
|
|
1110
|
-
|
|
1111
|
-
if (!this._has_fixed) {
|
|
1112
|
-
return;
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
let el = Component.parentElement(e.target as HTMLElement, Component);
|
|
1116
|
-
while (el && !el.hasClass("row")) {
|
|
1117
|
-
el = el.parentElement();
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
if (el) {
|
|
1121
|
-
const data = el.getData("row");
|
|
1122
|
-
|
|
1123
|
-
this.queryAll(".hover").forEach(x => x.removeClass("hover"));
|
|
1124
|
-
|
|
1125
|
-
if (data) {
|
|
1126
|
-
const rows = this.queryAll(`.row[data-row="${data}"]`);
|
|
1127
|
-
rows.forEach(x => x.addClass("hover"));
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
});
|
|
1131
|
-
|
|
1132
|
-
// MOUSE LEAVE
|
|
1133
|
-
this.addDOMEvent("mouseleave", (e) => {
|
|
1134
|
-
|
|
1135
|
-
if (!this._has_fixed) {
|
|
1136
|
-
return;
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
|
-
this.queryAll(".hover").forEach(x => x.removeClass("hover"));
|
|
1140
|
-
});
|
|
1141
|
-
|
|
1142
1289
|
this._updateFlexs( );
|
|
1143
1290
|
|
|
1144
1291
|
this._fheader = this._buildColHeader(true);
|
|
@@ -1271,17 +1418,37 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
|
|
|
1271
1418
|
this._clearSelection( false );
|
|
1272
1419
|
}
|
|
1273
1420
|
|
|
1421
|
+
/**
|
|
1422
|
+
* Select every record of the view (with multisel only).
|
|
1423
|
+
* The id of each record is kept, and selectionChange gives them all:
|
|
1424
|
+
* on a very large view this costs memory.
|
|
1425
|
+
*/
|
|
1426
|
+
|
|
1427
|
+
selectAll( ) {
|
|
1428
|
+
const count = this._dataview ? this._dataview.getCount( ) : 0;
|
|
1429
|
+
if( !this.props.multisel || !count ) {
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
for( let row=0; row<count; row++ ) {
|
|
1434
|
+
this._selection.add( this._dataview.getIdByIndex( row ) );
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
this.queryAll(".row").forEach(el => {
|
|
1438
|
+
el.addClass("selected");
|
|
1439
|
+
});
|
|
1440
|
+
|
|
1441
|
+
this.fire("selectionChange", { selection: this.getSelection( ), empty: false } );
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1274
1444
|
private _clearSelection( notify: boolean ) {
|
|
1275
1445
|
if( !this._selection.size ) {
|
|
1276
1446
|
return;
|
|
1277
1447
|
}
|
|
1278
1448
|
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
el.removeClass("selected");
|
|
1283
|
-
})
|
|
1284
|
-
}
|
|
1449
|
+
this.queryAll(".row.selected").forEach(el => {
|
|
1450
|
+
el.removeClass("selected");
|
|
1451
|
+
});
|
|
1285
1452
|
|
|
1286
1453
|
this._selection.clear();
|
|
1287
1454
|
if( notify ) {
|
|
@@ -1293,16 +1460,18 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
|
|
|
1293
1460
|
*
|
|
1294
1461
|
*/
|
|
1295
1462
|
|
|
1296
|
-
private _addSelection(rowid: number) {
|
|
1297
|
-
this._selection.add(rowid)
|
|
1463
|
+
private _addSelection(rowid: number, notify = true) {
|
|
1464
|
+
this._selection.add( this._dataview.getIdByIndex( rowid ) );
|
|
1298
1465
|
|
|
1299
1466
|
const els = this.queryAll(`.row[data-row="${rowid}"]`)
|
|
1300
1467
|
els.forEach(el => {
|
|
1301
1468
|
el.addClass("selected");
|
|
1302
1469
|
});
|
|
1303
1470
|
|
|
1304
|
-
|
|
1305
|
-
|
|
1471
|
+
// all that is selected, not this row only
|
|
1472
|
+
if( notify ) {
|
|
1473
|
+
this.fire("selectionChange", { selection: this.getSelection( ), empty: false } );
|
|
1474
|
+
}
|
|
1306
1475
|
}
|
|
1307
1476
|
|
|
1308
1477
|
/**
|
|
@@ -1310,25 +1479,31 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
|
|
|
1310
1479
|
*/
|
|
1311
1480
|
|
|
1312
1481
|
getSelection( ) {
|
|
1313
|
-
|
|
1314
|
-
|
|
1482
|
+
// a record may be gone: out of the document, the grid does not follow its store
|
|
1483
|
+
const recs: DataRecord[] = [];
|
|
1484
|
+
for (const id of this._selection) {
|
|
1485
|
+
const rec = this._dataview.getById( id );
|
|
1486
|
+
if (rec) {
|
|
1487
|
+
recs.push( rec );
|
|
1488
|
+
}
|
|
1315
1489
|
}
|
|
1316
1490
|
|
|
1317
|
-
|
|
1318
|
-
return ids.map( id => this._dataview.getByIndex(id) );
|
|
1491
|
+
return recs.length ? recs : null;
|
|
1319
1492
|
}
|
|
1320
1493
|
|
|
1321
1494
|
/**
|
|
1322
|
-
*
|
|
1495
|
+
*
|
|
1323
1496
|
*/
|
|
1324
1497
|
|
|
1325
1498
|
getFirstSel( ) {
|
|
1326
|
-
|
|
1327
|
-
|
|
1499
|
+
for (const id of this._selection) {
|
|
1500
|
+
const rec = this._dataview.getById( id );
|
|
1501
|
+
if (rec) {
|
|
1502
|
+
return rec;
|
|
1503
|
+
}
|
|
1328
1504
|
}
|
|
1329
1505
|
|
|
1330
|
-
|
|
1331
|
-
return this._dataview.getByIndex( id );
|
|
1506
|
+
return null;
|
|
1332
1507
|
}
|
|
1333
1508
|
|
|
1334
1509
|
/**
|