From baa8917eaed9e521da3cffa0d28d07f07fc3ddeb Mon Sep 17 00:00:00 2001 From: Jokul Date: Tue, 7 Jul 2026 23:49:47 +0800 Subject: [PATCH] style: optimize DB selector dropdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace el-select with custom el-dropdown for compact tab bar fit - Trigger: compact pill with DB name + key count badge + arrow - Dropdown: two-column layout (DB index + custom name | key count badge + edit icon) - Edit icon hidden until hover, colored on hover - Selected DB highlighted with accent color - Remove duplicate style block - Key counts formatted (10k+ → '10k') --- src/components/DbSelector.vue | 260 +++++++++++++++++++++++----------- 1 file changed, 175 insertions(+), 85 deletions(-) diff --git a/src/components/DbSelector.vue b/src/components/DbSelector.vue index 0c80551..1882174 100644 --- a/src/components/DbSelector.vue +++ b/src/components/DbSelector.vue @@ -28,27 +28,27 @@ function loadCustomNames() { } } -function getDbLabel(dbIndex: number): string { - const count = dbKeyCounts.value[dbIndex] +function getDbShortLabel(dbIndex: number): string { const customName = customDbNames.value[String(dbIndex)] - const baseLabel = count !== undefined && count > 0 - ? `DB${dbIndex} (${t('db.keyCount', { n: count })})` - : count === 0 - ? `DB${dbIndex} (${t('db.noKeys')})` - : `DB${dbIndex}` - if (customName) { - return `${baseLabel} - ${customName}` - } - return baseLabel + return customName || `DB${dbIndex}` +} + +function getDbKeyCount(dbIndex: number): number | undefined { + return dbKeyCounts.value[dbIndex] } const dbList = computed(() => { return Array.from({ length: dbCount.value }, (_, i) => ({ value: i, - label: getDbLabel(i), + label: getDbShortLabel(i), + count: getDbKeyCount(i), + customName: customDbNames.value[String(i)] || '', })) }) +const currentLabel = computed(() => getDbShortLabel(selectedDb.value)) +const currentCount = computed(() => getDbKeyCount(selectedDb.value)) + async function handleEditName(dbIndex: number, event: MouseEvent) { event.stopPropagation() const currentName = customDbNames.value[String(dbIndex)] || '' @@ -80,7 +80,6 @@ async function handleEditName(dbIndex: number, event: MouseEvent) { async function fetchDbInfo() { if (!connStore.activeId) return try { - // Get actual DB count from CONFIG GET databases const configResult = await window.electronAPI.redis.execute( connStore.activeId, 'CONFIG', ['GET', 'databases'] ) @@ -92,7 +91,6 @@ async function fetchDbInfo() { } try { - // Parse INFO keyspace for per-DB key counts const info = await connStore.getInfo() const keyspace: Record = {} const match = info.match(/# Keyspace[\s\S]*?(?=\n#|$)/) @@ -105,7 +103,6 @@ async function fetchDbInfo() { } } } - // Mark DBs with 0 keys explicitly for (let i = 0; i < dbCount.value; i++) { if (!(i in keyspace)) { keyspace[i] = 0 @@ -142,31 +139,53 @@ async function switchDb(db: number) { connStore.saveLastDb(connStore.activeId, db) await keyStore.selectDb(connStore.activeId, db) } + +function formatCount(n: number): string { + if (n >= 10000) return `${(n / 1000).toFixed(0)}k` + return String(n) +} @@ -174,84 +193,155 @@ async function switchDb(db: number) { .db-selector { display: flex; align-items: center; - gap: 4px; - padding: 0 8px; } -.db-label { +/* Trigger button — compact, fits in tab bar */ +.db-trigger { + display: flex; + align-items: center; + gap: 4px; + padding: 4px 10px; + background: var(--bg-card); + border: 1px solid var(--border-color); + border-radius: 6px; + cursor: pointer; + font-size: 12px; + color: var(--text-primary); + transition: all 0.15s; + white-space: nowrap; + user-select: none; +} + +.db-trigger:hover { + border-color: var(--accent); + background: var(--bg-hover); +} + +.db-trigger-label { + font-weight: 600; + font-family: 'Cascadia Code', 'Fira Code', monospace; +} + +.db-trigger-badge { + font-size: 10px; + font-weight: 700; + color: var(--accent-light); + background: var(--accent-bg, rgba(99, 102, 241, 0.15)); + padding: 1px 6px; + border-radius: 8px; + line-height: 1.4; +} + +.db-trigger-arrow { font-size: 10px; color: var(--text-muted); - font-weight: 600; + transition: transform 0.15s; } -.db-option-content { +.db-trigger:hover .db-trigger-arrow { + color: var(--accent-light); +} + +/* Dropdown */ +:deep(.db-dropdown) { + min-width: 200px; + max-height: 360px; + overflow-y: auto; +} + +.db-dropdown-header { + padding: 6px 12px; + font-size: 10px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--text-muted); + border-bottom: 1px solid var(--border-color); +} + +:deep(.el-dropdown-menu__item) { + padding: 0; +} + +:deep(.el-dropdown-menu__item.is-selected) { + background: var(--bg-card-active); + color: var(--accent-light); +} + +.db-option { display: flex; align-items: center; justify-content: space-between; width: 100%; + padding: 6px 12px; + gap: 8px; } -.db-option-label { - flex: 1; +.db-option-left { + display: flex; + align-items: center; + gap: 6px; + min-width: 0; +} + +.db-option-index { + font-size: 12px; + font-weight: 600; + font-family: 'Cascadia Code', 'Fira Code', monospace; + color: var(--text-primary); + flex-shrink: 0; +} + +.db-option-name { + font-size: 11px; + color: var(--accent-light); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -.db-edit-icon { - font-size: 12px; - cursor: pointer; - opacity: 0.4; - transition: opacity 0.2s; - margin-left: 4px; +.db-option-right { + display: flex; + align-items: center; + gap: 6px; flex-shrink: 0; } -.db-edit-icon:hover { - opacity: 1; - color: var(--accent); +.db-option-count { + font-size: 10px; + font-weight: 600; + color: var(--accent-light); + background: var(--accent-bg, rgba(99, 102, 241, 0.15)); + padding: 1px 6px; + border-radius: 8px; } -:deep(.el-select .el-input__wrapper) { - background: var(--bg-card); - border-color: var(--border-color); - box-shadow: none; -} - -:deep(.el-select .el-input__wrapper:hover) { - border-color: var(--border-accent); -} - -:deep(.el-select .el-input__wrapper.is-focus) { - border-color: var(--accent); -} - - -