feat(data-grid): add whitespace visualization toggle
This commit is contained in:
18 files changed
+64
-8
No files matched your search
@@ -549,6 +549,7 @@ const editInfiniteScroll = ref(settingsStore.editorSettings.infiniteScroll);
|
||||
const editRegexMaxMatchCount = ref(settingsStore.editorSettings.regexMaxMatchCount);
|
||||
const editAutoCalculateTotalRows = ref(settingsStore.editorSettings.autoCalculateTotalRows);
|
||||
const editFlatteningMultiLineText = ref(settingsStore.editorSettings.flatteningMultiLineText);
|
||||
const editDataGridShowWhitespace = ref(settingsStore.editorSettings.dataGridShowWhitespace);
|
||||
const editTableColumnTemplateRows = ref<TableColumnTemplateGridRow[]>(tableColumnTemplateRowsFromSettings(settingsStore.editorSettings.tableColumnTemplateFields));
|
||||
const editTableColumnTemplateDatabaseType = ref<DatabaseType>(TABLE_COLUMN_TEMPLATE_DATABASE_TYPES[0] ?? "mysql");
|
||||
const editSqlVariableSubstitutionEnabled = ref(settingsStore.editorSettings.sqlVariableSubstitutionEnabled);
|
||||
@@ -767,6 +768,7 @@ function currentEditorSettingsDraft(): EditorSettingsDraft {
|
||||
dataGridCellDetailButtonVisible: editDataGridCellDetailButtonVisible.value,
|
||||
dataGridCrosshairHighlight: editDataGridCrosshairHighlight.value,
|
||||
flatteningMultiLineText: editFlatteningMultiLineText.value,
|
||||
dataGridShowWhitespace: editDataGridShowWhitespace.value,
|
||||
pageSize: editPageSize.value,
|
||||
tableOpenPageSize: editTableOpenPageSize.value,
|
||||
queryResultMaxRowsEnabled: editQueryResultMaxRowsEnabled.value,
|
||||
@@ -1265,6 +1267,7 @@ function syncEditorSettingsDraftFromStore() {
|
||||
editDataGridCellDetailButtonVisible.value = settingsStore.editorSettings.dataGridCellDetailButtonVisible;
|
||||
editDataGridCrosshairHighlight.value = settingsStore.editorSettings.dataGridCrosshairHighlight;
|
||||
editFlatteningMultiLineText.value = settingsStore.editorSettings.flatteningMultiLineText;
|
||||
editDataGridShowWhitespace.value = settingsStore.editorSettings.dataGridShowWhitespace;
|
||||
editPageSize.value = settingsStore.editorSettings.pageSize;
|
||||
editTableOpenPageSize.value = settingsStore.editorSettings.tableOpenPageSize;
|
||||
editQueryResultMaxRowsEnabled.value = settingsStore.editorSettings.queryResultMaxRowsEnabled;
|
||||
@@ -1390,6 +1393,7 @@ const editorSettingsDraftRefs: EditorSettingsDraftRefMap = {
|
||||
regexMaxMatchCount: editRegexMaxMatchCount,
|
||||
autoCalculateTotalRows: editAutoCalculateTotalRows,
|
||||
flatteningMultiLineText: editFlatteningMultiLineText,
|
||||
dataGridShowWhitespace: editDataGridShowWhitespace,
|
||||
shortcuts: editShortcuts,
|
||||
sqlFormatter: editSqlFormatter,
|
||||
sidebarActivation: editSidebarActivation,
|
||||
@@ -1752,6 +1756,7 @@ function resetDefaultsForTab(tab: SettingsCategory) {
|
||||
editDataGridCellDetailButtonVisible.value = DEFAULT_EDITOR_SETTINGS.dataGridCellDetailButtonVisible;
|
||||
editDataGridCrosshairHighlight.value = DEFAULT_EDITOR_SETTINGS.dataGridCrosshairHighlight;
|
||||
editFlatteningMultiLineText.value = DEFAULT_EDITOR_SETTINGS.flatteningMultiLineText;
|
||||
editDataGridShowWhitespace.value = DEFAULT_EDITOR_SETTINGS.dataGridShowWhitespace;
|
||||
editPageSize.value = DEFAULT_EDITOR_SETTINGS.pageSize;
|
||||
editTableOpenPageSize.value = DEFAULT_EDITOR_SETTINGS.tableOpenPageSize;
|
||||
editQueryResultMaxRowsEnabled.value = DEFAULT_EDITOR_SETTINGS.queryResultMaxRowsEnabled;
|
||||
@@ -1840,6 +1845,7 @@ function resetAllDefaults() {
|
||||
editDataGridCellDetailButtonVisible.value = DEFAULT_EDITOR_SETTINGS.dataGridCellDetailButtonVisible;
|
||||
editDataGridCrosshairHighlight.value = DEFAULT_EDITOR_SETTINGS.dataGridCrosshairHighlight;
|
||||
editFlatteningMultiLineText.value = DEFAULT_EDITOR_SETTINGS.flatteningMultiLineText;
|
||||
editDataGridShowWhitespace.value = DEFAULT_EDITOR_SETTINGS.dataGridShowWhitespace;
|
||||
editPageSize.value = DEFAULT_EDITOR_SETTINGS.pageSize;
|
||||
editTableOpenPageSize.value = DEFAULT_EDITOR_SETTINGS.tableOpenPageSize;
|
||||
editQueryResultMaxRowsEnabled.value = DEFAULT_EDITOR_SETTINGS.queryResultMaxRowsEnabled;
|
||||
@@ -7076,6 +7082,13 @@ onUnmounted(() => {
|
||||
</div>
|
||||
<Switch id="data-grid-crosshair-highlight" v-model="editDataGridCrosshairHighlight" />
|
||||
</div>
|
||||
<div class="settings-item flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
|
||||
<div class="space-y-1">
|
||||
<Label for="data-grid-show-whitespace">{{ t("settings.dataGridShowWhitespace") }}</Label>
|
||||
<p class="text-xs text-muted-foreground">{{ t("settings.dataGridShowWhitespaceDescription") }}</p>
|
||||
</div>
|
||||
<Switch id="data-grid-show-whitespace" v-model="editDataGridShowWhitespace" />
|
||||
</div>
|
||||
<div class="settings-item flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
|
||||
<div class="space-y-1">
|
||||
<Label for="flattening-multi-line">
|
||||
|
||||
@@ -138,7 +138,7 @@ import {
|
||||
type TransposeScrollAlignment,
|
||||
} from "@/lib/dataGrid/dataGridTranspose";
|
||||
import { canApplyGridSelectionValue, canDeleteGridRowItem, canEditGridCellDetail, matchesRowStatusFilter, shouldShowQuickEntryDraftRow, type RowStatus, type RowStatusFilter } from "@/lib/dataGrid/gridRowStatus";
|
||||
import { displayCellValue, firstLineCellDisplayValue, limitDataGridCellDisplay, SQLSERVER_DATA_GRID_CELL_DISPLAY_MAX_LENGTH, type CellValue } from "@/lib/dataGrid/cellValue";
|
||||
import { displayCellValue, firstLineCellDisplayValue, gridCellDisplayValue, limitDataGridCellDisplay, SQLSERVER_DATA_GRID_CELL_DISPLAY_MAX_LENGTH, type CellValue } from "@/lib/dataGrid/cellValue";
|
||||
import { cellExternalUrl } from "@/lib/dataGrid/cellExternalUrl";
|
||||
import { getApplicablePreviewActions, type PreviewAction } from "@/lib/dataGrid/resultPreviewRegistry";
|
||||
import "@/lib/dataGrid/geometryMapPreview";
|
||||
@@ -751,6 +751,7 @@ const infiniteScrollEnabled = computed(() => props.paginationEnabled && settings
|
||||
const queryResultMaxRows = computed(() => effectiveQueryResultMaxRows(settingsStore.editorSettings.queryResultMaxRowsEnabled, settingsStore.editorSettings.queryResultMaxRows));
|
||||
const paginationMaxRows = computed(() => (isResultsContext.value ? queryResultMaxRows.value : undefined));
|
||||
const infiniteScrollMaxRows = computed(() => continuousQueryResultMaxRows(settingsStore.editorSettings.queryResultMaxRowsEnabled, settingsStore.editorSettings.queryResultMaxRows));
|
||||
const showWhitespaceEnabled = computed(() => settingsStore.editorSettings.dataGridShowWhitespace);
|
||||
const flatteningMultiLineEnabled = computed(() => settingsStore.editorSettings.flatteningMultiLineText);
|
||||
const expandedCellEditor = ref<{ rowId: number; col: number } | null>(null);
|
||||
const readonlyTextCell = ref<{
|
||||
@@ -2225,7 +2226,8 @@ let columnFormatterForWidth: ((columnIndex: number) => ColumnFormatterConfig | u
|
||||
|
||||
function columnWidthDisplayValue(value: CellValue, columnIndex: number): CellValue {
|
||||
const formatter = columnFormatterForWidth?.(columnIndex);
|
||||
return formatter ? applyColumnFormatter(value, formatter) : value;
|
||||
const display = formatter ? applyColumnFormatter(value, formatter) : value;
|
||||
return showWhitespaceEnabled.value && typeof display === "string" ? gridCellDisplayValue(display, flatteningMultiLineEnabled.value, true) : display;
|
||||
}
|
||||
|
||||
const { initColumnWidths, onResizeStart, autoFitColumn, renderedColumnWidths, totalWidth, columnVars, getIsResizing } = useDataGridColumnResize({
|
||||
@@ -6848,9 +6850,11 @@ async function onCanvasDblClick(event: MouseEvent) {
|
||||
function canvasCellContentOverflows(item: RowItem, actualColIdx: number, visibleColIdx: number): boolean {
|
||||
const cellWidth = renderedColumnWidths.value[visibleColIdx] ?? 0;
|
||||
if (cellWidth <= 0) return false;
|
||||
const displayText = formatCellCached(item.data[actualColIdx], actualColIdx);
|
||||
const formattedText = formatCellCached(item.data[actualColIdx], actualColIdx);
|
||||
const editText = cellEditorTextForValue(item.data[actualColIdx], actualColIdx);
|
||||
if (editText.includes("\n") || editText.includes("\r") || editText.length > displayText.length) return true;
|
||||
// Detect truncated previews before visual whitespace markers add characters.
|
||||
if (editText.includes("\n") || editText.includes("\r") || editText.length > formattedText.length) return true;
|
||||
const displayText = showWhitespaceEnabled.value ? gridCellDisplayValue(formattedText, flatteningMultiLineEnabled.value, true) : formattedText;
|
||||
const canvas = activeCanvasSurface();
|
||||
const context = canvas?.getContext("2d");
|
||||
if (!context) {
|
||||
@@ -7086,6 +7090,7 @@ function drawCanvasGrid() {
|
||||
columnIsBoolean: isBooleanGridColumn,
|
||||
booleanDisplayMode: booleanDisplayMode.value,
|
||||
flatteningMultiLineEnabled: flatteningMultiLineEnabled.value,
|
||||
showWhitespace: showWhitespaceEnabled.value,
|
||||
});
|
||||
flipCanvasSurface();
|
||||
}
|
||||
@@ -7110,6 +7115,7 @@ watch(showDataGridTopbar, () => nextTick(observeDataGridTopbarWidth), {
|
||||
watch(columnAligns, () => scheduleCanvasDraw());
|
||||
watch(booleanDisplayMode, () => scheduleCanvasDraw());
|
||||
watch(flatteningMultiLineEnabled, () => scheduleCanvasDraw());
|
||||
watch(showWhitespaceEnabled, () => scheduleCanvasDraw());
|
||||
watch(colorizeDataGridCellTypes, () => scheduleCanvasDraw());
|
||||
watch(
|
||||
[
|
||||
@@ -11993,7 +11999,7 @@ useUpdateBlocker(() => (hasPendingChanges.value || hasPendingDataEditorDraft.val
|
||||
<template v-if="newRowCellPlaceholder(displayItems[cell.recordIndex], cell.valueIndex)">
|
||||
<span class="text-muted-foreground/70 italic">{{ firstLineCellDisplayValue(newRowCellPlaceholder(displayItems[cell.recordIndex], cell.valueIndex) ?? "", flatteningMultiLineEnabled) }}</span>
|
||||
</template>
|
||||
<template v-else>{{ firstLineCellDisplayValue(cell.display, flatteningMultiLineEnabled) }}</template>
|
||||
<template v-else>{{ gridCellDisplayValue(cell.display, flatteningMultiLineEnabled, showWhitespaceEnabled) }}</template>
|
||||
<div v-if="cellDetailButtonVisible(cell.recordIndex, cell.valueIndex)" class="absolute right-2 top-1/2 flex -translate-y-1/2 items-center gap-1">
|
||||
<LightDropdownMenu
|
||||
v-if="canQuickDownloadCellValue(cell.recordIndex, cell.valueIndex)"
|
||||
@@ -12913,7 +12919,7 @@ useUpdateBlocker(() => (hasPendingChanges.value || hasPendingDataEditorDraft.val
|
||||
<template v-if="newRowCellPlaceholder(item, col.actualColIdx)">
|
||||
<span class="text-muted-foreground/70 italic">{{ firstLineCellDisplayValue(newRowCellPlaceholder(item, col.actualColIdx) ?? "", flatteningMultiLineEnabled) }}</span>
|
||||
</template>
|
||||
<template v-else>{{ firstLineCellDisplayValue(formatGridItemCell(item, col.actualColIdx), flatteningMultiLineEnabled) }}</template>
|
||||
<template v-else>{{ gridCellDisplayValue(formatGridItemCell(item, col.actualColIdx), flatteningMultiLineEnabled, showWhitespaceEnabled) }}</template>
|
||||
<div v-if="cellDetailButtonVisible(item.displayIndex, col.actualColIdx)" class="absolute right-2 top-1/2 flex -translate-y-1/2 items-center gap-1">
|
||||
<LightDropdownMenu
|
||||
v-if="canQuickDownloadCellValue(item.displayIndex, col.actualColIdx)"
|
||||
|
||||
Reference in new issue
Block a user