feat(grid): use temporal inputs for date cells

This commit is contained in:
t8y2 committed 2026-05-19 16:39:19 +08:00
1 parent b76efa56d6
commit b6e519dfaa
3 files changed
+140 -1

No files matched your search

+47 -1
View File
@@ -104,6 +104,12 @@ import {
type ColumnFormatterConfig,
type DateTimeFormatterUnit,
} from "@/lib/columnFormatter";
import {
formatTemporalInputValue,
parseTemporalInputValue,
temporalCellEditorKind,
type TemporalCellEditorKind,
} from "@/lib/dataGridTemporalEditor";
import { isCancelSearchShortcut, isFocusSearchShortcut } from "@/lib/keyboardShortcuts";
import { dataGridHeaderContentWidth, scrollbarGutterWidth } from "@/lib/dataGridScrollGutter";
import { dataGridSaveActionMode, dataGridSaveToolbarState } from "@/lib/dataGridSaveUi";
@@ -1386,6 +1392,43 @@ function canEditCellItem(item: RowItem | undefined, columnIndex: number): boolea
return true;
}
function tableColumnForGridColumn(columnIndex: number): ColumnInfo | undefined {
const columnName = props.sourceColumns?.[columnIndex] ?? props.result.columns[columnIndex];
if (!columnName) return undefined;
return props.tableMeta?.columns.find((column) => column.name.toLowerCase() === columnName.toLowerCase());
}
function temporalEditorKindForColumn(columnIndex: number): TemporalCellEditorKind | undefined {
return temporalCellEditorKind(tableColumnForGridColumn(columnIndex)?.data_type, props.databaseType);
}
function cellEditInputType(columnIndex: number): string {
const kind = temporalEditorKindForColumn(columnIndex);
if (kind === "date") return "date";
if (kind === "time") return "time";
if (kind === "datetime") return "datetime-local";
return "text";
}
function cellEditInputStep(columnIndex: number): string | undefined {
return temporalEditorKindForColumn(columnIndex) ? "1" : undefined;
}
function cellEditInputValue(columnIndex: number): string {
const kind = temporalEditorKindForColumn(columnIndex);
return kind ? formatTemporalInputValue(editValue.value, kind) : editValue.value;
}
function onCellEditInput(columnIndex: number, event: Event) {
const target = event.target as HTMLInputElement;
const kind = temporalEditorKindForColumn(columnIndex);
if (!kind) {
editValue.value = target.value;
return;
}
editValue.value = parseTemporalInputValue(target.value, kind) ?? "NULL";
}
function canDeleteRowItem(item: RowItem | undefined): boolean {
return !!props.editable && !!item && !item.isDeleted && (item.isNew || canEditExistingRows.value);
}
@@ -3500,11 +3543,14 @@ defineExpose({
>
<template v-if="editingCell?.rowId === item.id && editingCell?.col === actualColIdx">
<input
v-model="editValue"
:type="cellEditInputType(actualColIdx)"
:step="cellEditInputStep(actualColIdx)"
:value="cellEditInputValue(actualColIdx)"
autocapitalize="off"
autocorrect="off"
spellcheck="false"
class="cell-edit-input absolute inset-0 bg-background border-2 border-primary px-2 py-0.5 text-xs outline-none z-10"
@input="onCellEditInput(actualColIdx, $event)"
@blur="commitEdit"
@click.stop
@keydown.stop="onEditKeydown"
@@ -0,0 +1,60 @@
import type { DatabaseType } from "@/types/database";
export type TemporalCellEditorKind = "date" | "time" | "datetime";
export function temporalCellEditorKind(
dataType: string | undefined,
_databaseType?: DatabaseType,
): TemporalCellEditorKind | undefined {
const normalized = normalizeTemporalType(dataType);
if (!normalized) return undefined;
if (/\b(?:datetime|datetime2|datetime64|smalldatetime|datetimeoffset|timestamp|timestamptz)\b/.test(normalized)) {
return "datetime";
}
if (/\bdate(?:32)?\b/.test(normalized)) return "date";
if (/\b(?:time|timetz|time64)\b/.test(normalized)) return "time";
return undefined;
}
export function formatTemporalInputValue(
value: string | number | boolean | null,
kind: TemporalCellEditorKind,
): string {
if (value === null || value === undefined) return "";
const text = String(value).trim();
if (!text) return "";
if (kind === "date") {
const date = text.match(/^(\d{4}-\d{2}-\d{2})/);
return date?.[1] ?? "";
}
if (kind === "time") {
return normalizeTimeInput(text);
}
const datetime = text.match(/^(\d{4}-\d{2}-\d{2})[ T](\d{2}:\d{2})(?::(\d{2}))?/);
if (!datetime) return "";
return `${datetime[1]}T${datetime[2]}:${datetime[3] ?? "00"}`;
}
export function parseTemporalInputValue(value: string, kind: TemporalCellEditorKind): string | null {
const text = value.trim();
if (!text) return null;
if (kind === "date") return text;
if (kind === "time") return normalizeTimeInput(text) || null;
const [date, time = ""] = text.split("T");
const normalizedTime = normalizeTimeInput(time);
return date && normalizedTime ? `${date} ${normalizedTime}` : text.replace("T", " ");
}
function normalizeTemporalType(dataType: string | undefined): string {
return (dataType ?? "").toLowerCase().replace(/_/g, "").replace(/[(),]/g, " ").replace(/\s+/g, " ").trim();
}
function normalizeTimeInput(text: string): string {
const match = text.match(/^(\d{2}:\d{2})(?::(\d{2}))?/);
if (!match) return "";
return `${match[1]}:${match[2] ?? "00"}`;
}