feat(grid): add data grid quick entry

This commit is contained in:
funnytime authored and GitHub committed 2026-06-30 09:14:44 +08:00
1 parent 074cef8358
commit 3e7cf30ce5
23 files changed
+1679 -186

No files matched your search

+14 -7
View File
@@ -9,6 +9,7 @@ export interface CanvasDataGridRow {
displayIndex: number;
data: CellValue[];
isNew: boolean;
isDraft?: boolean;
isDeleted: boolean;
isDirtyCol: boolean[];
status: RowStatus;
@@ -50,6 +51,7 @@ export interface DrawCanvasDataGridOptions {
searchMatchKeys: ReadonlySet<string>;
currentSearchMatch: CanvasSearchMatch | null;
formatCell: (value: CellValue, columnIndex: number) => string;
draftCellPlaceholder?: string;
isRowActive: (rowIndex: number) => boolean;
rowCellsUseSelectionVisual: (rowId: number) => boolean;
cellIsSelected: (rowIndex: number, visibleColIdx: number) => boolean;
@@ -222,6 +224,7 @@ export function drawCanvasDataGrid(options: DrawCanvasDataGridOptions) {
searchMatchKeys,
currentSearchMatch,
formatCell,
draftCellPlaceholder,
isRowActive,
rowCellsUseSelectionVisual,
cellIsSelected,
@@ -275,13 +278,13 @@ export function drawCanvasDataGrid(options: DrawCanvasDataGridOptions) {
const y = rowIndex * CANVAS_DATA_GRID_ROW_HEIGHT - scrollTop;
const rowIsActive = isRowActive(item.displayIndex);
const rowBase = item.isDeleted ? theme.rowDeleted : item.isNew && !rowIsActive ? theme.rowNew : item.displayIndex % 2 === 1 && !rowIsActive ? theme.rowMuted : theme.background;
const rowBase = item.isDeleted ? theme.rowDeleted : item.isNew && !rowIsActive ? theme.rowNew : item.isDraft && !rowIsActive ? theme.rowMuted : item.displayIndex % 2 === 1 && !rowIsActive ? theme.rowMuted : theme.background;
const rowBorderY = crispCanvasLine(y + CANVAS_DATA_GRID_ROW_HEIGHT - 1, dpr);
ctx.globalAlpha = item.isDeleted ? 0.7 : 1;
ctx.fillStyle = rowIsActive && !item.isDeleted ? theme.cellSelectedSingle : rowBase;
ctx.fillRect(0, y, width, CANVAS_DATA_GRID_ROW_HEIGHT);
const rowNumberFill = item.status === "new" ? theme.rowNumberNew : item.status === "edited" ? theme.rowNumberEdited : item.status === "deleted" ? theme.rowNumberDeleted : theme.rowNumberDefault;
const rowNumberFill = item.status === "draft" ? theme.rowNumberDefault : item.status === "new" ? theme.rowNumberNew : item.status === "edited" ? theme.rowNumberEdited : item.status === "deleted" ? theme.rowNumberDeleted : theme.rowNumberDefault;
ctx.fillStyle = rowNumberFill;
ctx.fillRect(0, y, rowNumberWidth, CANVAS_DATA_GRID_ROW_HEIGHT);
ctx.strokeStyle = theme.border;
@@ -292,10 +295,12 @@ export function drawCanvasDataGrid(options: DrawCanvasDataGridOptions) {
const rowNumberText = item.status === "new" ? theme.rowNumberTextNew : item.status === "edited" ? theme.rowNumberTextEdited : item.status === "deleted" ? theme.rowNumberTextDeleted : theme.rowNumberTextClean;
ctx.fillStyle = rowNumberText;
ctx.font = item.status === "new" || item.status === "edited" ? semiboldFont : normalFont;
ctx.font = item.status === "new" || item.status === "edited" || item.status === "draft" ? semiboldFont : normalFont;
ctx.textAlign = "center";
const textY = alignCanvasPixel(y + rowTextOffsetY, dpr);
if (infiniteScrollEnabled) {
if (item.isDraft) {
ctx.fillText("*", rowNumberTextX, textY);
} else if (infiniteScrollEnabled) {
ctx.fillText(String(item.displayIndex + 1), rowNumberTextX, textY);
} else {
ctx.fillText(String(item.displayIndex + 1 + pageSize * (currentPage - 1)), rowNumberTextX, textY);
@@ -367,10 +372,12 @@ export function drawCanvasDataGrid(options: DrawCanvasDataGridOptions) {
ctx.font = value === null ? italicFont : tabularFont;
setCanvasNumericVariant(ctx, value === null ? "normal" : "tabular-nums");
const textLeft = alignCanvasPixel(x + 12, dpr);
const textMaxWidth = Math.max(0, x + colWidth - textLeft - 12);
const paddedMaxWidth = Math.max(0, x + colWidth - textLeft - 12);
const isEditingThisCell = editingCell?.rowId === item.id && editingCell.col === actualColIdx;
const displayText = isEditingThisCell ? "" : formatCell(value, actualColIdx);
const text = isEditingThisCell ? displayText : fitCanvasText(ctx, displayText, textMaxWidth);
const displayText = isEditingThisCell ? "" : item.isDraft && value === null ? (draftCellPlaceholder ?? "") : formatCell(value, actualColIdx);
const needsTruncation = ctx.measureText(displayText).width > paddedMaxWidth;
const textMaxWidth = needsTruncation ? Math.max(0, x + colWidth - textLeft) : paddedMaxWidth;
const text = isEditingThisCell ? displayText : fitCanvasText(ctx, displayText, textMaxWidth - 12);
ctx.fillText(text, textLeft, textY);
if (item.isDeleted && text) {
const textWidth = Math.min(ctx.measureText(text).width, textMaxWidth);
+18 -1
View File
@@ -1,7 +1,8 @@
export type RowStatus = "clean" | "edited" | "new" | "deleted";
export type RowStatus = "clean" | "edited" | "new" | "deleted" | "draft";
export type RowStatusFilter = "all" | "changed" | "edited" | "new" | "deleted";
export function matchesRowStatusFilter(status: RowStatus, filter: RowStatusFilter): boolean {
if (status === "draft") return filter === "all";
if (filter === "all") return true;
if (filter === "changed") return status !== "clean";
return status === filter;
@@ -10,3 +11,19 @@ export function matchesRowStatusFilter(status: RowStatus, filter: RowStatusFilte
export function rowStatusFilterAfterAddingRow(filter: RowStatusFilter): RowStatusFilter {
return matchesRowStatusFilter("new", filter) ? filter : "all";
}
export function shouldShowQuickEntryDraftRow(options: { editable: boolean; hasInsertTarget: boolean; quickEntryEnabled: boolean; rowStatusFilter: RowStatusFilter; hasPendingChanges: boolean }): boolean {
return options.editable && options.hasInsertTarget && options.quickEntryEnabled && !options.hasPendingChanges && matchesRowStatusFilter("draft", options.rowStatusFilter);
}
export function canDeleteGridRowItem(options: { editable: boolean; isDraft: boolean; isDeleted: boolean; isNew: boolean; canEditExistingRows: boolean; isSavingNewRow: boolean }): boolean {
return options.editable && !options.isDraft && !options.isDeleted && !options.isSavingNewRow && (options.isNew || options.canEditExistingRows);
}
export function canEditGridCellDetail(options: { canEditCell: boolean; isDraft: boolean }): boolean {
return options.canEditCell && !options.isDraft;
}
export function canApplyGridSelectionValue(options: { isDraft: boolean; allowDraft?: boolean }): boolean {
return !options.isDraft || options.allowDraft === true;
}
+4
View File
@@ -34,6 +34,10 @@ export function isTableDataEditable(databaseType: DatabaseType | undefined, prim
return primaryKeys.length > 0;
}
export function canInsertTableRows(databaseType: DatabaseType | undefined): boolean {
return getDatabaseCapability(databaseType).tableData.insert;
}
export function supportsDataGridTransaction(databaseType: DatabaseType | undefined): boolean {
return getDatabaseCapability(databaseType).tableData.transaction;
}