move notification indicator to menu button
shows mail icon with pulse animation when notifications exist
This commit is contained in:
parent
907b4503dd
commit
62dd3f5cd7
2 changed files with 99 additions and 36 deletions
3
src/assets/icons/mail.svg
Normal file
3
src/assets/icons/mail.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="14" height="14" viewBox="0 0 14 14" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 10C12 10.5523 11.5523 11 11 11H3C2.44772 11 2 10.5523 2 10V6.42915C2 6.23669 2.20835 6.11641 2.37502 6.21266L6.54395 8.62012C6.64258 8.67706 6.75256 8.69416 6.85742 8.68066C6.9449 8.6822 7.03392 8.66303 7.11523 8.61621L11.625 6.01241C11.7917 5.91619 12 6.03647 12 6.22892V10ZM11 3C11.5523 3 12 3.44772 12 4V4.35196C12 4.53059 11.9047 4.69565 11.75 4.78496L7.07621 7.48356C6.92149 7.5729 6.73086 7.57289 6.57615 7.48354L2.24995 4.98517C2.09528 4.89584 2 4.7308 2 4.55218V4C2 3.44772 2.44772 3 3 3H11Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 647 B |
|
|
@ -17,7 +17,6 @@
|
|||
import UserSettingsModal from './UserSettingsModal.svelte'
|
||||
import InvitationsModal from './crew/InvitationsModal.svelte'
|
||||
import { authStore } from '$lib/stores/auth.store'
|
||||
import { crewStore } from '$lib/stores/crew.store.svelte'
|
||||
|
||||
// Props from layout data
|
||||
const {
|
||||
|
|
@ -145,6 +144,15 @@
|
|||
// Database back button hover state
|
||||
let databaseBackHovered = $state(false)
|
||||
|
||||
// Query for the user's crew (to determine if phantom claims should be fetched)
|
||||
const myCrewQuery = createQuery(() => ({
|
||||
...crewQueries.myCrew(),
|
||||
enabled: isAuth
|
||||
}))
|
||||
|
||||
// Derived: whether the user is in a crew (from query, not store)
|
||||
const isInCrew = $derived(myCrewQuery.data != null)
|
||||
|
||||
// Query for pending invitations (only when authenticated)
|
||||
const pendingInvitationsQuery = createQuery(() => ({
|
||||
...crewQueries.pendingInvitations(),
|
||||
|
|
@ -154,7 +162,7 @@
|
|||
// Query for pending phantom claims (only when authenticated and in a crew)
|
||||
const pendingPhantomClaimsQuery = createQuery(() => ({
|
||||
...crewQueries.pendingPhantomClaims(),
|
||||
enabled: isAuth && crewStore.isInCrew
|
||||
enabled: isAuth && isInCrew
|
||||
}))
|
||||
|
||||
// Derived counts
|
||||
|
|
@ -264,23 +272,16 @@
|
|||
aria-label="Your account"
|
||||
class="profile-link"
|
||||
>
|
||||
<span class="avatar-container">
|
||||
{#if avatarSrc}
|
||||
<img
|
||||
src={avatarSrc}
|
||||
srcset={avatarSrcSet}
|
||||
alt={username}
|
||||
class="user-avatar"
|
||||
width="24"
|
||||
height="24"
|
||||
/>
|
||||
{/if}
|
||||
{#if totalNotificationCount > 0}
|
||||
<span class="avatar-badge">
|
||||
<NotificationBadge count={totalNotificationCount} />
|
||||
</span>
|
||||
{/if}
|
||||
</span>
|
||||
{#if avatarSrc}
|
||||
<img
|
||||
src={avatarSrc}
|
||||
srcset={avatarSrcSet}
|
||||
alt={username}
|
||||
class="user-avatar"
|
||||
width="24"
|
||||
height="24"
|
||||
/>
|
||||
{/if}
|
||||
<span>{username}</span>
|
||||
</a>
|
||||
</li>
|
||||
|
|
@ -298,8 +299,14 @@
|
|||
|
||||
<li>
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger class="nav-more-trigger">
|
||||
<Icon name="ellipsis" size={14} />
|
||||
<DropdownMenu.Trigger
|
||||
class="nav-more-trigger {totalNotificationCount > 0 ? `has-notification ${userElement ?? ''}` : ''}"
|
||||
>
|
||||
{#if totalNotificationCount > 0}
|
||||
<Icon name="mail" size={18} />
|
||||
{:else}
|
||||
<Icon name="ellipsis" size={14} />
|
||||
{/if}
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Portal>
|
||||
|
|
@ -317,11 +324,13 @@
|
|||
</DropdownItem>
|
||||
<DropdownMenu.Separator class="dropdown-separator" />
|
||||
{/if}
|
||||
{#if isAuth && totalNotificationCount > 0}
|
||||
{#if isAuth}
|
||||
<DropdownItem>
|
||||
<button class="dropdown-button-with-badge" onclick={() => (invitationsModalOpen = true)}>
|
||||
<span>Notifications</span>
|
||||
<NotificationBadge count={totalNotificationCount} showCount />
|
||||
{#if totalNotificationCount > 0}
|
||||
<NotificationBadge count={totalNotificationCount} showCount element={userElement} />
|
||||
{/if}
|
||||
</button>
|
||||
</DropdownItem>
|
||||
{/if}
|
||||
|
|
@ -556,25 +565,12 @@
|
|||
align-items: center;
|
||||
gap: spacing.$unit-half;
|
||||
|
||||
.avatar-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.avatar-badge {
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
right: -4px;
|
||||
}
|
||||
}
|
||||
|
||||
// Dropdown button with badge (for Invitations)
|
||||
|
|
@ -630,6 +626,70 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Notification pulse animation for the more trigger
|
||||
@keyframes notification-pulse {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.nav-more-trigger.has-notification) {
|
||||
animation: notification-pulse 2s ease-in-out infinite;
|
||||
// Default pulse color (no element selected)
|
||||
background-color: var(--button-primary-bg);
|
||||
color: white;
|
||||
// Compensate for larger mail icon (18px vs 14px ellipsis)
|
||||
padding: spacing.$unit calc(spacing.$unit + 3px);
|
||||
|
||||
&:hover {
|
||||
animation: none;
|
||||
background-color: var(--button-primary-bg-hover);
|
||||
}
|
||||
}
|
||||
|
||||
// Element-specific notification colors
|
||||
:global(.nav-more-trigger.has-notification.wind) {
|
||||
background-color: var(--wind-button-bg);
|
||||
&:hover {
|
||||
background-color: var(--wind-button-bg-hover);
|
||||
}
|
||||
}
|
||||
:global(.nav-more-trigger.has-notification.fire) {
|
||||
background-color: var(--fire-button-bg);
|
||||
&:hover {
|
||||
background-color: var(--fire-button-bg-hover);
|
||||
}
|
||||
}
|
||||
:global(.nav-more-trigger.has-notification.water) {
|
||||
background-color: var(--water-button-bg);
|
||||
&:hover {
|
||||
background-color: var(--water-button-bg-hover);
|
||||
}
|
||||
}
|
||||
:global(.nav-more-trigger.has-notification.earth) {
|
||||
background-color: var(--earth-button-bg);
|
||||
&:hover {
|
||||
background-color: var(--earth-button-bg-hover);
|
||||
}
|
||||
}
|
||||
:global(.nav-more-trigger.has-notification.light) {
|
||||
background-color: var(--light-button-bg);
|
||||
color: black;
|
||||
&:hover {
|
||||
background-color: var(--light-button-bg-hover);
|
||||
}
|
||||
}
|
||||
:global(.nav-more-trigger.has-notification.dark) {
|
||||
background-color: var(--dark-button-bg);
|
||||
&:hover {
|
||||
background-color: var(--dark-button-bg-hover);
|
||||
}
|
||||
}
|
||||
|
||||
// Style the new team button as a prominent circular button
|
||||
// Remove redundant styles that the Button component already handles
|
||||
:global(.new-team-button) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue