@alaarab/ogrid-core 2.1.15 → 2.2.0
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/dist/esm/index.js +294 -1
- package/dist/types/index.d.ts +4 -2
- package/dist/types/types/dataGridTypes.d.ts +4 -0
- package/dist/types/utils/index.d.ts +4 -2
- package/dist/types/utils/virtualScroll.d.ts +34 -0
- package/dist/types/utils/workerSortFilter.d.ts +29 -0
- package/dist/types/workers/sortFilterWorker.d.ts +48 -0
- package/package.json +1 -1
package/dist/esm/index.js
CHANGED
|
@@ -738,6 +738,66 @@ function calculateDropTarget(params) {
|
|
|
738
738
|
}
|
|
739
739
|
|
|
740
740
|
// src/utils/virtualScroll.ts
|
|
741
|
+
function computeVisibleColumnRange(scrollLeft, columnWidths, containerWidth, overscan = 2) {
|
|
742
|
+
if (columnWidths.length === 0 || containerWidth <= 0) {
|
|
743
|
+
return { startIndex: 0, endIndex: -1, leftOffset: 0, rightOffset: 0 };
|
|
744
|
+
}
|
|
745
|
+
let cumWidth = 0;
|
|
746
|
+
let rawStart = columnWidths.length;
|
|
747
|
+
let rawEnd = -1;
|
|
748
|
+
for (let i = 0; i < columnWidths.length; i++) {
|
|
749
|
+
const colStart = cumWidth;
|
|
750
|
+
cumWidth += columnWidths[i];
|
|
751
|
+
if (cumWidth > scrollLeft && rawStart === columnWidths.length) {
|
|
752
|
+
rawStart = i;
|
|
753
|
+
}
|
|
754
|
+
if (colStart < scrollLeft + containerWidth) {
|
|
755
|
+
rawEnd = i;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
if (rawStart > rawEnd) {
|
|
759
|
+
return { startIndex: 0, endIndex: -1, leftOffset: 0, rightOffset: 0 };
|
|
760
|
+
}
|
|
761
|
+
const startIndex = Math.max(0, rawStart - overscan);
|
|
762
|
+
const endIndex = Math.min(columnWidths.length - 1, rawEnd + overscan);
|
|
763
|
+
let leftOffset = 0;
|
|
764
|
+
for (let i = 0; i < startIndex; i++) {
|
|
765
|
+
leftOffset += columnWidths[i];
|
|
766
|
+
}
|
|
767
|
+
let rightOffset = 0;
|
|
768
|
+
for (let i = endIndex + 1; i < columnWidths.length; i++) {
|
|
769
|
+
rightOffset += columnWidths[i];
|
|
770
|
+
}
|
|
771
|
+
return { startIndex, endIndex, leftOffset, rightOffset };
|
|
772
|
+
}
|
|
773
|
+
function partitionColumnsForVirtualization(visibleCols, columnRange, pinnedColumns) {
|
|
774
|
+
const pinnedLeft = [];
|
|
775
|
+
const pinnedRight = [];
|
|
776
|
+
const unpinned = [];
|
|
777
|
+
for (const col of visibleCols) {
|
|
778
|
+
const pin = pinnedColumns?.[col.columnId];
|
|
779
|
+
if (pin === "left") pinnedLeft.push(col);
|
|
780
|
+
else if (pin === "right") pinnedRight.push(col);
|
|
781
|
+
else unpinned.push(col);
|
|
782
|
+
}
|
|
783
|
+
if (!columnRange || columnRange.endIndex < 0) {
|
|
784
|
+
return {
|
|
785
|
+
pinnedLeft,
|
|
786
|
+
virtualizedUnpinned: unpinned,
|
|
787
|
+
pinnedRight,
|
|
788
|
+
leftSpacerWidth: 0,
|
|
789
|
+
rightSpacerWidth: 0
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
const virtualizedUnpinned = unpinned.slice(columnRange.startIndex, columnRange.endIndex + 1);
|
|
793
|
+
return {
|
|
794
|
+
pinnedLeft,
|
|
795
|
+
virtualizedUnpinned,
|
|
796
|
+
pinnedRight,
|
|
797
|
+
leftSpacerWidth: columnRange.leftOffset,
|
|
798
|
+
rightSpacerWidth: columnRange.rightOffset
|
|
799
|
+
};
|
|
800
|
+
}
|
|
741
801
|
function computeVisibleRange(scrollTop, rowHeight, containerHeight, totalRows, overscan = 5) {
|
|
742
802
|
if (totalRows <= 0 || rowHeight <= 0 || containerHeight <= 0) {
|
|
743
803
|
return { startIndex: 0, endIndex: 0, offsetTop: 0, offsetBottom: 0 };
|
|
@@ -766,6 +826,239 @@ function getScrollTopForRow(rowIndex, rowHeight, containerHeight, align = "start
|
|
|
766
826
|
}
|
|
767
827
|
}
|
|
768
828
|
|
|
829
|
+
// src/workers/sortFilterWorker.ts
|
|
830
|
+
function workerBody() {
|
|
831
|
+
const ctx = self;
|
|
832
|
+
ctx.onmessage = (e) => {
|
|
833
|
+
const msg = e.data;
|
|
834
|
+
if (msg.type !== "sort-filter") return;
|
|
835
|
+
const { requestId, values, filters, sort } = msg;
|
|
836
|
+
const rowCount = values.length;
|
|
837
|
+
let indices = [];
|
|
838
|
+
const filterEntries = Object.entries(filters);
|
|
839
|
+
if (filterEntries.length === 0) {
|
|
840
|
+
indices = new Array(rowCount);
|
|
841
|
+
for (let i = 0; i < rowCount; i++) indices[i] = i;
|
|
842
|
+
} else {
|
|
843
|
+
for (let r = 0; r < rowCount; r++) {
|
|
844
|
+
let pass = true;
|
|
845
|
+
for (let f = 0; f < filterEntries.length; f++) {
|
|
846
|
+
const colIdx = Number(filterEntries[f][0]);
|
|
847
|
+
const filter = filterEntries[f][1];
|
|
848
|
+
const cellVal = values[r][colIdx];
|
|
849
|
+
switch (filter.type) {
|
|
850
|
+
case "text": {
|
|
851
|
+
const trimmed = filter.value.trim().toLowerCase();
|
|
852
|
+
if (trimmed && !String(cellVal ?? "").toLowerCase().includes(trimmed)) {
|
|
853
|
+
pass = false;
|
|
854
|
+
}
|
|
855
|
+
break;
|
|
856
|
+
}
|
|
857
|
+
case "multiSelect": {
|
|
858
|
+
if (filter.value.length > 0) {
|
|
859
|
+
const set = new Set(filter.value);
|
|
860
|
+
if (!set.has(String(cellVal ?? ""))) {
|
|
861
|
+
pass = false;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
break;
|
|
865
|
+
}
|
|
866
|
+
case "date": {
|
|
867
|
+
if (cellVal == null) {
|
|
868
|
+
pass = false;
|
|
869
|
+
break;
|
|
870
|
+
}
|
|
871
|
+
const ts = new Date(String(cellVal)).getTime();
|
|
872
|
+
if (isNaN(ts)) {
|
|
873
|
+
pass = false;
|
|
874
|
+
break;
|
|
875
|
+
}
|
|
876
|
+
if (filter.value.from) {
|
|
877
|
+
const fromTs = (/* @__PURE__ */ new Date(filter.value.from + "T00:00:00")).getTime();
|
|
878
|
+
if (ts < fromTs) {
|
|
879
|
+
pass = false;
|
|
880
|
+
break;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
if (filter.value.to) {
|
|
884
|
+
const toTs = (/* @__PURE__ */ new Date(filter.value.to + "T23:59:59.999")).getTime();
|
|
885
|
+
if (ts > toTs) {
|
|
886
|
+
pass = false;
|
|
887
|
+
break;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
break;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
if (!pass) break;
|
|
894
|
+
}
|
|
895
|
+
if (pass) indices.push(r);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
if (sort) {
|
|
899
|
+
const { columnIndex, direction } = sort;
|
|
900
|
+
const dir = direction === "asc" ? 1 : -1;
|
|
901
|
+
indices.sort((a, b) => {
|
|
902
|
+
const av = values[a][columnIndex];
|
|
903
|
+
const bv = values[b][columnIndex];
|
|
904
|
+
if (av == null && bv == null) return 0;
|
|
905
|
+
if (av == null) return -1 * dir;
|
|
906
|
+
if (bv == null) return 1 * dir;
|
|
907
|
+
if (typeof av === "number" && typeof bv === "number") {
|
|
908
|
+
return av === bv ? 0 : av > bv ? dir : -dir;
|
|
909
|
+
}
|
|
910
|
+
const sa = String(av).toLowerCase();
|
|
911
|
+
const sb = String(bv).toLowerCase();
|
|
912
|
+
return sa === sb ? 0 : sa > sb ? dir : -dir;
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
const response = {
|
|
916
|
+
type: "sort-filter-result",
|
|
917
|
+
requestId,
|
|
918
|
+
indices
|
|
919
|
+
};
|
|
920
|
+
ctx.postMessage(response);
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// src/utils/workerSortFilter.ts
|
|
925
|
+
var workerInstance = null;
|
|
926
|
+
var requestCounter = 0;
|
|
927
|
+
var pendingRequests = /* @__PURE__ */ new Map();
|
|
928
|
+
function createSortFilterWorker() {
|
|
929
|
+
if (workerInstance) return workerInstance;
|
|
930
|
+
if (typeof Worker === "undefined" || typeof Blob === "undefined" || typeof URL === "undefined") {
|
|
931
|
+
return null;
|
|
932
|
+
}
|
|
933
|
+
try {
|
|
934
|
+
const fnStr = workerBody.toString();
|
|
935
|
+
const blob = new Blob(
|
|
936
|
+
[`(${fnStr})()`],
|
|
937
|
+
{ type: "application/javascript" }
|
|
938
|
+
);
|
|
939
|
+
const url = URL.createObjectURL(blob);
|
|
940
|
+
workerInstance = new Worker(url);
|
|
941
|
+
URL.revokeObjectURL(url);
|
|
942
|
+
workerInstance.onmessage = (e) => {
|
|
943
|
+
const { requestId, indices } = e.data;
|
|
944
|
+
const pending = pendingRequests.get(requestId);
|
|
945
|
+
if (pending) {
|
|
946
|
+
pendingRequests.delete(requestId);
|
|
947
|
+
pending.resolve(indices);
|
|
948
|
+
}
|
|
949
|
+
};
|
|
950
|
+
workerInstance.onerror = (err) => {
|
|
951
|
+
for (const [id, pending] of pendingRequests) {
|
|
952
|
+
pending.reject(new Error(err.message || "Worker error"));
|
|
953
|
+
pendingRequests.delete(id);
|
|
954
|
+
}
|
|
955
|
+
};
|
|
956
|
+
return workerInstance;
|
|
957
|
+
} catch {
|
|
958
|
+
return null;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
function terminateSortFilterWorker() {
|
|
962
|
+
if (workerInstance) {
|
|
963
|
+
workerInstance.terminate();
|
|
964
|
+
workerInstance = null;
|
|
965
|
+
}
|
|
966
|
+
for (const [id, pending] of pendingRequests) {
|
|
967
|
+
pending.reject(new Error("Worker terminated"));
|
|
968
|
+
pendingRequests.delete(id);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
function extractValueMatrix(data, columns) {
|
|
972
|
+
const matrix = new Array(data.length);
|
|
973
|
+
for (let r = 0; r < data.length; r++) {
|
|
974
|
+
const row = new Array(columns.length);
|
|
975
|
+
for (let c = 0; c < columns.length; c++) {
|
|
976
|
+
const val = getCellValue(data[r], columns[c]);
|
|
977
|
+
if (val == null) {
|
|
978
|
+
row[c] = null;
|
|
979
|
+
} else if (typeof val === "string" || typeof val === "number" || typeof val === "boolean") {
|
|
980
|
+
row[c] = val;
|
|
981
|
+
} else {
|
|
982
|
+
row[c] = String(val);
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
matrix[r] = row;
|
|
986
|
+
}
|
|
987
|
+
return matrix;
|
|
988
|
+
}
|
|
989
|
+
function processClientSideDataAsync(data, columns, filters, sortBy, sortDirection) {
|
|
990
|
+
if (sortBy) {
|
|
991
|
+
const sortCol = columns.find((c) => c.columnId === sortBy);
|
|
992
|
+
if (sortCol?.compare) {
|
|
993
|
+
return Promise.resolve(processClientSideData(data, columns, filters, sortBy, sortDirection));
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
const worker = createSortFilterWorker();
|
|
997
|
+
if (!worker) {
|
|
998
|
+
return Promise.resolve(processClientSideData(data, columns, filters, sortBy, sortDirection));
|
|
999
|
+
}
|
|
1000
|
+
const columnIndexMap = /* @__PURE__ */ new Map();
|
|
1001
|
+
for (let i = 0; i < columns.length; i++) {
|
|
1002
|
+
columnIndexMap.set(columns[i].columnId, i);
|
|
1003
|
+
}
|
|
1004
|
+
const values = extractValueMatrix(data, columns);
|
|
1005
|
+
const columnMeta = columns.map((col, idx) => ({
|
|
1006
|
+
type: col.type ?? "text",
|
|
1007
|
+
index: idx
|
|
1008
|
+
}));
|
|
1009
|
+
const workerFilters = {};
|
|
1010
|
+
for (const col of columns) {
|
|
1011
|
+
const filterKey = getFilterField(col);
|
|
1012
|
+
const val = filters[filterKey];
|
|
1013
|
+
if (!val) continue;
|
|
1014
|
+
const colIdx = columnIndexMap.get(col.columnId);
|
|
1015
|
+
if (colIdx === void 0) continue;
|
|
1016
|
+
switch (val.type) {
|
|
1017
|
+
case "text":
|
|
1018
|
+
workerFilters[colIdx] = { type: "text", value: val.value };
|
|
1019
|
+
break;
|
|
1020
|
+
case "multiSelect":
|
|
1021
|
+
workerFilters[colIdx] = { type: "multiSelect", value: val.value };
|
|
1022
|
+
break;
|
|
1023
|
+
case "date":
|
|
1024
|
+
workerFilters[colIdx] = { type: "date", value: { from: val.value.from, to: val.value.to } };
|
|
1025
|
+
break;
|
|
1026
|
+
// 'people' filter has a UserLike object — fall back to sync
|
|
1027
|
+
case "people":
|
|
1028
|
+
return Promise.resolve(processClientSideData(data, columns, filters, sortBy, sortDirection));
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
let sort;
|
|
1032
|
+
if (sortBy) {
|
|
1033
|
+
const sortColIdx = columnIndexMap.get(sortBy);
|
|
1034
|
+
if (sortColIdx !== void 0) {
|
|
1035
|
+
sort = { columnIndex: sortColIdx, direction: sortDirection ?? "asc" };
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
const requestId = ++requestCounter;
|
|
1039
|
+
return new Promise((resolve, reject) => {
|
|
1040
|
+
pendingRequests.set(requestId, {
|
|
1041
|
+
resolve: (indices) => {
|
|
1042
|
+
const result = new Array(indices.length);
|
|
1043
|
+
for (let i = 0; i < indices.length; i++) {
|
|
1044
|
+
result[i] = data[indices[i]];
|
|
1045
|
+
}
|
|
1046
|
+
resolve(result);
|
|
1047
|
+
},
|
|
1048
|
+
reject
|
|
1049
|
+
});
|
|
1050
|
+
const request = {
|
|
1051
|
+
type: "sort-filter",
|
|
1052
|
+
requestId,
|
|
1053
|
+
values,
|
|
1054
|
+
columnMeta,
|
|
1055
|
+
filters: workerFilters,
|
|
1056
|
+
sort
|
|
1057
|
+
};
|
|
1058
|
+
worker.postMessage(request);
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
|
|
769
1062
|
// src/utils/dataGridViewModel.ts
|
|
770
1063
|
function getHeaderFilterConfig(col, input) {
|
|
771
1064
|
const filterable = isFilterConfig(col.filterable) ? col.filterable : null;
|
|
@@ -1652,4 +1945,4 @@ var Z_INDEX = {
|
|
|
1652
1945
|
CONTEXT_MENU: 1e4
|
|
1653
1946
|
};
|
|
1654
1947
|
|
|
1655
|
-
export { AUTOSIZE_EXTRA_PX, AUTOSIZE_MAX_PX, CELL_PADDING, CHECKBOX_COLUMN_WIDTH, COLUMN_HEADER_MENU_ITEMS, CellDescriptorCache, DEFAULT_DEBOUNCE_MS, DEFAULT_MIN_COLUMN_WIDTH, GRID_BORDER_RADIUS, GRID_CONTEXT_MENU_ITEMS, MAX_PAGE_BUTTONS, PAGE_SIZE_OPTIONS, PEOPLE_SEARCH_DEBOUNCE_MS, ROW_NUMBER_COLUMN_WIDTH, SIDEBAR_TRANSITION_MS, UndoRedoStack, Z_INDEX, applyCellDeletion, applyCutClear, applyFillValues, applyPastedValues, applyRangeRowSelection, areGridRowPropsEqual, booleanParser, buildCellIndex, buildCsvHeader, buildCsvRows, buildHeaderRows, buildInlineEditorProps, buildPopoverEditorProps, calculateDropTarget, clampSelectionToBounds, computeAggregations, computeArrowNavigation, computeAutoScrollSpeed, computeNextSortState, computeRowSelectionState, computeTabNavigation, computeTotalHeight, computeVisibleRange, currencyParser, dateParser, debounce, deriveFilterOptionsFromData, emailParser, escapeCsvValue, exportToCsv, findCtrlArrowTarget, flattenColumns, formatCellValueForTsv, formatSelectionAsTsv, formatShortcut, getCellRenderDescriptor, getCellValue, getColumnHeaderMenuItems, getContextMenuHandlers, getDataGridStatusBarConfig, getFilterField, getHeaderFilterConfig, getMultiSelectFilterFields, getPaginationViewModel, getPinStateForColumn, getScrollTopForRow, getStatusBarParts, injectGlobalStyles, isColumnEditable, isFilterConfig, isInSelectionRange, isRowInRange, measureColumnContentWidth, measureRange, mergeFilter, normalizeSelectionRange, numberParser, parseTsvClipboard, parseValue, processClientSideData, rangesEqual, reorderColumnArray, resolveCellDisplayContent, resolveCellStyle, toUserLike, triggerCsvDownload, validateColumns, validateRowIds, validateVirtualScrollConfig };
|
|
1948
|
+
export { AUTOSIZE_EXTRA_PX, AUTOSIZE_MAX_PX, CELL_PADDING, CHECKBOX_COLUMN_WIDTH, COLUMN_HEADER_MENU_ITEMS, CellDescriptorCache, DEFAULT_DEBOUNCE_MS, DEFAULT_MIN_COLUMN_WIDTH, GRID_BORDER_RADIUS, GRID_CONTEXT_MENU_ITEMS, MAX_PAGE_BUTTONS, PAGE_SIZE_OPTIONS, PEOPLE_SEARCH_DEBOUNCE_MS, ROW_NUMBER_COLUMN_WIDTH, SIDEBAR_TRANSITION_MS, UndoRedoStack, Z_INDEX, applyCellDeletion, applyCutClear, applyFillValues, applyPastedValues, applyRangeRowSelection, areGridRowPropsEqual, booleanParser, buildCellIndex, buildCsvHeader, buildCsvRows, buildHeaderRows, buildInlineEditorProps, buildPopoverEditorProps, calculateDropTarget, clampSelectionToBounds, computeAggregations, computeArrowNavigation, computeAutoScrollSpeed, computeNextSortState, computeRowSelectionState, computeTabNavigation, computeTotalHeight, computeVisibleColumnRange, computeVisibleRange, createSortFilterWorker, currencyParser, dateParser, debounce, deriveFilterOptionsFromData, emailParser, escapeCsvValue, exportToCsv, extractValueMatrix, findCtrlArrowTarget, flattenColumns, formatCellValueForTsv, formatSelectionAsTsv, formatShortcut, getCellRenderDescriptor, getCellValue, getColumnHeaderMenuItems, getContextMenuHandlers, getDataGridStatusBarConfig, getFilterField, getHeaderFilterConfig, getMultiSelectFilterFields, getPaginationViewModel, getPinStateForColumn, getScrollTopForRow, getStatusBarParts, injectGlobalStyles, isColumnEditable, isFilterConfig, isInSelectionRange, isRowInRange, measureColumnContentWidth, measureRange, mergeFilter, normalizeSelectionRange, numberParser, parseTsvClipboard, parseValue, partitionColumnsForVirtualization, processClientSideData, processClientSideDataAsync, rangesEqual, reorderColumnArray, resolveCellDisplayContent, resolveCellStyle, terminateSortFilterWorker, toUserLike, triggerCsvDownload, validateColumns, validateRowIds, validateVirtualScrollConfig };
|
package/dist/types/index.d.ts
CHANGED
|
@@ -22,8 +22,10 @@ export { areGridRowPropsEqual, isRowInRange } from './utils';
|
|
|
22
22
|
export type { GridRowComparatorProps } from './utils';
|
|
23
23
|
export { getPinStateForColumn, reorderColumnArray, calculateDropTarget, } from './utils';
|
|
24
24
|
export type { ColumnPinState, IDropTarget, ICalculateDropTargetParams } from './utils';
|
|
25
|
-
export { computeVisibleRange, computeTotalHeight, getScrollTopForRow, } from './utils';
|
|
26
|
-
export type { IVisibleRange } from './utils';
|
|
25
|
+
export { computeVisibleRange, computeTotalHeight, getScrollTopForRow, computeVisibleColumnRange, partitionColumnsForVirtualization, } from './utils';
|
|
26
|
+
export type { IVisibleRange, IVisibleColumnRange } from './utils';
|
|
27
|
+
export { createSortFilterWorker, terminateSortFilterWorker, extractValueMatrix, processClientSideDataAsync, } from './utils';
|
|
28
|
+
export type { SortFilterRequest, SortFilterResponse } from './utils';
|
|
27
29
|
export { getHeaderFilterConfig, getCellRenderDescriptor, CellDescriptorCache, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, } from './utils';
|
|
28
30
|
export type { HeaderFilterConfigInput, HeaderFilterConfig, CellRenderDescriptorInput, CellRenderDescriptor, CellRenderMode, } from './utils';
|
|
29
31
|
export { debounce } from './utils';
|
|
@@ -143,6 +143,10 @@ export interface IVirtualScrollConfig {
|
|
|
143
143
|
* higher values keep small grids fully rendered (no scroll offset artifacts).
|
|
144
144
|
*/
|
|
145
145
|
threshold?: number;
|
|
146
|
+
/** Enable column virtualization — only render visible columns (default: false). */
|
|
147
|
+
columns?: boolean;
|
|
148
|
+
/** Number of extra columns to render outside the visible area (default: 2). */
|
|
149
|
+
columnOverscan?: number;
|
|
146
150
|
}
|
|
147
151
|
/** Configuration for column reordering via drag-and-drop. */
|
|
148
152
|
export interface IColumnReorderConfig {
|
|
@@ -19,8 +19,10 @@ export { areGridRowPropsEqual, isRowInRange } from './gridRowComparator';
|
|
|
19
19
|
export type { GridRowComparatorProps } from './gridRowComparator';
|
|
20
20
|
export { getPinStateForColumn, reorderColumnArray, calculateDropTarget, } from './columnReorder';
|
|
21
21
|
export type { ColumnPinState, IDropTarget, ICalculateDropTargetParams } from './columnReorder';
|
|
22
|
-
export { computeVisibleRange, computeTotalHeight, getScrollTopForRow, } from './virtualScroll';
|
|
23
|
-
export type { IVisibleRange } from './virtualScroll';
|
|
22
|
+
export { computeVisibleRange, computeTotalHeight, getScrollTopForRow, computeVisibleColumnRange, partitionColumnsForVirtualization, } from './virtualScroll';
|
|
23
|
+
export type { IVisibleRange, IVisibleColumnRange } from './virtualScroll';
|
|
24
|
+
export { createSortFilterWorker, terminateSortFilterWorker, extractValueMatrix, processClientSideDataAsync, } from './workerSortFilter';
|
|
25
|
+
export type { SortFilterRequest, SortFilterResponse } from '../workers/sortFilterWorker';
|
|
24
26
|
export { getHeaderFilterConfig, getCellRenderDescriptor, CellDescriptorCache, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, } from './dataGridViewModel';
|
|
25
27
|
export type { HeaderFilterConfigInput, HeaderFilterConfig, CellRenderDescriptorInput, CellRenderDescriptor, CellRenderMode, } from './dataGridViewModel';
|
|
26
28
|
export { debounce } from './debounce';
|
|
@@ -1,3 +1,37 @@
|
|
|
1
|
+
import type { IColumnDef } from '../types';
|
|
2
|
+
/** The visible column range for horizontal virtualization. */
|
|
3
|
+
export interface IVisibleColumnRange {
|
|
4
|
+
/** First visible unpinned column index (inclusive, accounting for overscan). */
|
|
5
|
+
startIndex: number;
|
|
6
|
+
/** Last visible unpinned column index (inclusive, accounting for overscan). */
|
|
7
|
+
endIndex: number;
|
|
8
|
+
/** Pixel width of the left spacer (for columns before the visible range). */
|
|
9
|
+
leftOffset: number;
|
|
10
|
+
/** Pixel width of the right spacer (for columns after the visible range). */
|
|
11
|
+
rightOffset: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Compute the range of columns visible in the horizontal viewport.
|
|
15
|
+
* Linear scan over cumulative widths to find first/last visible column.
|
|
16
|
+
*
|
|
17
|
+
* @param scrollLeft - Current horizontal scroll offset (px)
|
|
18
|
+
* @param columnWidths - Array of widths for each unpinned column (px)
|
|
19
|
+
* @param containerWidth - Visible width of the scroll container (px)
|
|
20
|
+
* @param overscan - Number of extra columns to render on each side (default: 2)
|
|
21
|
+
* @returns The visible column range with start/end indices and left/right spacer widths
|
|
22
|
+
*/
|
|
23
|
+
export declare function computeVisibleColumnRange(scrollLeft: number, columnWidths: number[], containerWidth: number, overscan?: number): IVisibleColumnRange;
|
|
24
|
+
/**
|
|
25
|
+
* Partition visible columns into pinned-left, virtualized-unpinned, and pinned-right.
|
|
26
|
+
* Pinned columns always render. Only unpinned columns in the visible range render.
|
|
27
|
+
*/
|
|
28
|
+
export declare function partitionColumnsForVirtualization<T>(visibleCols: IColumnDef<T>[], columnRange: IVisibleColumnRange | null, pinnedColumns?: Record<string, 'left' | 'right'>): {
|
|
29
|
+
pinnedLeft: IColumnDef<T>[];
|
|
30
|
+
virtualizedUnpinned: IColumnDef<T>[];
|
|
31
|
+
pinnedRight: IColumnDef<T>[];
|
|
32
|
+
leftSpacerWidth: number;
|
|
33
|
+
rightSpacerWidth: number;
|
|
34
|
+
};
|
|
1
35
|
/** The visible row range and spacer offsets for virtual scrolling. */
|
|
2
36
|
export interface IVisibleRange {
|
|
3
37
|
/** First visible row index (inclusive, accounting for overscan). */
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main-thread wrapper for the sort/filter Web Worker.
|
|
3
|
+
* Falls back to synchronous processClientSideData when:
|
|
4
|
+
* - Worker API is unavailable (SSR, jsdom)
|
|
5
|
+
* - Sort column has a custom `compare` function
|
|
6
|
+
*/
|
|
7
|
+
import type { IColumnDef, IFilters } from '../types';
|
|
8
|
+
/**
|
|
9
|
+
* Create (or reuse) the sort/filter Web Worker from an inline Blob URL.
|
|
10
|
+
* Returns null if the Worker API is unavailable.
|
|
11
|
+
*/
|
|
12
|
+
export declare function createSortFilterWorker(): Worker | null;
|
|
13
|
+
/**
|
|
14
|
+
* Terminate the sort/filter worker and clean up.
|
|
15
|
+
*/
|
|
16
|
+
export declare function terminateSortFilterWorker(): void;
|
|
17
|
+
/**
|
|
18
|
+
* Build a flat value matrix from data and columns.
|
|
19
|
+
* Each cell is extracted via getCellValue and coerced to a primitive.
|
|
20
|
+
*/
|
|
21
|
+
export declare function extractValueMatrix<T>(data: T[], columns: IColumnDef<T>[]): (string | number | boolean | null)[][];
|
|
22
|
+
/**
|
|
23
|
+
* Async version of processClientSideData that offloads to a Web Worker.
|
|
24
|
+
*
|
|
25
|
+
* Falls back to synchronous processing when:
|
|
26
|
+
* - Worker API is unavailable
|
|
27
|
+
* - Sort column has a custom `compare` function (not serializable)
|
|
28
|
+
*/
|
|
29
|
+
export declare function processClientSideDataAsync<T>(data: T[], columns: IColumnDef<T>[], filters: IFilters, sortBy?: string, sortDirection?: 'asc' | 'desc'): Promise<T[]>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web Worker script for offloading sort/filter to a background thread.
|
|
3
|
+
*
|
|
4
|
+
* Operates on flat primitives (no IColumnDef references) so it can be
|
|
5
|
+
* serialized into an inline Blob URL. The main thread sends a value matrix
|
|
6
|
+
* and column metadata; the worker applies filters + sort and returns row indices.
|
|
7
|
+
*/
|
|
8
|
+
export interface SortFilterRequest {
|
|
9
|
+
type: 'sort-filter';
|
|
10
|
+
requestId: number;
|
|
11
|
+
/** Flat value matrix: values[row][col] */
|
|
12
|
+
values: (string | number | boolean | null)[][];
|
|
13
|
+
/** Column metadata (only columns that participate in filter/sort). */
|
|
14
|
+
columnMeta: {
|
|
15
|
+
type: 'text' | 'numeric' | 'date' | 'boolean';
|
|
16
|
+
index: number;
|
|
17
|
+
}[];
|
|
18
|
+
/** Active filters keyed by column index in the values matrix. */
|
|
19
|
+
filters: Record<number, {
|
|
20
|
+
type: 'text';
|
|
21
|
+
value: string;
|
|
22
|
+
} | {
|
|
23
|
+
type: 'multiSelect';
|
|
24
|
+
value: string[];
|
|
25
|
+
} | {
|
|
26
|
+
type: 'date';
|
|
27
|
+
value: {
|
|
28
|
+
from?: string;
|
|
29
|
+
to?: string;
|
|
30
|
+
};
|
|
31
|
+
}>;
|
|
32
|
+
/** Sort spec (optional). */
|
|
33
|
+
sort?: {
|
|
34
|
+
columnIndex: number;
|
|
35
|
+
direction: 'asc' | 'desc';
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export interface SortFilterResponse {
|
|
39
|
+
type: 'sort-filter-result';
|
|
40
|
+
requestId: number;
|
|
41
|
+
/** Sorted/filtered row indices into the original data array. */
|
|
42
|
+
indices: number[];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The worker function body. This is stringified into an inline Blob URL
|
|
46
|
+
* by the main-thread wrapper, so it must be fully self-contained.
|
|
47
|
+
*/
|
|
48
|
+
export declare function workerBody(): void;
|
package/package.json
CHANGED