mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-29 16:18:55 +00:00
feat(grid): add transposed table view
This commit is contained in:
+690
-560
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,157 @@
|
||||
export interface DataGridTransposeState {
|
||||
showTranspose: boolean;
|
||||
transposeRowIndex: number | null;
|
||||
}
|
||||
|
||||
export interface BuildTransposeRowsOptions<T> {
|
||||
columns: string[];
|
||||
records: T[][];
|
||||
typeByColumn?: Map<string, string>;
|
||||
displayValue: (value: T, column: string, columnIndex: number, recordIndex: number) => string;
|
||||
}
|
||||
|
||||
export interface DataGridTransposeCell<T> {
|
||||
value: T;
|
||||
display: string;
|
||||
isNull: boolean;
|
||||
}
|
||||
|
||||
export interface DataGridTransposeRow<T> {
|
||||
id: string;
|
||||
column: string;
|
||||
type: string;
|
||||
values: Array<DataGridTransposeCell<T>>;
|
||||
}
|
||||
|
||||
export interface TransposeRecordWindowOptions {
|
||||
totalRecords: number;
|
||||
scrollLeft: number;
|
||||
viewportWidth: number;
|
||||
pinnedWidth: number;
|
||||
recordWidth: number;
|
||||
overscan?: number;
|
||||
}
|
||||
|
||||
export interface TransposeRecordWindow {
|
||||
start: number;
|
||||
end: number;
|
||||
beforeWidth: number;
|
||||
afterWidth: number;
|
||||
}
|
||||
|
||||
export interface TransposeSelectionRange {
|
||||
startRow: number;
|
||||
endRow: number;
|
||||
startCol: number;
|
||||
endCol: number;
|
||||
}
|
||||
|
||||
export interface TransposeAnchorOptions {
|
||||
requestedRowIndex: number;
|
||||
rowIds: number[];
|
||||
selectedRowIds: Set<number>;
|
||||
selectedRange: TransposeSelectionRange | null;
|
||||
}
|
||||
|
||||
export interface TransposeFieldWidthOptions {
|
||||
minWidth?: number;
|
||||
maxWidth?: number;
|
||||
charWidth?: number;
|
||||
padding?: number;
|
||||
}
|
||||
|
||||
export interface TransposeScrollLeftOptions {
|
||||
recordIndex: number;
|
||||
totalRecords: number;
|
||||
viewportWidth: number;
|
||||
pinnedWidth: number;
|
||||
recordWidth: number;
|
||||
}
|
||||
|
||||
export function nextTransposeState(
|
||||
showTranspose: boolean,
|
||||
transposeRowIndex: number | null,
|
||||
requestedRowIndex: number,
|
||||
): DataGridTransposeState {
|
||||
if (showTranspose && transposeRowIndex === requestedRowIndex) {
|
||||
return { showTranspose: false, transposeRowIndex: null };
|
||||
}
|
||||
return { showTranspose: true, transposeRowIndex: requestedRowIndex };
|
||||
}
|
||||
|
||||
export function buildTransposeRows<T>(options: BuildTransposeRowsOptions<T>): Array<DataGridTransposeRow<T>> {
|
||||
return options.columns.map((column, columnIndex) => {
|
||||
return {
|
||||
id: `${columnIndex}:${column}`,
|
||||
column,
|
||||
type: options.typeByColumn?.get(column) || "",
|
||||
values: options.records.map((record, recordIndex) => {
|
||||
const value = record[columnIndex] as T;
|
||||
return {
|
||||
value,
|
||||
display: options.displayValue(value, column, columnIndex, recordIndex),
|
||||
isNull: value === null,
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function visibleTransposeRecordWindow(options: TransposeRecordWindowOptions): TransposeRecordWindow {
|
||||
if (options.totalRecords <= 0 || options.recordWidth <= 0) {
|
||||
return { start: 0, end: 0, beforeWidth: 0, afterWidth: 0 };
|
||||
}
|
||||
|
||||
const overscan = options.overscan ?? 2;
|
||||
const recordScrollLeft = Math.max(0, options.scrollLeft - options.pinnedWidth);
|
||||
const recordViewportWidth = Math.max(0, options.viewportWidth - options.pinnedWidth);
|
||||
const start = Math.max(0, Math.floor(recordScrollLeft / options.recordWidth) - overscan);
|
||||
const end = Math.min(
|
||||
options.totalRecords,
|
||||
Math.ceil((recordScrollLeft + recordViewportWidth) / options.recordWidth) + overscan + 1,
|
||||
);
|
||||
|
||||
return {
|
||||
start,
|
||||
end,
|
||||
beforeWidth: start * options.recordWidth,
|
||||
afterWidth: Math.max(0, (options.totalRecords - end) * options.recordWidth),
|
||||
};
|
||||
}
|
||||
|
||||
export function transposeAnchorRowIndex(options: TransposeAnchorOptions): number {
|
||||
const requestedRowId = options.rowIds[options.requestedRowIndex];
|
||||
if (requestedRowId !== undefined && options.selectedRowIds.size > 1 && options.selectedRowIds.has(requestedRowId)) {
|
||||
const firstSelectedIndex = options.rowIds.findIndex((rowId) => options.selectedRowIds.has(rowId));
|
||||
if (firstSelectedIndex >= 0) return firstSelectedIndex;
|
||||
}
|
||||
|
||||
const range = options.selectedRange;
|
||||
if (
|
||||
range &&
|
||||
range.startRow !== range.endRow &&
|
||||
options.requestedRowIndex >= range.startRow &&
|
||||
options.requestedRowIndex <= range.endRow
|
||||
) {
|
||||
return range.startRow;
|
||||
}
|
||||
|
||||
return options.requestedRowIndex;
|
||||
}
|
||||
|
||||
export function transposeFieldWidth(columns: string[], options: TransposeFieldWidthOptions = {}): number {
|
||||
const minWidth = options.minWidth ?? 104;
|
||||
const maxWidth = options.maxWidth ?? 220;
|
||||
const charWidth = options.charWidth ?? 8;
|
||||
const padding = options.padding ?? 32;
|
||||
const longest = columns.reduce((max, column) => Math.max(max, column.length), 0);
|
||||
return Math.min(maxWidth, Math.max(minWidth, Math.ceil(longest * charWidth + padding)));
|
||||
}
|
||||
|
||||
export function transposeScrollLeftForRecord(options: TransposeScrollLeftOptions): number {
|
||||
if (options.recordWidth <= 0 || options.totalRecords <= 0) return 0;
|
||||
const desired = Math.max(0, options.recordIndex) * options.recordWidth;
|
||||
const totalWidth = options.pinnedWidth + options.totalRecords * options.recordWidth;
|
||||
const maxScrollLeft = Math.max(0, totalWidth - options.viewportWidth);
|
||||
return Math.min(desired, maxScrollLeft);
|
||||
}
|
||||
Reference in New Issue
Block a user