diff --git a/client/src/components/favorites/FavoriteButton.tsx b/client/src/components/favorites/FavoriteButton.tsx index 018b632..2e52459 100644 --- a/client/src/components/favorites/FavoriteButton.tsx +++ b/client/src/components/favorites/FavoriteButton.tsx @@ -4,6 +4,9 @@ import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder'; import { useCurrentUser } from '../../lib/useCurrentUser'; import { LoginRequiredDialog } from '../layout/LoginRequiredDialog'; import { MY_FAVORITES, TOGGLE_FAVORITE } from '../../graphql/favorites'; +import { useState } from 'react'; +import { motion, AnimatePresence } from 'motion/react'; +import { quick } from '../../lib/motion'; type FavoriteButtonProps = { recipeId: string; @@ -24,6 +27,8 @@ export function FavoriteButton({ recipeId, title }: FavoriteButtonProps) { // henter favorittene på nytt så hjertene og favorittsiden blir riktige refetchQueries: [MY_FAVORITES], }); + // teller trykk så ringen spilles av på nytt hver gang + const [burst, setBurst] = useState(0); // ikke innlogget så hjertet åpner innloggingspopupen if (!user) { @@ -47,19 +52,39 @@ export function FavoriteButton({ recipeId, title }: FavoriteButtonProps) { data?.myFavorites.some((recipe) => recipe.id === recipeId) ?? false; return ( - + ); } diff --git a/client/src/components/layout/LoginRequiredDialog.tsx b/client/src/components/layout/LoginRequiredDialog.tsx index e2c74d0..b2a53bc 100644 --- a/client/src/components/layout/LoginRequiredDialog.tsx +++ b/client/src/components/layout/LoginRequiredDialog.tsx @@ -20,9 +20,9 @@ export function LoginRequiredDialog({ {/* knappene som åpner popupen */} {trigger} - + {/* popup */} - + Du må logge inn for å bruke dette diff --git a/client/src/components/profile/DeleteRecipesDialog.tsx b/client/src/components/profile/DeleteRecipesDialog.tsx index 7a9da18..7acf6e9 100644 --- a/client/src/components/profile/DeleteRecipesDialog.tsx +++ b/client/src/components/profile/DeleteRecipesDialog.tsx @@ -55,8 +55,8 @@ export function DeleteRecipesDialog({ - - + + {count === 1 ? 'Vil du slette 1 oppskrift?' diff --git a/client/src/components/profile/EditProfileDialog.tsx b/client/src/components/profile/EditProfileDialog.tsx index cd3988b..405c74f 100644 --- a/client/src/components/profile/EditProfileDialog.tsx +++ b/client/src/components/profile/EditProfileDialog.tsx @@ -1,11 +1,13 @@ import { useState, type ReactNode, type SubmitEvent } from 'react'; import * as RadixDialog from '@radix-ui/react-dialog'; import { useMutation } from '@apollo/client/react'; +import { AnimatePresence, motion } from 'motion/react'; import { Button } from '../ui/Button'; import { Field } from '../ui/Field'; import { Input } from '../ui/Input'; import { useCurrentUser } from '../../lib/useCurrentUser'; import { CHANGE_PASSWORD, UPDATE_PROFILE } from '../../graphql/auth'; +import { fadeUp, quick } from '../../lib/motion'; // passordet må være minst så mange tegn const MIN_PASSWORD_LENGTH = 6; @@ -54,6 +56,11 @@ export function EditProfileDialog({ trigger }: EditProfileDialogProps) { try { await updateProfile({ variables: { name: name.trim() } }); setNameMessage('Navnet er lagret.'); + // skjuler meldingen etter litt men bare hvis den ikke er byttet ut + setTimeout( + () => setNameMessage((m) => (m === 'Navnet er lagret.' ? '' : m)), + 2500, + ); } catch (error) { setNameMessage( error instanceof Error ? error.message : 'Kunne ikke lagre navnet.', @@ -87,6 +94,12 @@ export function EditProfileDialog({ trigger }: EditProfileDialogProps) { setNewPassword(''); setRepeatPassword(''); setPasswordMessage('Passordet er byttet.'); + // skjuler meldingen etter litt men bare hvis den ikke er byttet ut + setTimeout( + () => + setPasswordMessage((m) => (m === 'Passordet er byttet.' ? '' : m)), + 2500, + ); } catch (error) { setPasswordMessage( error instanceof Error ? error.message : 'Kunne ikke bytte passord.', @@ -98,10 +111,10 @@ export function EditProfileDialog({ trigger }: EditProfileDialogProps) { {trigger} - + {/* popup som kan scrolles på små skjermer */} - - + + Rediger profil @@ -119,11 +132,23 @@ export function EditProfileDialog({ trigger }: EditProfileDialogProps) { autoComplete="name" /> - {nameMessage && ( -

- {nameMessage} -

- )} + {/* meldingen tones inn hver gang teksten endres */} + + {nameMessage && ( + + {nameMessage} + + )} + ))} - + {/* preferanser på mobil bare når fanen er valgt */}
setActiveTab(tab)} - className={`-mb-px cursor-pointer border-b-2 pb-2 font-display text-2xl transition-colors ${FOCUS_RING} ${ - desktopTab === tab - ? 'border-primary text-primary' - : 'border-transparent hover:text-primary' + className={`relative cursor-pointer pb-2 font-display text-2xl transition-colors ${FOCUS_RING} ${ + desktopTab === tab ? 'text-primary' : 'hover:text-primary' }`} > {RECIPE_TABS[tab].label} + {/* streken glir til fanen som er valgt */} + {desktopTab === tab && ( + + )} ))}
@@ -256,19 +272,29 @@ export default function ProfilePage() { {/* søppelbøtte som slår på valg av egne oppskrifter */} {desktopTab === 'shared' && myRecipes.length > 0 && (
- {isSelecting && ( - <> - - {selectedIds.length} valgt - - - selectedIds.includes(recipe.id), - )} - onDeleted={toggleSelecting} - /> - - )} + {/* teksten og slett knappen vokser ut fra søppelbøtta */} + + {isSelecting && ( + + + {selectedIds.length} valgt + + + selectedIds.includes(recipe.id), + )} + onDeleted={toggleSelecting} + /> + + )} + - ) : ( -
- -
- )} - - ))} - + // key gjør at listen tones inn på nytt når fanen byttes + + {/* kort som fjernes tones ut og resten glir på plass */} + + {tabRecipes.map((recipe) => ( + + + {isSelecting && ( + // dekker kortet så trykk velger i stedet for å åpne oppskriften + + )} + + ))} + + ) : (

{RECIPE_TABS[desktopTab].empty}