fix(kiwi-a11y): undo toast for 'I cooked this' dismiss action (#45)
This commit is contained in:
parent
bc04739447
commit
5c135d0860
2 changed files with 52 additions and 1 deletions
|
|
@ -601,11 +601,22 @@
|
||||||
@close="browserSelectedRecipe = null"
|
@close="browserSelectedRecipe = null"
|
||||||
@cooked="browserSelectedRecipe = null"
|
@cooked="browserSelectedRecipe = null"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- Undo toast for "I cooked this" dismiss -->
|
||||||
|
<div
|
||||||
|
v-if="lastCookedRecipe"
|
||||||
|
class="undo-toast"
|
||||||
|
role="status"
|
||||||
|
aria-live="polite"
|
||||||
|
>
|
||||||
|
Dismissed from suggestions.
|
||||||
|
<button class="btn-link" @click="undoCooked">Undo</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted, nextTick, watch } from 'vue'
|
import { ref, computed, onMounted, onUnmounted, nextTick, watch } from 'vue'
|
||||||
import { useRecipesStore } from '../stores/recipes'
|
import { useRecipesStore } from '../stores/recipes'
|
||||||
import { useInventoryStore } from '../stores/inventory'
|
import { useInventoryStore } from '../stores/inventory'
|
||||||
import { useSavedRecipesStore } from '../stores/savedRecipes'
|
import { useSavedRecipesStore } from '../stores/savedRecipes'
|
||||||
|
|
@ -763,11 +774,28 @@ function openRecipe(recipe: RecipeSuggestion) {
|
||||||
selectedRecipe.value = recipe
|
selectedRecipe.value = recipe
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const lastCookedRecipe = ref<{ id: number; title: string } | null>(null)
|
||||||
|
let undoTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
|
|
||||||
function onCooked(recipe: RecipeSuggestion) {
|
function onCooked(recipe: RecipeSuggestion) {
|
||||||
recipesStore.logCook(recipe.id, recipe.title)
|
recipesStore.logCook(recipe.id, recipe.title)
|
||||||
recipesStore.dismiss(recipe.id)
|
recipesStore.dismiss(recipe.id)
|
||||||
|
lastCookedRecipe.value = { id: recipe.id, title: recipe.title }
|
||||||
|
if (undoTimer) clearTimeout(undoTimer)
|
||||||
|
undoTimer = setTimeout(() => {
|
||||||
|
lastCookedRecipe.value = null
|
||||||
|
}, 12000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function undoCooked() {
|
||||||
|
if (!lastCookedRecipe.value) return
|
||||||
|
recipesStore.undismiss(lastCookedRecipe.value.id)
|
||||||
|
if (undoTimer) clearTimeout(undoTimer)
|
||||||
|
lastCookedRecipe.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnmounted(() => { if (undoTimer) clearTimeout(undoTimer) })
|
||||||
|
|
||||||
const levels = [
|
const levels = [
|
||||||
{ value: 1, label: 'Use What I Have', description: 'Finds recipes you can make right now using exactly what\'s in your pantry.' },
|
{ value: 1, label: 'Use What I Have', description: 'Finds recipes you can make right now using exactly what\'s in your pantry.' },
|
||||||
{ value: 2, label: 'Allow Swaps', description: 'Same as above, plus recipes where one or two ingredients can be substituted.' },
|
{ value: 2, label: 'Allow Swaps', description: 'Same as above, plus recipes where one or two ingredients can be substituted.' },
|
||||||
|
|
@ -1490,4 +1518,21 @@ details[open] .collapsible-summary::before {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.undo-toast {
|
||||||
|
position: fixed;
|
||||||
|
bottom: var(--spacing-lg);
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background: var(--color-bg-elevated);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
padding: var(--spacing-sm) var(--spacing-md);
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
font-size: var(--font-size-sm);
|
||||||
|
z-index: 100;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-sm);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -238,6 +238,11 @@ export const useRecipesStore = defineStore('recipes', () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function undismiss(id: number) {
|
||||||
|
dismissedIds.value = new Set([...dismissedIds.value].filter((d) => d !== id))
|
||||||
|
saveDismissed(dismissedIds.value)
|
||||||
|
}
|
||||||
|
|
||||||
function clearDismissed() {
|
function clearDismissed() {
|
||||||
dismissedIds.value = new Set()
|
dismissedIds.value = new Set()
|
||||||
localStorage.removeItem(DISMISSED_KEY)
|
localStorage.removeItem(DISMISSED_KEY)
|
||||||
|
|
@ -318,6 +323,7 @@ export const useRecipesStore = defineStore('recipes', () => {
|
||||||
suggest,
|
suggest,
|
||||||
loadMore,
|
loadMore,
|
||||||
dismiss,
|
dismiss,
|
||||||
|
undismiss,
|
||||||
clearDismissed,
|
clearDismissed,
|
||||||
clearResult,
|
clearResult,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue