This commit is contained in:
2026-03-31 14:03:41 +02:00
parent 399d78e508
commit d567fa3e02
14 changed files with 336 additions and 153 deletions

View File

@@ -7,32 +7,58 @@ import { cn } from "~/lib/utils";
const AnimateTextIn = ({
children,
animation = "type",
position,
position = 0,
tlId = undefined,
speed = 1,
scrollOnly = false,
className
}: {
children: ReactNode,
animation?: "type" | "slide",
position: gsap.Position,
className?:HTMLAttributes<HTMLDivElement>['className']
position?: gsap.Position,
tlId?: string,
scrollOnly?: boolean,
speed?: number,
className?: HTMLAttributes<HTMLDivElement>['className']
}) => {
const el = useRef<HTMLDivElement>(null)
const gsapContext = useGsapContext();
useGSAP(() => {
const rect = el.current?.getBoundingClientRect()
const isInView = rect && rect.top < window.innerHeight
const scroller = gsapContext?.getScroller()
console.log(scroller)
let viewportTop = 0
let viewportBottom = window.innerHeight
if (scroller && scroller instanceof Element) {
const scrollerRect = scroller.getBoundingClientRect()
viewportTop = scrollerRect.top
viewportBottom = scrollerRect.top + scrollerRect.height
}
const isInView = rect && rect.bottom > viewportTop && rect.top < viewportBottom
console.log(isInView)
const chars = new SplitText(el.current, { type: 'chars' })
gsapContext?.addAnimation(gsap.to(el.current, { opacity: 100, duration: 0 }), 0)
gsapContext?.addAnimation(gsap.to(el.current, { opacity: 100, duration: 0 }), 0, tlId)
const fromVars = animation === "slide"
? { opacity: 0, x: -10, duration: 0.2, stagger: { each: 0.08 }, ease: 'bounce.inOut', onComplete: () => chars.revert() }
: { opacity: 0, duration: 0.01, stagger: { each: 0.04 }, ease: 'bounce.inOut', onComplete: () => chars.revert() }
if (isInView) {
gsapContext?.addAnimation(gsap.from(chars.chars, fromVars), position)
? { opacity: 0, x: -10, duration: 0.2 * speed, stagger: { each: 0.08 * speed }, ease: 'bounce.inOut', onComplete: () => chars.revert() }
: { opacity: 0, duration: 0.01 * speed, stagger: { each: 0.04 * speed }, ease: 'bounce.inOut', onComplete: () => chars.revert() }
if (isInView && !scrollOnly) {
gsapContext?.addAnimation(gsap.from(chars.chars, fromVars), position, tlId)
} else {
gsap.from(chars.chars, { ...fromVars, scrollTrigger: { trigger: el.current, start: 'top 85%', scroller: gsapContext?.getScroller() } })
gsap.from(chars.chars,
{
...fromVars,
scrollTrigger: {
trigger: el.current,
start: 'top bottom',
end: 'bottom top',
toggleActions: "play reverse play reverse",
scroller
}
})
}
}, { dependencies: [] })
return (
<div ref={el} className={cn(className,"opacity-0")}>
<div ref={el} className={cn(className, "opacity-0")}>
{children}
</div>
)

View File

@@ -3,17 +3,23 @@ import Link from 'next/link'
import { MessageCircle } from 'lucide-react'
import { Show } from '@clerk/nextjs'
import { Button } from '~/components/ui/button'
import { usePathname } from 'next/navigation'
export default function ChatFAB() {
const pathName = usePathname()
const isChat = pathName.indexOf('\/chat') > -1
return (
<Show when="signed-in">
<div className="fixed bottom-6 right-6 z-50">
<Button asChild size="icon" className="h-14 w-14 rounded-full shadow-lg">
<Link href="/chat">
<MessageCircle className="h-6 w-6" />
</Link>
</Button>
</div>
</Show>
<>
{!isChat &&
<Show when="signed-in">
<div className="fixed bottom-6 right-6 z-50">
<Button asChild size="icon" className="h-14 w-14 rounded-full shadow-lg">
<Link href="/assistant">
<MessageCircle className="h-6 w-6" />
</Link>
</Button>
</div>
</Show>
}
</>
)
}

View File

@@ -24,7 +24,7 @@ export default function TopNav() {
</Button>
<Show when="signed-in">
<Button asChild className="flex h-10 lg:h-full w-full lg:w-20" variant="outline">
<a href="/chat"> Chat </a>
<Link href="/chat"> Chat </Link>
</Button>
</Show>
</div>