refactor(ui): consolidate 7 hand-rolled kebabs into one KebabMenu (DRY pattern sweep)
First pattern-consistency DRY pass (process #594). The overflow kebab was hand-rolled 7 ways in two divergent activator strategies — Pattern A (#activator + v-bind) which silently breaks inside the teleported image modal (#711), and Pattern B (manual v-model + activator=parent + open-on-click=false + z-index 2400) the modal kebabs needed as a workaround. New <KebabMenu> (components/common) bakes in the modal-safe strategy UNIVERSALLY, so every kebab works in modal and non-modal contexts — folding the latent #711-class bug fix into all five Pattern-A sites. Menu items go in the default slot; variations (size/variant/location/label/min-width) are props. Adopted across all 7: TagChip, SuggestionItem, TagCard, SeriesView card, SeriesManageView, BackupRunsTable, SourceActions. Exhaustiveness (§8b): mdi-dots-vertical now lives only in KebabMenu. Labeled dropdowns / nav menus / filter popovers are a different concept and left alone. Seeded the pattern catalog so new code reuses the primitive. Test: KebabMenu renders slot items + trigger label/glyph + presentational props. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -26,40 +26,33 @@
|
||||
</v-tooltip>
|
||||
</v-btn>
|
||||
|
||||
<v-menu location="bottom end">
|
||||
<template #activator="{ props: menuProps }">
|
||||
<v-btn icon size="small" variant="text" v-bind="menuProps" @click.stop>
|
||||
<v-icon>mdi-dots-vertical</v-icon>
|
||||
<v-tooltip activator="parent" location="top">More actions</v-tooltip>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list density="compact" min-width="260">
|
||||
<v-list-item
|
||||
v-if="isPatreon && !running"
|
||||
prepend-icon="mdi-backup-restore"
|
||||
@click="emit('recover', source)"
|
||||
>
|
||||
<v-list-item-title>Recover dropped near-duplicates</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
Re-fetch images previously dropped as near-dups and re-judge them
|
||||
under the current similarity threshold
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-divider v-if="isPatreon && !running" />
|
||||
<v-list-item
|
||||
base-color="error"
|
||||
prepend-icon="mdi-delete-outline"
|
||||
@click="emit('remove', source)"
|
||||
>
|
||||
<v-list-item-title>Remove source</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
<KebabMenu size="small" :min-width="260" label="More actions">
|
||||
<v-list-item
|
||||
v-if="isPatreon && !running"
|
||||
prepend-icon="mdi-backup-restore"
|
||||
@click="emit('recover', source)"
|
||||
>
|
||||
<v-list-item-title>Recover dropped near-duplicates</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
Re-fetch images previously dropped as near-dups and re-judge them
|
||||
under the current similarity threshold
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-divider v-if="isPatreon && !running" />
|
||||
<v-list-item
|
||||
base-color="error"
|
||||
prepend-icon="mdi-delete-outline"
|
||||
@click="emit('remove', source)"
|
||||
>
|
||||
<v-list-item-title>Remove source</v-list-item-title>
|
||||
</v-list-item>
|
||||
</KebabMenu>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import KebabMenu from '../common/KebabMenu.vue'
|
||||
|
||||
const props = defineProps({
|
||||
source: { type: Object, required: true },
|
||||
|
||||
Reference in New Issue
Block a user