canvas-datagrid 0.4.2 → 0.4.3
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/CHANGELOG.md +11 -0
- package/dist/canvas-datagrid.debug.js +778 -129
- package/dist/canvas-datagrid.debug.map +1 -1
- package/dist/canvas-datagrid.js +1 -1
- package/dist/canvas-datagrid.module.js +1 -1
- package/dist/canvas-datagrid.module.js.map +1 -1
- package/dist/types.d.ts +25 -0
- package/lib/contextMenu.js +176 -47
- package/lib/defaults.js +8 -0
- package/lib/draw.js +246 -13
- package/lib/events/index.js +70 -32
- package/lib/groups/util.js +42 -0
- package/lib/intf.js +37 -0
- package/lib/publicMethods.js +84 -2
- package/package.json +1 -1
package/dist/types.d.ts
CHANGED
|
@@ -516,6 +516,18 @@ declare class canvasDatagrid {
|
|
|
516
516
|
* @returns header with the selected name, or undefined.
|
|
517
517
|
*/
|
|
518
518
|
static getHeaderByName(name: string): header;
|
|
519
|
+
/**
|
|
520
|
+
* Hide column/columns
|
|
521
|
+
*/
|
|
522
|
+
static hideColumns: any;
|
|
523
|
+
/**
|
|
524
|
+
* Hide rows
|
|
525
|
+
*/
|
|
526
|
+
static hideRows: any;
|
|
527
|
+
/**
|
|
528
|
+
* Unhide rows
|
|
529
|
+
*/
|
|
530
|
+
static unhideRows: any;
|
|
519
531
|
/**
|
|
520
532
|
* Resizes a column to fit the longest value in the column. Call without a value to resize all columns.
|
|
521
533
|
* Warning, can be slow on very large record sets (1m records ~3-5 seconds on an i7).
|
|
@@ -620,6 +632,12 @@ declare class canvasDatagrid {
|
|
|
620
632
|
* @returns cell at the selected location.
|
|
621
633
|
*/
|
|
622
634
|
static getVisibleCellByIndex(x: number, y: number): cell;
|
|
635
|
+
/**
|
|
636
|
+
* Get an unhide indicator at grid pixel coordinate x and y.
|
|
637
|
+
* @param x - Number of pixels from the left.
|
|
638
|
+
* @param y - Number of pixels from the top.
|
|
639
|
+
*/
|
|
640
|
+
static getUnhideIndicator(x: number, y: number): void;
|
|
623
641
|
/**
|
|
624
642
|
* Get a column group at grid pixel coordinate x and y.
|
|
625
643
|
* @param x - Number of pixels from the left.
|
|
@@ -2186,3 +2204,10 @@ declare namespace canvasDatagrid {
|
|
|
2186
2204
|
}
|
|
2187
2205
|
}
|
|
2188
2206
|
|
|
2207
|
+
/**
|
|
2208
|
+
* Merge a new hidden row range into existed ranges array
|
|
2209
|
+
* @param hiddenRowRanges - tuples: Array<[bgeinRowIndex, endRowIndex]>
|
|
2210
|
+
* @param newRange - tuple: [beginRowIndex, endRowIndex]
|
|
2211
|
+
*/
|
|
2212
|
+
declare function mergeHiddenRowRanges(hiddenRowRanges: any[], newRange: number[]): boolean;
|
|
2213
|
+
|
package/lib/contextMenu.js
CHANGED
|
@@ -538,8 +538,114 @@ export default function (self) {
|
|
|
538
538
|
});
|
|
539
539
|
}
|
|
540
540
|
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Return a tuple if the user selected contiguous columns, otherwise `null`.
|
|
544
|
+
* Info: Because the user may reorder the columns,
|
|
545
|
+
* the schemaIndex of the first item may be greater than the schemaIndex of the second item,
|
|
546
|
+
* but the columnIndex of the firs item must less than the columnIndex of the second item.
|
|
547
|
+
* @param {any[]} schema from `self.getSchema()`
|
|
548
|
+
* @returns {any[]} column schemas tuple (each schema has an additional field `schemaIndex`)
|
|
549
|
+
*/
|
|
550
|
+
function getSelectedContiguousColumns(ev, schema) {
|
|
551
|
+
const memoKey = '__contiguousColumns';
|
|
552
|
+
if (Array.isArray(ev[memoKey]) || ev[memoKey] === null) return ev[memoKey];
|
|
553
|
+
ev[memoKey] = null;
|
|
554
|
+
|
|
555
|
+
if (!Array.isArray(self.selections) || self.selections.length === 0) return;
|
|
556
|
+
const selection = self.selections[0];
|
|
557
|
+
if (!selection || selection.length === 0) return;
|
|
558
|
+
for (let rowIndex = 0; rowIndex < self.viewData.length; rowIndex++) {
|
|
559
|
+
const row = self.viewData[rowIndex];
|
|
560
|
+
if (!row) continue;
|
|
561
|
+
const compare = self.selections[rowIndex];
|
|
562
|
+
if (!compare) return;
|
|
563
|
+
if (compare.length !== selection.length) return;
|
|
564
|
+
for (let i = 0; i < selection.length; i++)
|
|
565
|
+
if (selection[i] !== compare[i]) return;
|
|
566
|
+
}
|
|
567
|
+
selection.sort((a, b) => a - b);
|
|
568
|
+
|
|
569
|
+
/** @type {number[][]} */
|
|
570
|
+
const ranges = [];
|
|
571
|
+
let begin = selection[0];
|
|
572
|
+
let end = selection[0];
|
|
573
|
+
for (let i = 1; i < selection.length; i++) {
|
|
574
|
+
const orderIndex = selection[i];
|
|
575
|
+
if (orderIndex === end + 1) {
|
|
576
|
+
end = orderIndex;
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
ranges.push([begin, end]);
|
|
580
|
+
begin = orderIndex;
|
|
581
|
+
end = orderIndex;
|
|
582
|
+
}
|
|
583
|
+
ranges.push([begin, end]);
|
|
584
|
+
|
|
585
|
+
const currentOrderIndex = ev.cell.columnIndex;
|
|
586
|
+
const matchedRange = ranges.find(
|
|
587
|
+
(range) =>
|
|
588
|
+
currentOrderIndex >= range[0] &&
|
|
589
|
+
currentOrderIndex <= range[1] &&
|
|
590
|
+
range[0] !== range[1],
|
|
591
|
+
);
|
|
592
|
+
if (!matchedRange) return;
|
|
593
|
+
|
|
594
|
+
/** @type {number[]} orders[index] => columnIndex */
|
|
595
|
+
const orders = self.orders.columns;
|
|
596
|
+
if (!Array.isArray(orders)) return;
|
|
597
|
+
|
|
598
|
+
const matchedSchema = matchedRange.map((orderIndex) => {
|
|
599
|
+
const schemaIndex = orders[orderIndex];
|
|
600
|
+
const thisSchema = schema[schemaIndex];
|
|
601
|
+
return Object.assign({}, thisSchema, { orderIndex });
|
|
602
|
+
});
|
|
603
|
+
if (matchedSchema.findIndex((it) => !it) >= 0) return;
|
|
604
|
+
return (ev[memoKey] = matchedSchema);
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* @param {boolean} [allowOnlyOneRow]
|
|
608
|
+
* @returns {number[]} a rowIndex tuple. It can contains one row index or two row indexes.
|
|
609
|
+
*/
|
|
610
|
+
function getSelectedContiguousRows(allowOnlyOneRow) {
|
|
611
|
+
let range = [];
|
|
612
|
+
let prev = -2;
|
|
613
|
+
let ok = true;
|
|
614
|
+
self.selections.forEach(function (row, index) {
|
|
615
|
+
if (!ok) return;
|
|
616
|
+
if (prev < -1) {
|
|
617
|
+
prev = index;
|
|
618
|
+
range[0] = index;
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
if (index !== prev + 1 || !row || row.length === 0) {
|
|
622
|
+
ok = false;
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
prev = index;
|
|
626
|
+
range[1] = index;
|
|
627
|
+
});
|
|
628
|
+
if (ok) {
|
|
629
|
+
if (range.length === 1) return allowOnlyOneRow ? range : null;
|
|
630
|
+
return range;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
541
633
|
function addDefaultContextMenuItem(e) {
|
|
542
|
-
|
|
634
|
+
const schema = self.getSchema();
|
|
635
|
+
/**
|
|
636
|
+
* A map between columnIndex and column data
|
|
637
|
+
* @type {Map<string,any>}
|
|
638
|
+
*/
|
|
639
|
+
let columns;
|
|
640
|
+
const getColumnsMap = () => {
|
|
641
|
+
if (!columns)
|
|
642
|
+
columns = new Map(schema.map((_col) => [_col.columnIndex, _col]));
|
|
643
|
+
return columns;
|
|
644
|
+
};
|
|
645
|
+
const isSorting =
|
|
646
|
+
self.orderings.columns && self.orderings.columns.length > 0;
|
|
647
|
+
|
|
648
|
+
const isNormalCell =
|
|
543
649
|
!(
|
|
544
650
|
e.cell.isBackground ||
|
|
545
651
|
e.cell.isColumnHeaderCellCap ||
|
|
@@ -614,20 +720,30 @@ export default function (self) {
|
|
|
614
720
|
const columnOrderIndex = e.cell.columnIndex;
|
|
615
721
|
const columnIndex = self.orders.columns[columnOrderIndex];
|
|
616
722
|
|
|
723
|
+
const contiguousColumns = getSelectedContiguousColumns(e, schema);
|
|
724
|
+
let title = '';
|
|
725
|
+
if (contiguousColumns) {
|
|
726
|
+
title = contiguousColumns
|
|
727
|
+
.map((col) => col.title || col.name)
|
|
728
|
+
.join('-');
|
|
729
|
+
} else {
|
|
730
|
+
const column = schema[columnIndex];
|
|
731
|
+
if (column) title = column.title || column.name;
|
|
732
|
+
}
|
|
617
733
|
e.items.push({
|
|
618
|
-
title: self.attributes.hideColumnText.replace(
|
|
619
|
-
/%s/gi,
|
|
620
|
-
e.cell.header.title || e.cell.header.name,
|
|
621
|
-
),
|
|
734
|
+
title: self.attributes.hideColumnText.replace(/%s/gi, title),
|
|
622
735
|
click: function (ev) {
|
|
623
|
-
self.getSchema()[columnIndex].hidden = true;
|
|
624
736
|
ev.preventDefault();
|
|
625
737
|
self.stopPropagation(ev);
|
|
626
738
|
self.disposeContextMenu();
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
739
|
+
if (contiguousColumns) {
|
|
740
|
+
self.hideColumns(
|
|
741
|
+
contiguousColumns[0].orderIndex,
|
|
742
|
+
contiguousColumns[1].orderIndex,
|
|
743
|
+
);
|
|
744
|
+
} else {
|
|
745
|
+
self.hideColumns(columnOrderIndex);
|
|
746
|
+
}
|
|
631
747
|
},
|
|
632
748
|
});
|
|
633
749
|
}
|
|
@@ -689,6 +805,51 @@ export default function (self) {
|
|
|
689
805
|
},
|
|
690
806
|
});
|
|
691
807
|
}
|
|
808
|
+
|
|
809
|
+
//#region hide rows
|
|
810
|
+
const canHideRows = !isSorting && e.cell.isRowHeader && e.cell.header;
|
|
811
|
+
if (canHideRows) {
|
|
812
|
+
const range = getSelectedContiguousRows(true);
|
|
813
|
+
if (range) {
|
|
814
|
+
const boundRowIndexes = range.map((viewRowIndex) =>
|
|
815
|
+
self.getBoundRowIndexFromViewRowIndex(viewRowIndex),
|
|
816
|
+
);
|
|
817
|
+
let title;
|
|
818
|
+
if (boundRowIndexes.length === 1) {
|
|
819
|
+
if (typeof boundRowIndexes[0] === 'number')
|
|
820
|
+
title = boundRowIndexes[0] + 1;
|
|
821
|
+
else title = range[0] + 1;
|
|
822
|
+
|
|
823
|
+
title = self.attributes.showHideRow.replace('%s', title);
|
|
824
|
+
// hide one row
|
|
825
|
+
e.items.push({
|
|
826
|
+
title,
|
|
827
|
+
click: function (ev) {
|
|
828
|
+
ev.preventDefault();
|
|
829
|
+
self.hideRows(boundRowIndexes[0], boundRowIndexes[0]);
|
|
830
|
+
},
|
|
831
|
+
});
|
|
832
|
+
} else if (boundRowIndexes[0] <= boundRowIndexes[1]) {
|
|
833
|
+
title = boundRowIndexes
|
|
834
|
+
.map((it, index) => {
|
|
835
|
+
if (typeof it === 'number') return it + 1;
|
|
836
|
+
return range[index] + 1;
|
|
837
|
+
})
|
|
838
|
+
.join('-');
|
|
839
|
+
title = self.attributes.showHideRows.replace('%s', title);
|
|
840
|
+
// hide rows
|
|
841
|
+
e.items.push({
|
|
842
|
+
title,
|
|
843
|
+
click: function (ev) {
|
|
844
|
+
ev.preventDefault();
|
|
845
|
+
self.hideRows(boundRowIndexes[0], boundRowIndexes[1]);
|
|
846
|
+
},
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
//#endregion hide rows
|
|
852
|
+
|
|
692
853
|
//#region group/ungroup columns
|
|
693
854
|
const groupAreaHeight = self.getColumnGroupAreaHeight();
|
|
694
855
|
const groupAreaWidth = self.getRowGroupAreaWidth();
|
|
@@ -760,28 +921,13 @@ export default function (self) {
|
|
|
760
921
|
const canUngroupColumns =
|
|
761
922
|
self.attributes.allowGroupingColumns && e.cell.isColumnHeader;
|
|
762
923
|
const canGroupByRows =
|
|
763
|
-
|
|
924
|
+
!isSorting &&
|
|
925
|
+
self.attributes.allowGroupingRows &&
|
|
926
|
+
e.cell.isRowHeader &&
|
|
927
|
+
e.cell.header;
|
|
764
928
|
const canUngroupRows =
|
|
765
929
|
self.attributes.allowGroupingRows && e.cell.isRowHeader;
|
|
766
930
|
|
|
767
|
-
/**
|
|
768
|
-
* The value for storing the return value from `self.getSchema()`
|
|
769
|
-
* @type {any[]}
|
|
770
|
-
*/
|
|
771
|
-
let schema;
|
|
772
|
-
/**
|
|
773
|
-
* A map between columnIndex and column data
|
|
774
|
-
* @type {Map<string,any>}
|
|
775
|
-
*/
|
|
776
|
-
let columns;
|
|
777
|
-
const getColumnsMap = () => {
|
|
778
|
-
if (!columns) {
|
|
779
|
-
if (!schema) schema = self.getSchema();
|
|
780
|
-
columns = new Map(schema.map((_col) => [_col.columnIndex, _col]));
|
|
781
|
-
}
|
|
782
|
-
return columns;
|
|
783
|
-
};
|
|
784
|
-
|
|
785
931
|
if (canGroupByColumns) {
|
|
786
932
|
/** @type {number[]} */
|
|
787
933
|
const groupIndexes = [];
|
|
@@ -863,23 +1009,7 @@ export default function (self) {
|
|
|
863
1009
|
}
|
|
864
1010
|
}
|
|
865
1011
|
if (canGroupByRows) {
|
|
866
|
-
|
|
867
|
-
let prev = -2;
|
|
868
|
-
let ok = true;
|
|
869
|
-
self.selections.forEach(function (row, index) {
|
|
870
|
-
if (!ok) return;
|
|
871
|
-
if (prev < -1) {
|
|
872
|
-
prev = index;
|
|
873
|
-
range[0] = index;
|
|
874
|
-
return;
|
|
875
|
-
}
|
|
876
|
-
if (index !== prev + 1 || !row || row.length === 0) {
|
|
877
|
-
ok = false;
|
|
878
|
-
return;
|
|
879
|
-
}
|
|
880
|
-
prev = index;
|
|
881
|
-
range[1] = index;
|
|
882
|
-
});
|
|
1012
|
+
const range = getSelectedContiguousRows(false) || [];
|
|
883
1013
|
const rangeTitle = range
|
|
884
1014
|
.map((rowIndex) => {
|
|
885
1015
|
const index = self.getBoundRowIndexFromViewRowIndex(rowIndex);
|
|
@@ -888,7 +1018,6 @@ export default function (self) {
|
|
|
888
1018
|
})
|
|
889
1019
|
.join('-');
|
|
890
1020
|
if (
|
|
891
|
-
ok &&
|
|
892
1021
|
range.length === 2 &&
|
|
893
1022
|
self.isNewGroupRangeValid(self.groupedRows, range[0], range[1])
|
|
894
1023
|
) {
|
package/lib/defaults.js
CHANGED
|
@@ -45,6 +45,10 @@ export default function (self) {
|
|
|
45
45
|
['filterFrozenRows', true],
|
|
46
46
|
['globalRowResize', false],
|
|
47
47
|
['hideColumnText', 'Hide %s'],
|
|
48
|
+
['showUnhideColumnsIndicator', false],
|
|
49
|
+
['showUnhideRowsIndicator', false],
|
|
50
|
+
['showHideRow', 'Hide row %s'],
|
|
51
|
+
['showHideRows', 'Hide rows %s'],
|
|
48
52
|
['hoverMode', 'cell'],
|
|
49
53
|
['keepFocusOnMouseOut', false],
|
|
50
54
|
['maxAutoCompleteItems', 200],
|
|
@@ -377,6 +381,10 @@ export default function (self) {
|
|
|
377
381
|
['treeArrowMarginTop', 6],
|
|
378
382
|
['treeArrowWidth', 13],
|
|
379
383
|
['treeGridHeight', 250],
|
|
384
|
+
['unhideIndicatorColor', 'rgba(0, 0, 0, 1)'],
|
|
385
|
+
['unhideIndicatorBackgroundColor', 'rgba(255, 255, 255, 1)'],
|
|
386
|
+
['unhideIndicatorBorderColor', 'rgba(174, 193, 232, 1)'],
|
|
387
|
+
['unhideIndicatorSize', 16],
|
|
380
388
|
['width', 'auto'],
|
|
381
389
|
],
|
|
382
390
|
};
|
package/lib/draw.js
CHANGED
|
@@ -110,8 +110,9 @@ export default function (self) {
|
|
|
110
110
|
}
|
|
111
111
|
/**
|
|
112
112
|
* @param {number[]} coords [x0,y0, x1,y1, x2,y2, ...]
|
|
113
|
+
* @param {boolean} [fill] fill the area that construct by these lines but not stroke
|
|
113
114
|
*/
|
|
114
|
-
function
|
|
115
|
+
function drawLines(coords, fill) {
|
|
115
116
|
if (coords.length < 4) return;
|
|
116
117
|
self.ctx.beginPath();
|
|
117
118
|
self.ctx.moveTo(
|
|
@@ -123,7 +124,8 @@ export default function (self) {
|
|
|
123
124
|
const y = coords[i + 1] + self.canvasOffsetTop;
|
|
124
125
|
self.ctx.lineTo(x, y);
|
|
125
126
|
}
|
|
126
|
-
self.ctx.
|
|
127
|
+
if (fill) self.ctx.fill();
|
|
128
|
+
else self.ctx.stroke();
|
|
127
129
|
}
|
|
128
130
|
/**
|
|
129
131
|
* @param {number} x based-X (left-top)
|
|
@@ -136,8 +138,67 @@ export default function (self) {
|
|
|
136
138
|
strokeRect(x, y, width, width);
|
|
137
139
|
const cx = x + width * 0.5;
|
|
138
140
|
const cy = y + width * 0.5;
|
|
139
|
-
|
|
140
|
-
if (collapsed)
|
|
141
|
+
drawLines([x + width * 0.2, cy, x + width * 0.78, cy]);
|
|
142
|
+
if (collapsed) drawLines([cx, y + width * 0.22, cx, y + width * 0.8]);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* @param {number} x
|
|
146
|
+
* @param {number} y
|
|
147
|
+
* @param {number} size
|
|
148
|
+
* @param {string} dir Direction of the triangle, one of the 't','b','l' and 'r'
|
|
149
|
+
* @param {boolean} [active]
|
|
150
|
+
*/
|
|
151
|
+
function drawUnhideIndicator(x, y, size, dir, active) {
|
|
152
|
+
const minPadding = size * 0.2;
|
|
153
|
+
const maxPadding = size * 0.3;
|
|
154
|
+
/** The long edge width of the triangle */
|
|
155
|
+
const longEdge = size - 2 * minPadding;
|
|
156
|
+
/** The median width of the triangle */
|
|
157
|
+
const median = size - 2 * maxPadding;
|
|
158
|
+
const halfLongEdge = longEdge * 0.5;
|
|
159
|
+
let x0, y0;
|
|
160
|
+
let coords, borderCoords;
|
|
161
|
+
switch (dir) {
|
|
162
|
+
case 'r':
|
|
163
|
+
x0 = x + maxPadding;
|
|
164
|
+
y0 = y + minPadding;
|
|
165
|
+
borderCoords = [x, y, x + size, y, x + size, y + size, x, y + size];
|
|
166
|
+
coords = [x0, y0, x0, y0 + longEdge, x0 + median, y0 + halfLongEdge];
|
|
167
|
+
break;
|
|
168
|
+
case 'l':
|
|
169
|
+
x0 = x + size - maxPadding;
|
|
170
|
+
y0 = y + minPadding;
|
|
171
|
+
borderCoords = [x + size, y, x, y, x, y + size, x + size, y + size];
|
|
172
|
+
coords = [x0, y0, x0, y0 + longEdge, x0 - median, y0 + halfLongEdge];
|
|
173
|
+
break;
|
|
174
|
+
case 't':
|
|
175
|
+
x0 = x + minPadding;
|
|
176
|
+
y0 = y + size - maxPadding;
|
|
177
|
+
borderCoords = [x, y + size, x, y, x + size, y, x + size, y + size];
|
|
178
|
+
coords = [x0, y0, x0 + longEdge, y0, x0 + halfLongEdge, y0 - median];
|
|
179
|
+
break;
|
|
180
|
+
case 'b':
|
|
181
|
+
x0 = x + minPadding;
|
|
182
|
+
y0 = y + maxPadding;
|
|
183
|
+
borderCoords = [x, y, x, y + size, x + size, y + size, x + size, y];
|
|
184
|
+
coords = [x0, y0, x0 + longEdge, y0, x0 + halfLongEdge, y0 + median];
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (active) {
|
|
189
|
+
self.ctx.strokeStyle = self.style.unhideIndicatorBorderColor;
|
|
190
|
+
self.ctx.lineWidth = 2;
|
|
191
|
+
drawLines(borderCoords);
|
|
192
|
+
|
|
193
|
+
self.ctx.fillStyle = self.style.unhideIndicatorBackgroundColor;
|
|
194
|
+
let offset = dir === 'r' || dir === 'b' ? 1 : 0;
|
|
195
|
+
if (dir === 'l' || dir === 'r')
|
|
196
|
+
fillRect(x - offset, y, size + offset, size);
|
|
197
|
+
else fillRect(x, y - offset, size, size + offset);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
self.ctx.fillStyle = self.style.unhideIndicatorColor;
|
|
201
|
+
drawLines(coords, true);
|
|
141
202
|
}
|
|
142
203
|
function drawOrderByArrow(x, y) {
|
|
143
204
|
var mt = self.style.columnHeaderOrderByArrowMarginTop * self.scale,
|
|
@@ -719,7 +780,15 @@ export default function (self) {
|
|
|
719
780
|
columnGroupsRectInfo = {},
|
|
720
781
|
collapsedColumnGroups = self.getCollapsedColumnGroups(),
|
|
721
782
|
collapsedRowGroups = self.getCollapsedRowGroups(),
|
|
722
|
-
cellHeight = self.style.cellHeight
|
|
783
|
+
cellHeight = self.style.cellHeight,
|
|
784
|
+
currentRowIndexOffset = 0,
|
|
785
|
+
/** @type {Array<{from:number,plus:number}>} */
|
|
786
|
+
rowIndexOffsetByHiddenRows = self.hiddenRowRanges
|
|
787
|
+
.map((range) => ({
|
|
788
|
+
from: range[0],
|
|
789
|
+
plus: range[1] - range[0] + 1,
|
|
790
|
+
}))
|
|
791
|
+
.sort((a, b) => a.from - b.from);
|
|
723
792
|
drawCount += 1;
|
|
724
793
|
p = performance.now();
|
|
725
794
|
self.visibleRowHeights = [];
|
|
@@ -1369,6 +1438,19 @@ export default function (self) {
|
|
|
1369
1438
|
group.to === cell.boundRowIndex,
|
|
1370
1439
|
) >= 0;
|
|
1371
1440
|
}
|
|
1441
|
+
// We don't treat the row index difference from hidden rows as the row gap.
|
|
1442
|
+
if (hasRowGap && self.hiddenRowRanges.length > 0) {
|
|
1443
|
+
for (let i = 0; i < self.hiddenRowRanges.length; i++) {
|
|
1444
|
+
const [beginRowIndex, endRowIndex] = self.hiddenRowRanges[i];
|
|
1445
|
+
if (
|
|
1446
|
+
cell.boundRowIndex === endRowIndex + 1 &&
|
|
1447
|
+
previousRowNumber === beginRowIndex - 1
|
|
1448
|
+
) {
|
|
1449
|
+
hasRowGap = false;
|
|
1450
|
+
break;
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1372
1454
|
|
|
1373
1455
|
if (hasRowGap) {
|
|
1374
1456
|
const barHeight = self.style.rowHeaderCellRowNumberGapHeight;
|
|
@@ -1384,9 +1466,141 @@ export default function (self) {
|
|
|
1384
1466
|
}
|
|
1385
1467
|
}
|
|
1386
1468
|
|
|
1469
|
+
//#region draw unhide indicator for column headers
|
|
1470
|
+
if (isColumnHeader && self.attributes.showUnhideColumnsIndicator) {
|
|
1471
|
+
const hovered = self.hovers.unhideIndicator;
|
|
1472
|
+
const size = self.style.unhideIndicatorSize;
|
|
1473
|
+
const cellX = x;
|
|
1474
|
+
const topY = cell.y + Math.max(0.5 * (cell.height - size), 0);
|
|
1475
|
+
|
|
1476
|
+
const isActive = (orderIndex) =>
|
|
1477
|
+
hovered &&
|
|
1478
|
+
(hovered.dir === 'l' || hovered.dir === 'r') &&
|
|
1479
|
+
orderIndex >= hovered.orderIndex0 &&
|
|
1480
|
+
orderIndex <= hovered.orderIndex1;
|
|
1481
|
+
const isHiddenColumn = (columnIndex) =>
|
|
1482
|
+
columnIndex >= 0 &&
|
|
1483
|
+
schema[columnIndex] &&
|
|
1484
|
+
schema[columnIndex].hidden;
|
|
1485
|
+
|
|
1486
|
+
let orderIndex0, orderIndex1;
|
|
1487
|
+
const drawIndicator = (leftX, dir, active) => {
|
|
1488
|
+
self.visibleUnhideIndicators.push({
|
|
1489
|
+
x: leftX - 1,
|
|
1490
|
+
y: topY - 1,
|
|
1491
|
+
x2: leftX + size + 2,
|
|
1492
|
+
y2: topY + size + 2,
|
|
1493
|
+
orderIndex0,
|
|
1494
|
+
orderIndex1,
|
|
1495
|
+
dir,
|
|
1496
|
+
});
|
|
1497
|
+
if (!active) {
|
|
1498
|
+
const line = cell.text && cell.text.lines && cell.text.lines[0];
|
|
1499
|
+
if (line) {
|
|
1500
|
+
const iconsWidth = orderByArrowSize + treeArrowSize;
|
|
1501
|
+
const lineX0 = iconsWidth > 0 ? iconsWidth : line.x;
|
|
1502
|
+
const lineX1 = line.x + line.width;
|
|
1503
|
+
if (leftX + size >= lineX0 && leftX <= lineX1) return;
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
drawUnhideIndicator(leftX, topY, size, dir, active);
|
|
1507
|
+
}; // end of drawIndicator
|
|
1508
|
+
|
|
1509
|
+
let orderIndexPtr = columnOrderIndex - 1;
|
|
1510
|
+
const prevColumnIndex = self.orders.columns[orderIndexPtr];
|
|
1511
|
+
if (isHiddenColumn(prevColumnIndex)) {
|
|
1512
|
+
const active = isActive(prevColumnIndex);
|
|
1513
|
+
orderIndex0 = orderIndexPtr;
|
|
1514
|
+
orderIndex1 = orderIndexPtr;
|
|
1515
|
+
while (--orderIndexPtr >= 0) {
|
|
1516
|
+
if (isHiddenColumn(self.orders.columns[orderIndexPtr]))
|
|
1517
|
+
orderIndex0 = orderIndexPtr;
|
|
1518
|
+
else break;
|
|
1519
|
+
}
|
|
1520
|
+
drawIndicator(cellX, 'r', active);
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
orderIndexPtr = columnOrderIndex + 1;
|
|
1524
|
+
const nextColumnIndex = self.orders.columns[orderIndexPtr];
|
|
1525
|
+
if (isHiddenColumn(nextColumnIndex)) {
|
|
1526
|
+
const active = isActive(nextColumnIndex);
|
|
1527
|
+
orderIndex0 = orderIndexPtr;
|
|
1528
|
+
orderIndex1 = orderIndexPtr;
|
|
1529
|
+
while (++orderIndexPtr < self.orders.columns.length) {
|
|
1530
|
+
if (isHiddenColumn(self.orders.columns[orderIndexPtr]))
|
|
1531
|
+
orderIndex1 = orderIndexPtr;
|
|
1532
|
+
else break;
|
|
1533
|
+
}
|
|
1534
|
+
const indicatorX = x + cell.width - size;
|
|
1535
|
+
drawIndicator(indicatorX, 'l', active);
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
//#endregion draw unhide indicator for column headers
|
|
1539
|
+
|
|
1540
|
+
//#region draw unhide indicator for row headers
|
|
1541
|
+
if (
|
|
1542
|
+
isRowHeader &&
|
|
1543
|
+
self.attributes.showUnhideRowsIndicator &&
|
|
1544
|
+
self.hiddenRowRanges.length > 0
|
|
1545
|
+
) {
|
|
1546
|
+
// Leo's comment:
|
|
1547
|
+
// from the first row to the last row, `rowIndex` is from 0 to the count of rows
|
|
1548
|
+
// but `boundRowIndex` can be disordered if there are any ordered columns or filtered columns
|
|
1549
|
+
// Like this statement:
|
|
1550
|
+
// console.log(rowIndex, cell.boundRowIndex, cell.formattedValue);
|
|
1551
|
+
// can output the result like this:
|
|
1552
|
+
// 0 1 '2'
|
|
1553
|
+
// 1 3 '4'
|
|
1554
|
+
const hovered = self.hovers.unhideIndicator;
|
|
1555
|
+
const size = self.style.unhideIndicatorSize;
|
|
1556
|
+
const leftX = cell.x + cell.width - size - 2;
|
|
1557
|
+
const cellY = y;
|
|
1558
|
+
const topIndicators = {};
|
|
1559
|
+
const bottomIndicators = {};
|
|
1560
|
+
self.hiddenRowRanges.forEach((it) => {
|
|
1561
|
+
topIndicators[it[0] - 1] = it;
|
|
1562
|
+
bottomIndicators[it[1] + 1] = it;
|
|
1563
|
+
});
|
|
1564
|
+
|
|
1565
|
+
const rowIndex = cell.rowIndex + currentRowIndexOffset;
|
|
1566
|
+
const isActive = () =>
|
|
1567
|
+
hovered &&
|
|
1568
|
+
(hovered.dir === 't' || hovered.dir === 'b') &&
|
|
1569
|
+
rowIndex >= hovered.orderIndex0 - 1 &&
|
|
1570
|
+
rowIndex <= hovered.orderIndex1 + 1;
|
|
1571
|
+
|
|
1572
|
+
let orderIndex0, orderIndex1;
|
|
1573
|
+
const drawIndicator = (topY, dir, active) => {
|
|
1574
|
+
self.visibleUnhideIndicators.push({
|
|
1575
|
+
x: leftX - 1,
|
|
1576
|
+
y: topY - 1,
|
|
1577
|
+
x2: leftX + size + 2,
|
|
1578
|
+
y2: topY + size + 2,
|
|
1579
|
+
orderIndex0,
|
|
1580
|
+
orderIndex1,
|
|
1581
|
+
dir,
|
|
1582
|
+
});
|
|
1583
|
+
drawUnhideIndicator(leftX, topY, size, dir, active);
|
|
1584
|
+
}; // end of drawIndicator
|
|
1585
|
+
|
|
1586
|
+
let matched = topIndicators[rowIndex];
|
|
1587
|
+
if (matched) {
|
|
1588
|
+
const indicatorY = cellY + cell.height - size;
|
|
1589
|
+
[orderIndex0, orderIndex1] = matched;
|
|
1590
|
+
drawIndicator(indicatorY, 't', isActive());
|
|
1591
|
+
}
|
|
1592
|
+
matched = bottomIndicators[rowIndex];
|
|
1593
|
+
if (matched) {
|
|
1594
|
+
const indicatorY = cellY;
|
|
1595
|
+
[orderIndex0, orderIndex1] = matched;
|
|
1596
|
+
drawIndicator(indicatorY, 'b', isActive());
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
//#endregion draw unhide indicator for row headers
|
|
1600
|
+
|
|
1387
1601
|
x += cell.width + (bc ? 0 : self.style.cellBorderWidth);
|
|
1388
1602
|
return cell.width;
|
|
1389
|
-
};
|
|
1603
|
+
}; // end of drawEach
|
|
1390
1604
|
}
|
|
1391
1605
|
function drawFilterButton(cell, ev) {
|
|
1392
1606
|
var posX = cell.x + cell.width - self.style.filterButtonWidth - 1;
|
|
@@ -1424,17 +1638,20 @@ export default function (self) {
|
|
|
1424
1638
|
// as it is in the unfiltered data, instead of simply the viewed
|
|
1425
1639
|
// row index + 1. If rowIndex > viewData.length, it's a new row
|
|
1426
1640
|
// added to the end, and we want to render that new row's number
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1641
|
+
let filteredRowNumber;
|
|
1642
|
+
if (self.viewData && rowIndex < self.viewData.length)
|
|
1643
|
+
filteredRowNumber =
|
|
1644
|
+
self.getBoundRowIndexFromViewRowIndex(rowIndex) + 1;
|
|
1645
|
+
else
|
|
1646
|
+
filteredRowNumber = self.originalData
|
|
1431
1647
|
? self.originalData.length + 1
|
|
1432
1648
|
: rowOrderIndex + 1;
|
|
1433
1649
|
|
|
1434
|
-
|
|
1650
|
+
let rowHeaderValue =
|
|
1435
1651
|
self.hasActiveFilters() || self.hasCollapsedRowGroup()
|
|
1436
1652
|
? filteredRowNumber
|
|
1437
1653
|
: rowIndex + 1;
|
|
1654
|
+
rowHeaderValue += currentRowIndexOffset;
|
|
1438
1655
|
|
|
1439
1656
|
const rowHeaderCell = { rowHeaderCell: rowHeaderValue };
|
|
1440
1657
|
const headerDescription = {
|
|
@@ -1451,6 +1668,14 @@ export default function (self) {
|
|
|
1451
1668
|
-1,
|
|
1452
1669
|
-1,
|
|
1453
1670
|
);
|
|
1671
|
+
|
|
1672
|
+
if (
|
|
1673
|
+
rowIndexOffsetByHiddenRows[0] &&
|
|
1674
|
+
rowHeaderValue >= rowIndexOffsetByHiddenRows[0].from
|
|
1675
|
+
) {
|
|
1676
|
+
const { plus } = rowIndexOffsetByHiddenRows.shift();
|
|
1677
|
+
currentRowIndexOffset += plus;
|
|
1678
|
+
}
|
|
1454
1679
|
}
|
|
1455
1680
|
}
|
|
1456
1681
|
function drawHeaders() {
|
|
@@ -1675,12 +1900,20 @@ export default function (self) {
|
|
|
1675
1900
|
schema = self.getSchema();
|
|
1676
1901
|
self.visibleCells = [];
|
|
1677
1902
|
self.visibleGroups = [];
|
|
1903
|
+
self.visibleUnhideIndicators = [];
|
|
1678
1904
|
self.canvasOffsetTop = self.isChildGrid ? self.parentNode.offsetTop : 0.5;
|
|
1679
1905
|
self.canvasOffsetLeft = self.isChildGrid
|
|
1680
1906
|
? self.parentNode.offsetLeft
|
|
1681
1907
|
: -0.5;
|
|
1682
1908
|
h = self.height;
|
|
1683
1909
|
w = self.width;
|
|
1910
|
+
|
|
1911
|
+
// patch for first row being hidden
|
|
1912
|
+
const firstRowIndexOffset = rowIndexOffsetByHiddenRows[0];
|
|
1913
|
+
if (firstRowIndexOffset && firstRowIndexOffset.from === 0) {
|
|
1914
|
+
currentRowIndexOffset = firstRowIndexOffset.plus;
|
|
1915
|
+
rowIndexOffsetByHiddenRows.shift();
|
|
1916
|
+
}
|
|
1684
1917
|
}
|
|
1685
1918
|
function drawBackground() {
|
|
1686
1919
|
radiusRect(0, 0, w, h, 0);
|
|
@@ -1876,7 +2109,7 @@ export default function (self) {
|
|
|
1876
2109
|
leftX -= toggleHandlePadding + 1;
|
|
1877
2110
|
if (containsEnd) rightX += toggleHandleSize;
|
|
1878
2111
|
}
|
|
1879
|
-
|
|
2112
|
+
drawLines(lineCoords);
|
|
1880
2113
|
ctx.restore();
|
|
1881
2114
|
pushToVisibleGroups(leftX, rightX);
|
|
1882
2115
|
}
|
|
@@ -2054,7 +2287,7 @@ export default function (self) {
|
|
|
2054
2287
|
// add more clickable area into `visibleGroups`
|
|
2055
2288
|
if (containsEnd) bottomY += toggleHandleSize;
|
|
2056
2289
|
}
|
|
2057
|
-
|
|
2290
|
+
drawLines(lineCoords);
|
|
2058
2291
|
ctx.restore();
|
|
2059
2292
|
pushToVisibleGroups(topY, bottomY);
|
|
2060
2293
|
}
|