mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-02 02:34:42 +08:00
feat: fix Redis key search visibility
This commit is contained in:
@@ -813,6 +813,7 @@ function typeColor(type: string): string {
|
||||
}
|
||||
|
||||
let searchTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
let hasAutoFocusedSearch = false;
|
||||
|
||||
function onSearchInput() {
|
||||
if (searchTimer) clearTimeout(searchTimer);
|
||||
@@ -834,6 +835,15 @@ function getSearchInput(): HTMLInputElement | null {
|
||||
return rootRef.value?.querySelector<HTMLInputElement>("[data-redis-search-input]") ?? null;
|
||||
}
|
||||
|
||||
async function autofocusSearchOnce() {
|
||||
if (hasAutoFocusedSearch) return;
|
||||
await nextTick();
|
||||
const input = getSearchInput();
|
||||
if (!input) return;
|
||||
hasAutoFocusedSearch = true;
|
||||
input.focus({ preventScroll: true });
|
||||
}
|
||||
|
||||
function getCommandInput(): HTMLInputElement | null {
|
||||
return rootRef.value?.querySelector<HTMLInputElement>("[data-redis-command-input]") ?? null;
|
||||
}
|
||||
@@ -938,6 +948,7 @@ function onCommandInputKeydown(event: KeyboardEvent) {
|
||||
|
||||
onMounted(async () => {
|
||||
resumeRedisBrowserBackgroundWork();
|
||||
void autofocusSearchOnce();
|
||||
try {
|
||||
await connectionStore.ensureConnected(props.connectionId);
|
||||
} catch (e) {
|
||||
@@ -948,6 +959,7 @@ onMounted(async () => {
|
||||
|
||||
onActivated(async () => {
|
||||
resumeRedisBrowserBackgroundWork();
|
||||
void autofocusSearchOnce();
|
||||
// Ensure the connection is still alive after reactivation (e.g. tab switch).
|
||||
// If keys failed to load previously (empty list), retry loading.
|
||||
try {
|
||||
@@ -981,33 +993,57 @@ defineExpose({ focusSearch });
|
||||
<Pane :size="36" :min-size="24">
|
||||
<div class="relative h-full flex flex-col overflow-hidden">
|
||||
<!-- Toolbar -->
|
||||
<div class="h-9 flex items-center gap-1 px-2 border-b shrink-0">
|
||||
<Search class="w-3.5 h-3.5 text-muted-foreground shrink-0" />
|
||||
<div class="h-6 flex rounded-md border bg-muted/30 p-0.5 shrink-0" role="group">
|
||||
<button type="button" class="h-5 px-2 text-xs rounded-sm transition-colors" :class="searchMode === 'key' ? 'bg-background text-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground'" @click="setSearchMode('key')">
|
||||
{{ t("redis.searchByKey") }}
|
||||
</button>
|
||||
<button type="button" class="h-5 px-2 text-xs rounded-sm transition-colors" :class="searchMode === 'value' ? 'bg-background text-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground'" @click="setSearchMode('value')">
|
||||
{{ t("redis.searchByValue") }}
|
||||
</button>
|
||||
<button type="button" class="h-5 px-2 text-xs rounded-sm transition-colors" :class="searchMode === 'all' ? 'bg-background text-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground'" @click="setSearchMode('all')">
|
||||
{{ t("redis.searchByAll") }}
|
||||
</button>
|
||||
<div class="border-b px-2 py-2 shrink-0">
|
||||
<div class="flex flex-wrap items-start gap-1.5">
|
||||
<div class="flex min-w-0 flex-1 flex-wrap rounded-md border bg-muted/30 p-0.5" role="group">
|
||||
<button type="button" class="h-5 px-2 text-xs rounded-sm transition-colors" :class="searchMode === 'key' ? 'bg-background text-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground'" @click="setSearchMode('key')">
|
||||
{{ t("redis.searchByKey") }}
|
||||
</button>
|
||||
<button type="button" class="h-5 px-2 text-xs rounded-sm transition-colors" :class="searchMode === 'value' ? 'bg-background text-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground'" @click="setSearchMode('value')">
|
||||
{{ t("redis.searchByValue") }}
|
||||
</button>
|
||||
<button type="button" class="h-5 px-2 text-xs rounded-sm transition-colors" :class="searchMode === 'all' ? 'bg-background text-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground'" @click="setSearchMode('all')">
|
||||
{{ t("redis.searchByAll") }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="ml-auto flex min-w-0 flex-wrap items-center justify-end gap-1">
|
||||
<span class="min-w-0 max-w-full truncate text-xs text-muted-foreground" :title="keyCountText">{{ keyCountText }}</span>
|
||||
<Button v-if="checkedKeys.size > 0" variant="ghost" size="sm" class="h-6 shrink-0 text-xs text-destructive" @click="requestBatchDelete"> <Trash2 class="w-3 h-3 mr-1" />{{ checkedKeys.size }} </Button>
|
||||
<Button variant="ghost" size="icon" class="h-6 w-6 shrink-0" @click="loadKeys">
|
||||
<Loader2 v-if="loading" class="h-3 w-3 animate-spin" />
|
||||
<RefreshCw v-else class="h-3 w-3" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" class="h-6 w-6 shrink-0" :title="t('redis.createKey')" @click="openCreateKeyDialog">
|
||||
<Plus class="h-3 w-3" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2 flex min-w-0 flex-wrap items-center gap-1.5">
|
||||
<div class="relative min-w-[120px] flex-1 basis-[180px]">
|
||||
<Search class="pointer-events-none absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground/80" />
|
||||
<Input
|
||||
v-model="searchPattern"
|
||||
data-redis-search-input
|
||||
class="h-8 border-border/70 bg-background pl-8 pr-3 text-xs shadow-sm caret-primary placeholder:text-muted-foreground/80 focus-visible:border-primary/60 focus-visible:ring-2 focus-visible:ring-primary/20"
|
||||
:placeholder="searchPlaceholder"
|
||||
@input="onSearchInput"
|
||||
@keydown="onSearchKeydown"
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
v-if="searchMode === 'key'"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
class="h-8 max-w-full shrink-0 px-2 text-xs"
|
||||
:class="fuzzyKeySearch ? 'bg-accent text-accent-foreground' : 'border border-dashed border-border/70 text-muted-foreground hover:text-foreground'"
|
||||
:title="t('redis.fuzzyMatchTitle')"
|
||||
:aria-pressed="fuzzyKeySearch"
|
||||
@click="toggleFuzzyKeySearch"
|
||||
>
|
||||
<Asterisk class="h-3 w-3 mr-1" />
|
||||
{{ t("redis.fuzzyMatch") }}
|
||||
</Button>
|
||||
</div>
|
||||
<Input v-model="searchPattern" data-redis-search-input class="h-6 text-xs border-0 shadow-none focus-visible:ring-0" :placeholder="searchPlaceholder" @input="onSearchInput" @keydown="onSearchKeydown" />
|
||||
<Button v-if="searchMode === 'key'" variant="ghost" size="sm" class="h-6 shrink-0 px-2 text-xs" :class="fuzzyKeySearch ? 'bg-accent text-accent-foreground' : 'text-muted-foreground'" :title="t('redis.fuzzyMatchTitle')" :aria-pressed="fuzzyKeySearch" @click="toggleFuzzyKeySearch">
|
||||
<Asterisk class="h-3 w-3 mr-1" />
|
||||
{{ t("redis.fuzzyMatch") }}
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" class="h-6 w-6 shrink-0" @click="loadKeys">
|
||||
<Loader2 v-if="loading" class="h-3 w-3 animate-spin" />
|
||||
<RefreshCw v-else class="h-3 w-3" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" class="h-6 w-6 shrink-0" :title="t('redis.createKey')" @click="openCreateKeyDialog">
|
||||
<Plus class="h-3 w-3" />
|
||||
</Button>
|
||||
<span class="text-xs text-muted-foreground shrink-0 ml-1">{{ keyCountText }}</span>
|
||||
<Button v-if="checkedKeys.size > 0" variant="ghost" size="sm" class="h-6 text-xs text-destructive shrink-0 ml-1" @click="requestBatchDelete"> <Trash2 class="w-3 h-3 mr-1" />{{ checkedKeys.size }} </Button>
|
||||
</div>
|
||||
|
||||
<div v-if="flatKeys.length === 0 && !loading" class="flex-1 flex flex-col items-center justify-center text-muted-foreground text-xs p-4 text-center">
|
||||
|
||||
Reference in New Issue
Block a user