mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-20 00:17:44 +00:00
feat(data-grid): add whitespace visualization toggle
This commit is contained in:
@@ -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)"
|
||||
|
||||
@@ -6941,6 +6941,8 @@ export default withEnglishFallback({
|
||||
autoCalculateTotalRows: "Ümumi sətir sayını avtomatik hesabla",
|
||||
autoCalculateTotalRowsDescription: "Uyğun gələn sətirlərin ümumi sayını göstərmək üçün hər sorğudan sonra COUNT(*) avtomatik işlədilir. Böyük sorğuların sürətini qorumaq üçün standart olaraq söndürülüb — ehtiyac olduqda nəticənin alt hissəsindən hesablayın.",
|
||||
infiniteScrollDescription: "Aşağıya sürüşdürdükdə növbəti toplunu avtomatik yüklə.",
|
||||
dataGridShowWhitespace: "Boşluq simvollarını göstər",
|
||||
dataGridShowWhitespaceDescription: "Verilənlər xanalarında boşluqları ·, tabulyasiyaları → kimi göstərir. Kopyalanan və redaktə edilən dəyərlər dəyişmir.",
|
||||
flatteningMultiLineText: "Çoxsətirli mətnin bir sətrə birləşdirilməsi",
|
||||
flatteningMultiLineTextDescription: "Çoxsətirli mətn bir sətirdə göstərilir, sətir keçidləri ¶ kimi göstərilir.",
|
||||
regexMaxMatchCount: "Hamısını seçmək üçün maksimum uyğunluq sayı",
|
||||
|
||||
@@ -6976,6 +6976,8 @@ export default {
|
||||
autoCalculateTotalRows: "Auto-calculate total row count",
|
||||
autoCalculateTotalRowsDescription: "Run COUNT(*) automatically after each query to show the total matching rows. Off by default to keep large queries fast — calculate it on demand from the result footer.",
|
||||
infiniteScrollDescription: "Automatically load the next batch when scrolling to the bottom.",
|
||||
dataGridShowWhitespace: "Show whitespace",
|
||||
dataGridShowWhitespaceDescription: "Show spaces as · and tabs as → in data cells. Copied and edited values remain unchanged.",
|
||||
flatteningMultiLineText: "Flattening Multi-line Text",
|
||||
flatteningMultiLineTextDescription: "Multi-line text merged into single line display, with line breaks shown as ¶ .",
|
||||
regexMaxMatchCount: "Maximum select-all matches",
|
||||
|
||||
@@ -6548,6 +6548,8 @@ export default withEnglishFallback({
|
||||
autoCalculateTotalRows: "Calcular automáticamente el total de filas",
|
||||
autoCalculateTotalRowsDescription: "Ejecuta COUNT(*) automáticamente tras cada consulta para mostrar el total de filas coincidentes. Desactivado por defecto para mantener rápidas las consultas grandes; puedes calcularlo cuando quieras desde el pie de resultados.",
|
||||
infiniteScrollDescription: "Carga automáticamente el siguiente lote al desplazarse hasta el final.",
|
||||
dataGridShowWhitespace: "Mostrar espacios en blanco",
|
||||
dataGridShowWhitespaceDescription: "Mostrar espacios como · y tabulaciones como → en las celdas. Los valores copiados y editados no cambian.",
|
||||
flatteningMultiLineText: "Aplastar texto multilínea",
|
||||
flatteningMultiLineTextDescription: "El texto multilínea se fusiona en una sola línea de visualización, con los saltos de línea mostrados como ¶ .",
|
||||
regexMaxMatchCount: "Máximo de coincidencias al seleccionar todas",
|
||||
|
||||
@@ -6548,6 +6548,8 @@ export default withEnglishFallback({
|
||||
autoCalculateTotalRows: "Calcola automaticamente il totale delle righe",
|
||||
autoCalculateTotalRowsDescription: "Esegue COUNT(*) automaticamente dopo ogni query per mostrare il totale delle righe corrispondenti. Disattivato per impostazione predefinita per mantenere veloci le query grandi; puoi calcolarlo all'occorrenza dal piè di pagina dei risultati.",
|
||||
infiniteScrollDescription: "Carica automaticamente il blocco successivo quando scorri fino in fondo.",
|
||||
dataGridShowWhitespace: "Mostra spazi bianchi",
|
||||
dataGridShowWhitespaceDescription: "Mostra gli spazi come · e le tabulazioni come → nelle celle. I valori copiati e modificati restano invariati.",
|
||||
flatteningMultiLineText: "Pianifica automaticamente i testi multi-riga",
|
||||
flatteningMultiLineTextDescription: "Se attivo, i testi multi-riga nei risultati vengono automaticamente pianificati in una singola colonna.",
|
||||
regexMaxMatchCount: "Numero massimo di corrispondenze per Seleziona tutte",
|
||||
|
||||
@@ -7232,6 +7232,8 @@ export default withEnglishFallback({
|
||||
autoCalculateTotalRows: "総行数を自動計算",
|
||||
autoCalculateTotalRowsDescription: "クエリごとに COUNT(*) を自動実行し、一致する総行数を表示します。大きなクエリを高速に保つため既定はオフです。結果フッターから必要に応じて計算できます。",
|
||||
infiniteScrollDescription: "末尾までスクロールしたときに、次のデータを自動的に読み込みます。",
|
||||
dataGridShowWhitespace: "空白文字を表示",
|
||||
dataGridShowWhitespaceDescription: "データセルのスペースを ·、タブを → で表示します。コピーや編集する値は変わりません。",
|
||||
flatteningMultiLineText: "多行テキストを展開",
|
||||
flatteningMultiLineTextDescription: "複数行のテキストを1行に結合して表示し、改行文字は¶として表示する。",
|
||||
regexMaxMatchCount: "すべての一致を選択する最大数",
|
||||
|
||||
@@ -6289,6 +6289,8 @@ export default withEnglishFallback({
|
||||
autoCalculateTotalRows: "전체 행 수 자동 집계",
|
||||
autoCalculateTotalRowsDescription: "각 쿼리 후 자동으로 COUNT(*)를 실행하여 일치하는 전체 행 수를 표시합니다. 대규모 쿼리를 빠르게 유지하기 위해 기본적으로 꺼져 있으며, 결과 푸터에서 요청 시 집계할 수 있습니다.",
|
||||
infiniteScrollDescription: "목록 하단으로 스크롤할 때 다음 데이터 묶음을 자동으로 불러옵니다.",
|
||||
dataGridShowWhitespace: "공백 문자 표시",
|
||||
dataGridShowWhitespaceDescription: "데이터 셀의 공백을 ·, 탭을 →로 표시합니다. 복사하거나 편집하는 값은 변경되지 않습니다.",
|
||||
flatteningMultiLineText: "멀티라인 텍스트 단순화",
|
||||
flatteningMultiLineTextDescription: "다중 행 텍스트를 단일 행으로 병합하여 표시하고, 개행 문자는 ¶로 표시합니다.",
|
||||
tableColumnTemplateFields: "새 테이블 미리 정의된 필드",
|
||||
|
||||
@@ -6550,6 +6550,8 @@ export default withEnglishFallback({
|
||||
autoCalculateTotalRows: "Calcular automaticamente o total de linhas",
|
||||
autoCalculateTotalRowsDescription: "Executa COUNT(*) automaticamente após cada consulta para mostrar o total de linhas correspondentes. Desativado por padrão para manter consultas grandes rápidas; você pode calculá-lo quando quiser no rodapé dos resultados.",
|
||||
infiniteScrollDescription: "Carregar automaticamente o próximo lote ao rolar até o final.",
|
||||
dataGridShowWhitespace: "Mostrar espaços em branco",
|
||||
dataGridShowWhitespaceDescription: "Mostrar espaços como · e tabulações como → nas células. Os valores copiados e editados permanecem inalterados.",
|
||||
flatteningMultiLineText: "Achatar texto multilinha",
|
||||
flatteningMultiLineTextDescription: "Exibir texto multilinha em uma única linha.",
|
||||
regexMaxMatchCount: "Máximo de correspondências para selecionar todas",
|
||||
|
||||
@@ -6823,6 +6823,8 @@ export default withEnglishFallback({
|
||||
autoCalculateTotalRows: "Toplam satır sayısını otomatik hesapla",
|
||||
autoCalculateTotalRowsDescription: "Eşleşen toplam satırı göstermek için her sorgunun ardından otomatik olarak COUNT(*) çalıştır. Büyük sorguların hızlı kalması için varsayılan olarak kapalıdır — sonuç alt bilgisinden istediğinizde hesaplayın.",
|
||||
infiniteScrollDescription: "En alta kaydırıldığında sonraki grubu otomatik yükle.",
|
||||
dataGridShowWhitespace: "Boşluk karakterlerini göster",
|
||||
dataGridShowWhitespaceDescription: "Veri hücrelerinde boşlukları ·, sekmeleri → olarak gösterir. Kopyalanan ve düzenlenen değerler değişmez.",
|
||||
flatteningMultiLineText: "Çok Satırlı Metni Düzleştirme",
|
||||
flatteningMultiLineTextDescription: "Çok satırlı metin, satır sonları ¶ ile gösterilerek tek satırda birleştirilir.",
|
||||
regexMaxMatchCount: 'En fazla "tümünü seç" eşleşmesi',
|
||||
|
||||
@@ -6959,6 +6959,8 @@ export default withEnglishFallback({
|
||||
autoCalculateTotalRows: "自动统计总行数",
|
||||
autoCalculateTotalRowsDescription: "每次查询后自动执行 COUNT(*) 显示匹配的总行数。默认关闭以保证大查询速度 —— 可在结果栏按需手动统计。",
|
||||
infiniteScrollDescription: "滚动到底部时自动加载下一批数据,无需手动翻页或加载。",
|
||||
dataGridShowWhitespace: "显示空白字符",
|
||||
dataGridShowWhitespaceDescription: "在数据单元格中将空格显示为 ·,制表符显示为 →。复制和编辑的值保持不变。",
|
||||
flatteningMultiLineText: "扁平化多行文本",
|
||||
flatteningMultiLineTextDescription: "多行文本合并为单行显示,换行符显示为 ¶ 。",
|
||||
regexMaxMatchCount: "全部选中最大数量",
|
||||
|
||||
@@ -5863,6 +5863,8 @@ export default withEnglishFallback({
|
||||
autoCalculateTotalRows: "自動統計總筆數",
|
||||
autoCalculateTotalRowsDescription: "每次查詢後自動執行 COUNT(*) 顯示符合的總筆數。預設關閉以確保大型查詢速度 —— 可在結果列按需手動統計。",
|
||||
infiniteScrollDescription: "滾動到底部時自動載入下一批資料,無需手動翻頁或載入。",
|
||||
dataGridShowWhitespace: "顯示空白字元",
|
||||
dataGridShowWhitespaceDescription: "在資料儲存格中將空格顯示為 ·,定位字元顯示為 →。複製和編輯的值保持不變。",
|
||||
flatteningMultiLineText: "扁平多行文本",
|
||||
flatteningMultiLineTextDescription: "多行文本合併為單行顯示,換行符顯示為 ¶ 。",
|
||||
regexMaxMatchCount: "全部選取最大數量",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -795,6 +795,7 @@ export interface EditorSettings {
|
||||
/** Preserved for downgrade compatibility; current clients use queryResultMaxRows. */
|
||||
infiniteScrollMaxRows: number;
|
||||
flatteningMultiLineText: boolean;
|
||||
dataGridShowWhitespace: boolean;
|
||||
regexMaxMatchCount: number;
|
||||
autoCalculateTotalRows: boolean;
|
||||
mongoViewMode: "document" | "table";
|
||||
@@ -1032,6 +1033,7 @@ export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
|
||||
infiniteScroll: false,
|
||||
infiniteScrollMaxRows: 5000,
|
||||
flatteningMultiLineText: false,
|
||||
dataGridShowWhitespace: false,
|
||||
regexMaxMatchCount: 1000,
|
||||
autoCalculateTotalRows: false,
|
||||
mongoViewMode: "document",
|
||||
@@ -1490,6 +1492,7 @@ export function normalizeEditorSettings(settings: Partial<EditorSettings>, exist
|
||||
infiniteScroll: settings.infiniteScroll ?? DEFAULT_EDITOR_SETTINGS.infiniteScroll,
|
||||
infiniteScrollMaxRows: typeof settings.infiniteScrollMaxRows === "number" && settings.infiniteScrollMaxRows >= 1000 && settings.infiniteScrollMaxRows <= 50000 ? Math.round(settings.infiniteScrollMaxRows) : DEFAULT_EDITOR_SETTINGS.infiniteScrollMaxRows,
|
||||
flatteningMultiLineText: settings.flatteningMultiLineText ?? DEFAULT_EDITOR_SETTINGS.flatteningMultiLineText,
|
||||
dataGridShowWhitespace: settings.dataGridShowWhitespace ?? DEFAULT_EDITOR_SETTINGS.dataGridShowWhitespace,
|
||||
regexMaxMatchCount: typeof settings.regexMaxMatchCount === "number" && Number.isFinite(settings.regexMaxMatchCount) && settings.regexMaxMatchCount >= 100 && settings.regexMaxMatchCount <= 10000 ? Math.round(settings.regexMaxMatchCount) : DEFAULT_EDITOR_SETTINGS.regexMaxMatchCount,
|
||||
autoCalculateTotalRows: settings.autoCalculateTotalRows ?? DEFAULT_EDITOR_SETTINGS.autoCalculateTotalRows,
|
||||
mongoViewMode: settings.mongoViewMode === "table" ? "table" : DEFAULT_EDITOR_SETTINGS.mongoViewMode,
|
||||
@@ -2347,6 +2350,7 @@ export const useSettingsStore = defineStore("settings", () => {
|
||||
if (partial.completionTriggerMode !== undefined) editorSettings.value.completionTriggerMode = normalizeCompletionTriggerMode(partial.completionTriggerMode);
|
||||
if (partial.defaultTransactionMode !== undefined) editorSettings.value.defaultTransactionMode = normalizeDefaultTransactionMode(partial.defaultTransactionMode);
|
||||
if (partial.flatteningMultiLineText !== undefined) editorSettings.value.flatteningMultiLineText = partial.flatteningMultiLineText;
|
||||
if (partial.dataGridShowWhitespace !== undefined) editorSettings.value.dataGridShowWhitespace = partial.dataGridShowWhitespace;
|
||||
}
|
||||
|
||||
function updateEditorSettings(partial: Partial<EditorSettings>) {
|
||||
|
||||
Reference in New Issue
Block a user