feat(sidebar): hide configured table prefixes
This commit is contained in:
parent
390e244c85
commit
2569d54f68
|
|
@ -32,6 +32,7 @@ import {
|
|||
normalizeShortcutSettings,
|
||||
type ShortcutActionId,
|
||||
} from "@/lib/shortcutRegistry";
|
||||
import { normalizeSidebarHiddenTablePrefixes } from "@/lib/sidebarTableNameDisplay";
|
||||
import AiProviderLogo from "@/components/icons/AiProviderLogo.vue";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
|
@ -57,6 +58,7 @@ const editAppLayout = ref(settingsStore.editorSettings.appLayout);
|
|||
const editRedisScanPageSize = ref(settingsStore.editorSettings.redisScanPageSize);
|
||||
const editShortcuts = ref(normalizeShortcutSettings(settingsStore.editorSettings.shortcuts));
|
||||
const editSidebarActivation = ref(settingsStore.editorSettings.sidebarActivation);
|
||||
const editSidebarHiddenTablePrefixes = ref(settingsStore.editorSettings.sidebarHiddenTablePrefixes.join("\n"));
|
||||
const redisScanPageSizeOptions = [200, 1000, 5000, 10000];
|
||||
const systemFonts = ref<string[]>([]);
|
||||
const systemFontsLoading = ref(false);
|
||||
|
|
@ -118,6 +120,7 @@ watch(
|
|||
editRedisScanPageSize.value = settingsStore.editorSettings.redisScanPageSize;
|
||||
editShortcuts.value = normalizeShortcutSettings(settingsStore.editorSettings.shortcuts);
|
||||
editSidebarActivation.value = settingsStore.editorSettings.sidebarActivation;
|
||||
editSidebarHiddenTablePrefixes.value = settingsStore.editorSettings.sidebarHiddenTablePrefixes.join("\n");
|
||||
void loadSystemFontOptions();
|
||||
}
|
||||
},
|
||||
|
|
@ -141,7 +144,9 @@ function hasChanges(): boolean {
|
|||
editAppLayout.value !== settingsStore.editorSettings.appLayout ||
|
||||
editRedisScanPageSize.value !== settingsStore.editorSettings.redisScanPageSize ||
|
||||
JSON.stringify(editShortcuts.value) !== JSON.stringify(settingsStore.editorSettings.shortcuts) ||
|
||||
editSidebarActivation.value !== settingsStore.editorSettings.sidebarActivation
|
||||
editSidebarActivation.value !== settingsStore.editorSettings.sidebarActivation ||
|
||||
JSON.stringify(normalizeSidebarHiddenTablePrefixes(editSidebarHiddenTablePrefixes.value)) !==
|
||||
JSON.stringify(settingsStore.editorSettings.sidebarHiddenTablePrefixes)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -157,6 +162,7 @@ function applySettings() {
|
|||
redisScanPageSize: editRedisScanPageSize.value,
|
||||
shortcuts: editShortcuts.value,
|
||||
sidebarActivation: editSidebarActivation.value,
|
||||
sidebarHiddenTablePrefixes: normalizeSidebarHiddenTablePrefixes(editSidebarHiddenTablePrefixes.value),
|
||||
});
|
||||
emit("update:open", false);
|
||||
}
|
||||
|
|
@ -171,6 +177,7 @@ function resetDefaults() {
|
|||
editRedisScanPageSize.value = DEFAULT_EDITOR_SETTINGS.redisScanPageSize;
|
||||
editShortcuts.value = normalizeShortcutSettings(DEFAULT_EDITOR_SETTINGS.shortcuts);
|
||||
editSidebarActivation.value = DEFAULT_EDITOR_SETTINGS.sidebarActivation;
|
||||
editSidebarHiddenTablePrefixes.value = DEFAULT_EDITOR_SETTINGS.sidebarHiddenTablePrefixes.join("\n");
|
||||
}
|
||||
|
||||
function onExecuteModeChange(v: any) {
|
||||
|
|
@ -835,6 +842,18 @@ watch(
|
|||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="sidebar-hidden-table-prefixes">{{ t("settings.sidebarHiddenTablePrefixes") }}</Label>
|
||||
<textarea
|
||||
id="sidebar-hidden-table-prefixes"
|
||||
v-model="editSidebarHiddenTablePrefixes"
|
||||
class="min-h-24 w-full rounded-md border border-input bg-background px-3 py-2 text-sm outline-none transition-colors placeholder:text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
||||
:placeholder="t('settings.sidebarHiddenTablePrefixesPlaceholder')"
|
||||
/>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{{ t("settings.sidebarHiddenTablePrefixesDescription") }}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-else-if="activeSettingsTab === 'redis'" class="flex flex-col gap-5 py-2">
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ import { buildViewDdl } from "@/lib/viewDdl";
|
|||
import { hexToRgba } from "@/lib/color";
|
||||
import { focusSidebarRenameInput, shouldPreventRenameCloseAutoFocus } from "@/lib/sidebarRenameFocus";
|
||||
import { hasTreeNodeDatabaseContext } from "@/lib/treeNodeContext";
|
||||
import { sidebarDisplayTableName } from "@/lib/sidebarTableNameDisplay";
|
||||
import DangerConfirmDialog from "@/components/editor/DangerConfirmDialog.vue";
|
||||
import { isTauriRuntime } from "@/lib/tauriRuntime";
|
||||
import DatabaseIcon from "@/components/icons/DatabaseIcon.vue";
|
||||
|
|
@ -256,6 +257,17 @@ function displayLabel(node: TreeNode): string {
|
|||
return isGroupLabel(node) ? t(node.label) : node.label;
|
||||
}
|
||||
|
||||
function visibleLabel(node: TreeNode): string {
|
||||
if (node.type === "table" || node.type === "view" || node.type === "mongo-collection") {
|
||||
return sidebarDisplayTableName(node.label, settingsStore.editorSettings.sidebarHiddenTablePrefixes);
|
||||
}
|
||||
return displayLabel(node);
|
||||
}
|
||||
|
||||
function isTooltipDisabled(node: TreeNode): boolean {
|
||||
return !isTruncated.value && visibleLabel(node) === displayLabel(node);
|
||||
}
|
||||
|
||||
async function toggle() {
|
||||
const node = props.node;
|
||||
if (node.isLoading) return;
|
||||
|
|
@ -1797,9 +1809,9 @@ const isDragging = computed(() => dragState.active && dragState.draggedId === pr
|
|||
@keydown.escape.prevent="isRenamingGroup = false"
|
||||
@click.stop
|
||||
/>
|
||||
<Tooltip v-else :disabled="!isTruncated">
|
||||
<Tooltip v-else :disabled="isTooltipDisabled(node)">
|
||||
<TooltipTrigger as-child>
|
||||
<span ref="labelRef" class="min-w-0 flex-1 truncate">{{ displayLabel(node) }}</span>
|
||||
<span ref="labelRef" class="min-w-0 flex-1 truncate">{{ visibleLabel(node) }}</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="right" :side-offset="8">{{ displayLabel(node) }}</TooltipContent>
|
||||
</Tooltip>
|
||||
|
|
|
|||
|
|
@ -1198,6 +1198,10 @@ export default {
|
|||
sidebarActivationSingleDescription: "Open actionable sidebar items with one click.",
|
||||
sidebarActivationDouble: "Double click",
|
||||
sidebarActivationDoubleDescription: "Single click selects rows; double click opens items.",
|
||||
sidebarHiddenTablePrefixes: "Hidden table name prefixes",
|
||||
sidebarHiddenTablePrefixesDescription:
|
||||
"One prefix per line. Only sidebar table, view, and collection labels are shortened; tooltips and actions still use the full name.",
|
||||
sidebarHiddenTablePrefixesPlaceholder: "Example:\nODS_\nT8Y2_LONG_",
|
||||
apply: "Apply",
|
||||
reset: "Reset",
|
||||
resetDefaults: "Reset Defaults",
|
||||
|
|
|
|||
|
|
@ -1096,6 +1096,10 @@ export default {
|
|||
sidebarActivationSingleDescription: "Abrir elementos accionables de la barra lateral con un clic.",
|
||||
sidebarActivationDouble: "Doble clic",
|
||||
sidebarActivationDoubleDescription: "Un clic selecciona la fila; doble clic abre elementos.",
|
||||
sidebarHiddenTablePrefixes: "Prefijos ocultos de tablas",
|
||||
sidebarHiddenTablePrefixesDescription:
|
||||
"Un prefijo por linea. Solo acorta etiquetas de tablas, vistas y colecciones en la barra lateral; las acciones y ayudas usan el nombre completo.",
|
||||
sidebarHiddenTablePrefixesPlaceholder: "Ejemplo:\nODS_\nT8Y2_LONG_",
|
||||
apply: "Aplicar",
|
||||
reset: "Restablecer",
|
||||
resetDefaults: "Restablecer valores por defecto",
|
||||
|
|
|
|||
|
|
@ -1176,6 +1176,10 @@ export default {
|
|||
sidebarActivationSingleDescription: "单击即可打开侧边栏中的可操作项目。",
|
||||
sidebarActivationDouble: "双击打开",
|
||||
sidebarActivationDoubleDescription: "单击只选中高亮,双击打开项目。",
|
||||
sidebarHiddenTablePrefixes: "隐藏表名前缀",
|
||||
sidebarHiddenTablePrefixesDescription:
|
||||
"每行一个前缀,仅影响侧边栏表、视图和集合的显示名称,悬浮提示和实际操作仍使用完整名称。",
|
||||
sidebarHiddenTablePrefixesPlaceholder: "例如:\nODS_\nT8Y2_LONG_",
|
||||
apply: "应用",
|
||||
reset: "重置",
|
||||
resetDefaults: "恢复默认",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
export function normalizeSidebarHiddenTablePrefixes(value: unknown): string[] {
|
||||
const rawPrefixes =
|
||||
typeof value === "string"
|
||||
? value.split(/\r?\n/)
|
||||
: Array.isArray(value)
|
||||
? value.filter((item) => typeof item === "string")
|
||||
: [];
|
||||
const seen = new Set<string>();
|
||||
const prefixes: string[] = [];
|
||||
|
||||
for (const rawPrefix of rawPrefixes) {
|
||||
const prefix = rawPrefix.trim();
|
||||
if (!prefix || seen.has(prefix)) continue;
|
||||
seen.add(prefix);
|
||||
prefixes.push(prefix);
|
||||
}
|
||||
|
||||
return prefixes.sort((a, b) => b.length - a.length);
|
||||
}
|
||||
|
||||
export function sidebarDisplayTableName(name: string, prefixes: readonly string[]): string {
|
||||
const prefix = [...prefixes]
|
||||
.sort((a, b) => b.length - a.length)
|
||||
.find((item) => name.startsWith(item) && name.length > item.length);
|
||||
return prefix ? `...${name.slice(prefix.length)}` : name;
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import {
|
|||
} from "@/lib/columnFormatter";
|
||||
import { normalizeShortcutSettings, type ShortcutSettings } from "@/lib/shortcutRegistry";
|
||||
import { normalizeResultPageSize } from "@/lib/paginationPageSize";
|
||||
import { normalizeSidebarHiddenTablePrefixes } from "@/lib/sidebarTableNameDisplay";
|
||||
import type { SidebarActivation } from "@/lib/treeNodeClick";
|
||||
|
||||
export type AiProvider =
|
||||
|
|
@ -170,6 +171,7 @@ export interface EditorSettings {
|
|||
mongoViewMode: "document" | "table";
|
||||
shortcuts: ShortcutSettings;
|
||||
sidebarActivation: SidebarActivation;
|
||||
sidebarHiddenTablePrefixes: string[];
|
||||
columnFormatters: Record<string, ColumnFormatterConfig>;
|
||||
customColumnFormatters: Record<string, CustomColumnFormatterConfig>;
|
||||
}
|
||||
|
|
@ -209,6 +211,7 @@ export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
|
|||
mongoViewMode: "document",
|
||||
shortcuts: normalizeShortcutSettings(),
|
||||
sidebarActivation: "single",
|
||||
sidebarHiddenTablePrefixes: [],
|
||||
columnFormatters: {},
|
||||
customColumnFormatters: {},
|
||||
};
|
||||
|
|
@ -253,6 +256,7 @@ export function normalizeEditorSettings(settings: Partial<EditorSettings>): Edit
|
|||
settings.sidebarActivation === "single" || settings.sidebarActivation === "double"
|
||||
? settings.sidebarActivation
|
||||
: DEFAULT_EDITOR_SETTINGS.sidebarActivation,
|
||||
sidebarHiddenTablePrefixes: normalizeSidebarHiddenTablePrefixes(settings.sidebarHiddenTablePrefixes),
|
||||
columnFormatters: normalizeColumnFormatters(settings.columnFormatters),
|
||||
customColumnFormatters: normalizeCustomColumnFormatters(settings.customColumnFormatters),
|
||||
};
|
||||
|
|
@ -334,6 +338,9 @@ export const useSettingsStore = defineStore("settings", () => {
|
|||
const normalizedPartial = {
|
||||
...partial,
|
||||
...(partial.pageSize !== undefined ? { pageSize: normalizeResultPageSize(partial.pageSize) } : {}),
|
||||
...(partial.sidebarHiddenTablePrefixes !== undefined
|
||||
? { sidebarHiddenTablePrefixes: normalizeSidebarHiddenTablePrefixes(partial.sidebarHiddenTablePrefixes) }
|
||||
: {}),
|
||||
};
|
||||
Object.assign(editorSettings.value, normalizedPartial);
|
||||
saveEditorSettings(editorSettings.value);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ test("settings dialog exposes sidebar activation in navigation settings", () =>
|
|||
assert.match(source, /value: "navigation"/);
|
||||
assert.match(source, /activeSettingsTab === ['"]navigation['"]/);
|
||||
assert.match(source, /settings\.sidebarActivation/);
|
||||
assert.match(source, /settings\.sidebarHiddenTablePrefixes/);
|
||||
assert.match(source, /editSidebarHiddenTablePrefixes/);
|
||||
});
|
||||
|
||||
test("AI settings can browse provider model names while keeping manual input", () => {
|
||||
|
|
|
|||
|
|
@ -51,6 +51,15 @@ test("keeps saved sidebar activation", () => {
|
|||
assert.equal(normalizeEditorSettings({ sidebarActivation: "invalid" } as any).sidebarActivation, "single");
|
||||
});
|
||||
|
||||
test("normalizes saved sidebar hidden table prefixes", () => {
|
||||
assert.deepEqual(DEFAULT_EDITOR_SETTINGS.sidebarHiddenTablePrefixes, []);
|
||||
assert.deepEqual(
|
||||
normalizeEditorSettings({ sidebarHiddenTablePrefixes: [" app_", "app_", "", "ods."] } as any)
|
||||
.sidebarHiddenTablePrefixes,
|
||||
["app_", "ods."],
|
||||
);
|
||||
});
|
||||
|
||||
test("defaults column formatters to an empty record", () => {
|
||||
assert.deepEqual(DEFAULT_EDITOR_SETTINGS.columnFormatters, {});
|
||||
assert.deepEqual(normalizeEditorSettings({}).columnFormatters, {});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
normalizeSidebarHiddenTablePrefixes,
|
||||
sidebarDisplayTableName,
|
||||
} from "../../apps/desktop/src/lib/sidebarTableNameDisplay.ts";
|
||||
|
||||
test("normalizes sidebar hidden table prefixes", () => {
|
||||
assert.deepEqual(normalizeSidebarHiddenTablePrefixes([" app_", "app_", "", "ods."]), ["app_", "ods."]);
|
||||
assert.deepEqual(normalizeSidebarHiddenTablePrefixes("app_\nods.\n app_ "), ["app_", "ods."]);
|
||||
assert.deepEqual(normalizeSidebarHiddenTablePrefixes(null), []);
|
||||
});
|
||||
|
||||
test("hides a configured table prefix from the sidebar label", () => {
|
||||
assert.equal(sidebarDisplayTableName("t8y2_long_customer_order", ["t8y2_long_"]), "...customer_order");
|
||||
});
|
||||
|
||||
test("uses the longest matching sidebar table prefix", () => {
|
||||
assert.equal(sidebarDisplayTableName("app_sales_orders", ["app_", "app_sales_"]), "...orders");
|
||||
});
|
||||
|
||||
test("keeps the full name when hiding would leave an empty label", () => {
|
||||
assert.equal(sidebarDisplayTableName("app_", ["app_"]), "app_");
|
||||
});
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import { readFileSync } from "node:fs";
|
||||
import { strict as assert } from "node:assert";
|
||||
import test from "node:test";
|
||||
|
||||
const source = readFileSync("apps/desktop/src/components/sidebar/TreeItem.vue", "utf8");
|
||||
|
||||
test("sidebar table prefix hiding is display-only", () => {
|
||||
assert.match(source, /sidebarDisplayTableName/);
|
||||
assert.match(source, /sidebarHiddenTablePrefixes/);
|
||||
assert.match(source, /node\.type === "table" \|\| node\.type === "view" \|\| node\.type === "mongo-collection"/);
|
||||
assert.match(source, /{{ visibleLabel\(node\) }}/);
|
||||
assert.match(source, /{{ displayLabel\(node\) }}/);
|
||||
});
|
||||
Loading…
Reference in New Issue