feat(data-grid): add whitespace visualization toggle

This commit is contained in:
二丫讲梵 authored and GitHub committed 2026-09-12 17:40:02 +08:00
1 parent 1191118b5c
commit b6cfb3b728
18 files changed
+64 -8

No files matched your search

@@ -1,4 +1,4 @@
import { firstLineCellDisplayValue, type CellValue } from "@/lib/dataGrid/cellValue";
import { gridCellDisplayValue, type CellValue } from "@/lib/dataGrid/cellValue";
import { BOOLEAN_CHECKBOX_SIZE, isBooleanCellValue, normalizeBooleanCellValue } from "@/lib/dataGrid/dataGridBooleanColumn";
import { resolveDataGridCellTextRole } from "@/lib/dataGrid/dataGridCellTextVisual";
import type { DataGridTypeVisualKind } from "@/lib/dataGrid/dataGridColumnType";
@@ -100,6 +100,7 @@ export interface DrawCanvasDataGridOptions {
rightAlignedActionCell?: CanvasRightAlignedActionCell | null;
booleanDisplayMode?: "checkbox" | "dropdown";
flatteningMultiLineEnabled: boolean;
showWhitespace?: boolean;
}
type NumericCanvasContext = CanvasRenderingContext2D & {
@@ -361,6 +362,7 @@ export function drawCanvasDataGrid(options: DrawCanvasDataGridOptions) {
columnIsBoolean,
booleanDisplayMode = "dropdown",
flatteningMultiLineEnabled,
showWhitespace = false,
} = options;
// 框选热路径:整次绘制只判断一次。常见情况(单矩形 / 多列且每段都是多格)可跳过逐格 kind 查询
const paintSelectionOuterFrame = dataGridSelectionUsesOuterFrame(selectionFrames);
@@ -596,7 +598,7 @@ export function drawCanvasDataGrid(options: DrawCanvasDataGridOptions) {
}
} else {
const rawDisplayText = (value === null ? newRowCellPlaceholder?.(item, actualColIdx) : null) ?? formatCell(value, actualColIdx, item);
const displayText = isEditingThisCell ? "" : firstLineCellDisplayValue(rawDisplayText, flatteningMultiLineEnabled);
const displayText = isEditingThisCell ? "" : gridCellDisplayValue(rawDisplayText, flatteningMultiLineEnabled, showWhitespace && value !== null);
const text = isEditingThisCell ? displayText : fitCanvasText(ctx, displayText, cellMaxWidth, isBooleanNullCell ? "left" : isRightAlign ? "right" : "left");
const anchorX = isBooleanNullCell ? alignCanvasPixel(drawX + colWidth / 2, scaleX) : textAnchorX;
ctx.fillText(text, anchorX, textY);
@@ -44,3 +44,10 @@ export function limitDataGridCellDisplay(value: string, maxLength = DATA_GRID_CE
if (value.length <= maxLength) return value;
return `${value.slice(0, maxLength)}...`;
}
/** Visual-only cell text; never use whitespace markers for editing or serialization. */
export function gridCellDisplayValue(value: string, flatteningMultiLine: boolean, showWhitespace: boolean): string {
const text = firstLineCellDisplayValue(value, flatteningMultiLine);
// Hair spaces keep adjacent dots legible in both DOM and Canvas rendering.
return showWhitespace ? text.replace(/[ \t]/g, (character) => (character === " " ? "\u200a·\u200a" : "→")) : text;
}
@@ -66,6 +66,7 @@ export const EDITOR_SETTINGS_DRAFT_KEYS = [
"regexMaxMatchCount",
"autoCalculateTotalRows",
"flatteningMultiLineText",
"dataGridShowWhitespace",
"tableColumnTemplateFields",
"shortcuts",
"sqlFormatter",
@@ -211,6 +211,7 @@ export const SETTINGS_SEARCH_DEFINITIONS: readonly SettingsSearchDefinition[] =
{ id: "data-grid-quick-entry", category: "data", titleKey: "settings.dataGridQuickEntry", descriptionKey: "settings.dataGridQuickEntryDescription", targetId: "data" },
{ id: "data-grid-filter-view", category: "data", titleKey: "settings.dataGridFilterView", descriptionKey: "settings.dataGridFilterViewDescription", targetId: "data-grid-filter-view" },
{ id: "data-grid-flattening-multi-line", category: "data", titleKey: "settings.flatteningMultiLineText", descriptionKey: "settings.flatteningMultiLineTextDescription", targetId: "data" },
{ id: "data-grid-show-whitespace", category: "data", titleKey: "settings.dataGridShowWhitespace", descriptionKey: "settings.dataGridShowWhitespaceDescription", targetId: "data" },
{ id: "sql-file-editor-max-mb", category: "editor", titleKey: "settings.externalSqlEditorMaxMb", descriptionKey: "settings.externalSqlEditorMaxMbDescription", targetId: "editor-sql-file" },
{ id: "sql-file-web-upload-max-mb", category: "data", titleKey: "settings.webSqlFileUploadMaxMb", descriptionKey: "settings.webSqlFileUploadMaxMbDescription", targetId: "data-sql-file-upload", visible: (context) => context.isWeb },
{ id: "appearance-toolbar", category: "appearance", titleKey: "settings.toolbarTitle", descriptionKey: "settings.toolbarHiddenHint", targetId: "appearance" },
@@ -149,6 +149,7 @@ const SETTINGS_TRANSFER_CATEGORY_KEYS: Record<SettingsTransferCategoryId, readon
"dataGridCellDetailButtonVisible",
"dataGridCrosshairHighlight",
"flatteningMultiLineText",
"dataGridShowWhitespace",
"pageSize",
"tableOpenPageSize",
"queryResultMaxRowsEnabled",
@@ -259,6 +260,7 @@ const PASS_THROUGH_BOOLEAN_KEYS = [
"compactColumnHeaderActions",
"dataGridQuickEntry",
"flatteningMultiLineText",
"dataGridShowWhitespace",
"infiniteScroll",
"autoCalculateTotalRows",
"autoSelectActiveSidebarNode",