mirror of
https://github.com/supabase/supabase.git
synced 2026-05-31 18:03:33 +08:00
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? We used to hide pointer events when someone hovered over this banner. That logic is no longer needed and caused issues on mobile. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Simplified banner component styling logic. Bottom banner styling is now fixed and consistently applied, removing conditional CSS class logic that previously adjusted behavior based on hover interactions. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46338?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
77 lines
2.3 KiB
TypeScript
77 lines
2.3 KiB
TypeScript
import { AnimatePresence, motion } from 'framer-motion'
|
|
import { useState } from 'react'
|
|
|
|
import { useBannerStack } from './BannerStackProvider'
|
|
|
|
export const BannerStack = () => {
|
|
const { banners } = useBannerStack()
|
|
const [isHovered, setIsHovered] = useState(false)
|
|
|
|
const activeBanners = banners.filter((b) => !b.isDismissed)
|
|
|
|
const PEEK_HEIGHT = 4
|
|
const CARD_GAP = 4
|
|
const CARD_HEIGHT = 212
|
|
|
|
if (activeBanners.length === 0) return null
|
|
|
|
return (
|
|
<motion.div
|
|
className="fixed bottom-4 right-4 z-50"
|
|
onMouseEnter={() => setIsHovered(true)}
|
|
onMouseLeave={() => setIsHovered(false)}
|
|
animate={{
|
|
y: isHovered ? -8 : 0,
|
|
}}
|
|
transition={{
|
|
type: 'spring',
|
|
stiffness: 300,
|
|
damping: 25,
|
|
}}
|
|
>
|
|
<div className="relative">
|
|
<AnimatePresence mode="popLayout">
|
|
{activeBanners.map((banner, index) => {
|
|
const isBottomBanner = index === 0
|
|
const reverseIndex = activeBanners.length - 1 - index
|
|
const collapsedY = index * PEEK_HEIGHT
|
|
const expandedY = index * (CARD_HEIGHT + CARD_GAP)
|
|
|
|
return (
|
|
<motion.div
|
|
key={banner.id}
|
|
initial={{ opacity: 0, scale: 0.99, y: 8 }}
|
|
animate={{
|
|
opacity: 1,
|
|
scale: isHovered ? 1 : 1 - index * 0.07,
|
|
x: 0,
|
|
y: isHovered ? -expandedY : -collapsedY,
|
|
}}
|
|
exit={{ opacity: 0, scale: 0.99, y: 8 }}
|
|
transition={{
|
|
type: 'spring',
|
|
stiffness: 300,
|
|
damping: 30,
|
|
delay: 0.25,
|
|
}}
|
|
onMouseEnter={() => setIsHovered(true)}
|
|
onMouseLeave={() => setIsHovered(false)}
|
|
style={{
|
|
position: isBottomBanner ? 'relative' : 'absolute',
|
|
bottom: isBottomBanner ? undefined : 0,
|
|
right: isBottomBanner ? undefined : 0,
|
|
zIndex: 30 + reverseIndex,
|
|
transformOrigin: 'center bottom',
|
|
}}
|
|
className="w-full max-w-72"
|
|
>
|
|
{banner.content}
|
|
</motion.div>
|
|
)
|
|
})}
|
|
</AnimatePresence>
|
|
</div>
|
|
</motion.div>
|
|
)
|
|
}
|