Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 41 additions & 28 deletions frontend/src/components/profile/ProfileHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { ChangeEvent } from "react";
import { useRef, type ChangeEvent } from "react";
import { Pencil } from "lucide-react";

type ProfileHeaderProps = {
username: string;
Expand All @@ -20,6 +21,8 @@ export default function ProfileHeader({
onCoverImageChange,
onProfileImageChange,
}: ProfileHeaderProps) {
const coverInputRef = useRef<HTMLInputElement>(null);
const profileInputRef = useRef<HTMLInputElement>(null);
return (
<>
<div
Expand All @@ -40,15 +43,16 @@ export default function ProfileHeader({
)
)}

<label
htmlFor="cover-image-input"
className="absolute bottom-4 right-4 z-20 cursor-pointer rounded-full bg-white px-4 py-2 text-sm text-gray-700 opacity-0 shadow-sm transition-opacity duration-200 group-hover:opacity-100 hover:bg-gray-100 dark:bg-[#242321] dark:text-gray-200 dark:hover:bg-[#35322e]"
<button
type="button"
onClick={() => coverInputRef.current?.click()}
className="absolute bottom-4 right-4 z-20 cursor-pointer rounded-full bg-white px-4 py-2 text-sm text-gray-700 shadow-sm hover:bg-gray-100 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-900 dark:bg-[#242321] dark:text-gray-200 dark:hover:bg-[#35322e] dark:focus-visible:outline-white"
>
Change
</label>
Change cover image
</button>

<input
id="cover-image-input"
ref={coverInputRef}
type="file"
accept="image/jpeg,image/png,image/webp"
onChange={onCoverImageChange}
Expand All @@ -57,33 +61,42 @@ export default function ProfileHeader({
</div>

<div className="picture-and-username relative z-10 -mt-16 ml-20 flex items-center gap-6">
<label className="profile-pic group relative block h-44 w-44 shrink-0 cursor-pointer overflow-hidden rounded-full border-4 border-white bg-gray-300 dark:border-[#171615]">
<button
type="button"
aria-label="Change profile image"
onClick={() => profileInputRef.current?.click()}
className="profile-pic relative block h-44 w-44 shrink-0 cursor-pointer rounded-full border-4 border-white bg-gray-300 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-900 dark:border-[#171615] dark:focus-visible:outline-white"
>
{isLoadingImages ? (
<div className="h-full w-full animate-pulse rounded-full bg-gray-300" />
<span className="block h-full w-full animate-pulse rounded-full bg-gray-300" />
) : profileImage ? (
<img
src={profileImage}
alt="Profile"
className="h-full w-full rounded-full object-cover"
/>
<>
<img
src={profileImage}
alt=""
className="h-full w-full rounded-full object-cover"
/>
<span
aria-hidden="true"
className="absolute bottom-2 right-2 grid size-9 place-items-center rounded-full border border-[#dedbd8] bg-white text-[#514943] shadow-sm dark:border-[#504b46] dark:bg-[#242321] dark:text-[#e6dfd6]"
>
<Pencil size={16} strokeWidth={1.75} />
</span>
</>
) : (
<span className="flex h-full w-full items-center justify-center text-gray-500">
Choose image
<span className="flex h-full w-full items-center justify-center rounded-full text-sm text-gray-800">
Change profile image
</span>
)}
</button>

{/* Hover overlay */}
<div className="absolute inset-0 flex items-center justify-center rounded-full bg-black/40 opacity-0 transition-opacity duration-200 group-hover:opacity-100">
<span className="text-sm font-medium text-white">Change image</span>
</div>

<input
type="file"
accept="image/jpeg,image/png,image/webp"
onChange={onProfileImageChange}
className="hidden"
/>
</label>
<input
ref={profileInputRef}
type="file"
accept="image/jpeg,image/png,image/webp"
onChange={onProfileImageChange}
className="hidden"
/>

<h1 className="font-bold text-4xl mt-16">{username}</h1>
</div>
Expand Down