Files
dupe/client/src/components/Topbar.svelte

247 lines
5.2 KiB
Svelte

<script>
import { createEventDispatcher, onMount, onDestroy } from "svelte";
import { navigate } from "svelte-routing";
import {
activePlaceholder,
activeSearchTerm,
updateSearchTerm
} from "../stores/searchStore.js";
import { avatarUrlStore } from "../stores/avatarStore.js";
import { clearTokens } from "../utils/api.js";
const dispatch = createEventDispatcher();
export let placeholder = "";
// Avatar opsiyonel; yoksa placeholder ikon gösterilir
export let avatarUrl = null;
let showAvatarMenu = false;
let avatarWrap;
$: resolvedAvatar = avatarUrl || $avatarUrlStore;
const onToggle = () => dispatch("toggleMenu");
function handleInput(event) {
updateSearchTerm(event.target.value);
}
function toggleAvatarMenu() {
showAvatarMenu = !showAvatarMenu;
}
function goProfile() {
showAvatarMenu = false;
navigate("/profile");
}
function goSettings() {
showAvatarMenu = false;
navigate("/settings");
}
function logout() {
clearTokens();
showAvatarMenu = false;
window.location.replace("/login");
}
function handleDocumentClick(event) {
if (!showAvatarMenu) return;
if (avatarWrap && !avatarWrap.contains(event.target)) {
showAvatarMenu = false;
}
}
onMount(() => {
document.addEventListener("click", handleDocumentClick);
});
onDestroy(() => {
document.removeEventListener("click", handleDocumentClick);
});
$: resolvedPlaceholder = placeholder || $activePlaceholder;
</script>
<div class="topbar">
<!-- 🔹 Hamburger butonu sadece küçük ekranlarda gösterilir -->
<button
class="menu-toggle"
on:click={onToggle}
aria-label="Toggle menu"
>
<i class="fa-solid fa-bars"></i>
</button>
<div class="search">
<i class="fa-solid fa-magnifying-glass"></i>
<input
type="search"
placeholder={resolvedPlaceholder}
value={$activeSearchTerm}
on:input={handleInput}
/>
</div>
<div class="avatar-wrap" bind:this={avatarWrap}>
<button class="avatar" type="button" aria-label="Profil" on:click={toggleAvatarMenu}>
{#if resolvedAvatar}
<img src={resolvedAvatar} alt="Avatar" loading="lazy" on:error={() => (resolvedAvatar = null)} />
{:else}
<div class="placeholder">
<i class="fa-regular fa-user"></i>
</div>
{/if}
</button>
{#if showAvatarMenu}
<div class="avatar-menu">
<button type="button" on:click={goProfile}>
<i class="fa-solid fa-user"></i>
Profile
</button>
<button type="button" on:click={goSettings}>
<i class="fa-solid fa-gear"></i>
Settings
</button>
<button type="button" class="logout" on:click={logout}>
<i class="fa-solid fa-right-from-bracket"></i>
Logout
</button>
</div>
{/if}
</div>
</div>
<style>
.topbar {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
border-bottom: 1px solid var(--border);
}
.search {
flex: 1;
display: flex;
align-items: center;
gap: 8px;
background: #f8f8f8;
border: 1px solid var(--border);
border-radius: 6px;
padding: 8px 12px;
}
.search input {
border: none;
outline: none;
background: transparent;
flex: 1;
}
/* 🟡 Hamburger sadece küçük ekranlarda görünsün */
.menu-toggle {
display: none;
background: none;
border: none;
font-size: 20px;
color: #333;
cursor: pointer;
}
@media (max-width: 768px) {
.menu-toggle {
display: inline-flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
}
}
.avatar {
border: 0.5px solid var(--border, #dcdcdc);
background: #f7f8fa;
border-radius: 10px;
width: 42px;
height: 42px;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
overflow: hidden;
}
.avatar img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 10px;
}
.avatar .placeholder {
width: 100%;
height: 100%;
background: #eef1f6;
border: 0.5px solid var(--border, #dcdcdc);
display: flex;
align-items: center;
justify-content: center;
border-radius: 10px;
color: #2c3e50;
font-size: 18px;
}
@media (max-width: 768px) {
.avatar {
width: 38px;
height: 38px;
}
}
.avatar-wrap {
position: relative;
}
.avatar-menu {
position: absolute;
right: 0;
top: calc(100% + 8px);
background: #fff;
border: 1px solid var(--border);
border-radius: 10px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.12);
display: flex;
flex-direction: column;
min-width: 170px;
overflow: hidden;
z-index: 20;
}
.avatar-menu button {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
background: #fff;
border: none;
text-align: left;
cursor: pointer;
color: #1c2440;
}
.avatar-menu button:hover {
background: #f3f4f7;
}
.avatar-menu i {
width: 18px;
text-align: center;
}
.avatar-menu .logout {
color: #c0392b;
}
</style>