Merge pull request '#69 dev→main: release v26.06.04.0' from dev into main
CI / lint (push) Successful in 2s
CI / backend-lint-and-test (push) Successful in 12s
CI / frontend-build (push) Successful in 19s
CI / integration (push) Successful in 3m2s
Build images / sign-extension (push) Has been skipped
Build images / build-ml (push) Successful in 6s
Build images / build-web (push) Successful in 6s
CI / lint (push) Successful in 2s
CI / backend-lint-and-test (push) Successful in 12s
CI / frontend-build (push) Successful in 19s
CI / integration (push) Successful in 3m2s
Build images / sign-extension (push) Has been skipped
Build images / build-ml (push) Successful in 6s
Build images / build-web (push) Successful in 6s
This commit was merged in pull request #69.
This commit is contained in:
@@ -17,6 +17,10 @@
|
||||
</div>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<!-- Not every character belongs to a fandom (original characters,
|
||||
unsorted, etc.). "No fandom" creates the character unassigned;
|
||||
a fandom can still be set later from the chip's kebab menu. -->
|
||||
<v-btn variant="text" @click="onNoFandom">No fandom</v-btn>
|
||||
<v-spacer />
|
||||
<v-btn @click="$emit('cancel')">Cancel</v-btn>
|
||||
<v-btn :disabled="!selectedId" color="primary" rounded="pill" @click="onConfirm">Use this fandom</v-btn>
|
||||
@@ -45,4 +49,9 @@ function onConfirm() {
|
||||
const f = store.fandomCache.find(x => x.id === selectedId.value)
|
||||
if (f) emit('confirm', f)
|
||||
}
|
||||
// Create the character with no fandom. Emits null so the caller knows this
|
||||
// was a deliberate "unassigned", not a cancel.
|
||||
function onNoFandom() {
|
||||
emit('confirm', null)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -19,24 +19,25 @@
|
||||
>
|
||||
Accept
|
||||
</v-btn>
|
||||
<!-- Operator-flagged 2026-06-02: the kebab menu wasn't opening.
|
||||
Wrapping in a <span @click.stop> matches the TagPanel chip
|
||||
fix — even though there's no parent click capture here today,
|
||||
the wrap is harmless and keeps both kebabs on the same
|
||||
pattern. Click bubbles from the v-btn → opens menu via
|
||||
activator props → bubble continues to span → stopPropagation
|
||||
halts it. -->
|
||||
<span class="fc-suggestion__menu-wrap" @click.stop>
|
||||
<v-menu>
|
||||
<template #activator="{ props }">
|
||||
<v-btn
|
||||
class="fc-suggestion__menu"
|
||||
icon="mdi-dots-vertical" size="small"
|
||||
variant="outlined" density="compact"
|
||||
:aria-label="`More actions for ${suggestion.display_name}`"
|
||||
v-bind="props"
|
||||
/>
|
||||
</template>
|
||||
<!-- Operator-flagged 2026-06-04: the kebab still wasn't opening. The
|
||||
prior `#activator` + `v-bind="props"` path never toggled the menu
|
||||
inside this teleported modal, while v-model-driven overlays (the
|
||||
dialogs in this modal) work fine. So drive the menu explicitly:
|
||||
the button toggles `menuOpen` with @click.stop (also shields any
|
||||
parent), and `activator="parent"` anchors the menu for positioning
|
||||
only — `:open-on-click="false"` keeps Vuetify's activator-click out
|
||||
of it, so there's a single, reliable opener. -->
|
||||
<span class="fc-suggestion__menu-wrap">
|
||||
<v-btn
|
||||
class="fc-suggestion__menu"
|
||||
icon="mdi-dots-vertical" size="small"
|
||||
variant="outlined" density="compact"
|
||||
:aria-label="`More actions for ${suggestion.display_name}`"
|
||||
@click.stop="menuOpen = !menuOpen"
|
||||
/>
|
||||
<v-menu
|
||||
v-model="menuOpen" activator="parent" :open-on-click="false"
|
||||
>
|
||||
<v-list density="compact">
|
||||
<v-list-item @click="$emit('alias', suggestion)">
|
||||
<v-list-item-title>Treat as alias for…</v-list-item-title>
|
||||
@@ -51,11 +52,12 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const props = defineProps({ suggestion: { type: Object, required: true } })
|
||||
defineEmits(['accept', 'alias', 'dismiss'])
|
||||
|
||||
const menuOpen = ref(false)
|
||||
const scorePct = computed(() => `${Math.round(props.suggestion.score * 100)}%`)
|
||||
</script>
|
||||
|
||||
|
||||
@@ -147,10 +147,14 @@ function onCreate () {
|
||||
reset()
|
||||
}
|
||||
|
||||
// fandom is null when the user picked "No fandom" — characters don't all
|
||||
// belong to a fandom. The backend already accepts fandom_id: null for the
|
||||
// character kind (tag.kind check + nullable fandom_id), and a fandom can be
|
||||
// assigned later from the chip kebab's "Set fandom…".
|
||||
function onFandomChosen (fandom) {
|
||||
fandomDialog.value = false
|
||||
emit('pick-new', {
|
||||
name: pendingNewName, kind: 'character', fandom_id: fandom.id,
|
||||
name: pendingNewName, kind: 'character', fandom_id: fandom ? fandom.id : null,
|
||||
})
|
||||
pendingNewName = null
|
||||
reset()
|
||||
|
||||
@@ -10,20 +10,24 @@
|
||||
>
|
||||
<v-icon start size="x-small">{{ iconFor(tag.kind) }}</v-icon>
|
||||
{{ tag.name }}<span v-if="tag.fandom_id">→</span>
|
||||
<!-- Operator-flagged 2026-06-02: the previous activator had
|
||||
`@click.stop` directly on the v-icon, which silently
|
||||
overrode Vuetify's onClick from `v-bind="mp"` — the menu
|
||||
never opened. Now the v-icon receives the activator
|
||||
onClick cleanly, and the wrapping span absorbs the
|
||||
bubbled click so the chip's close button isn't tripped. -->
|
||||
<span class="kebab-wrap" @click.stop>
|
||||
<v-menu>
|
||||
<template #activator="{ props: mp }">
|
||||
<v-icon
|
||||
v-bind="mp" size="x-small" class="ml-1"
|
||||
icon="mdi-dots-vertical"
|
||||
/>
|
||||
</template>
|
||||
<!-- Operator-flagged 2026-06-04: the `#activator` + `v-bind` menu
|
||||
never opened inside this teleported modal. Drive it explicitly
|
||||
instead (same mechanism as the dialogs below, which work): the
|
||||
icon toggles `openTagId` with @click.stop (shielding the chip's
|
||||
close button), and `activator="parent"` + `:open-on-click=false`
|
||||
anchors the menu for positioning only. One tag's menu open at a
|
||||
time, so a single id is enough. -->
|
||||
<span class="kebab-wrap">
|
||||
<v-icon
|
||||
size="x-small" class="ml-1 kebab-icon"
|
||||
icon="mdi-dots-vertical"
|
||||
@click.stop="openTagId = openTagId === tag.id ? null : tag.id"
|
||||
/>
|
||||
<v-menu
|
||||
:model-value="openTagId === tag.id"
|
||||
activator="parent" :open-on-click="false"
|
||||
@update:model-value="v => { if (!v) openTagId = null }"
|
||||
>
|
||||
<v-list density="compact">
|
||||
<v-list-item @click="openRename(tag)">
|
||||
<v-list-item-title>Rename…</v-list-item-title>
|
||||
@@ -84,6 +88,9 @@ import FandomSetDialog from './FandomSetDialog.vue'
|
||||
const modal = useModalStore()
|
||||
const store = useTagStore()
|
||||
const errorMsg = ref(null)
|
||||
// Which tag chip's kebab menu is open (only one at a time). Drives each
|
||||
// chip menu's v-model so opening never depends on Vuetify's activator click.
|
||||
const openTagId = ref(null)
|
||||
|
||||
const KIND_ICONS = {
|
||||
general: 'mdi-tag', character: 'mdi-account-circle',
|
||||
@@ -147,4 +154,5 @@ async function onFandomUpdated() {
|
||||
}
|
||||
.fc-tag-panel__chips { display: flex; flex-wrap: wrap; gap: 6px; }
|
||||
.kebab-wrap { display: inline-flex; align-items: center; }
|
||||
.kebab-icon { cursor: pointer; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user