feat: home page hero enter button
This commit is contained in:
parent
b245e6d8c1
commit
0e9891b5a2
112
components/motion-primitives/magnetic.tsx
Normal file
112
components/motion-primitives/magnetic.tsx
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
|
import {
|
||||||
|
motion,
|
||||||
|
useMotionValue,
|
||||||
|
useSpring,
|
||||||
|
type SpringOptions,
|
||||||
|
} from 'motion/react';
|
||||||
|
|
||||||
|
const SPRING_CONFIG = { stiffness: 26.7, damping: 4.1, mass: 0.2 };
|
||||||
|
|
||||||
|
export type MagneticProps = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
intensity?: number;
|
||||||
|
range?: number;
|
||||||
|
actionArea?: 'self' | 'parent' | 'global';
|
||||||
|
springOptions?: SpringOptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Magnetic({
|
||||||
|
children,
|
||||||
|
intensity = 0.6,
|
||||||
|
range = 100,
|
||||||
|
actionArea = 'self',
|
||||||
|
springOptions = SPRING_CONFIG,
|
||||||
|
}: MagneticProps) {
|
||||||
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const x = useMotionValue(0);
|
||||||
|
const y = useMotionValue(0);
|
||||||
|
|
||||||
|
const springX = useSpring(x, springOptions);
|
||||||
|
const springY = useSpring(y, springOptions);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const calculateDistance = (e: MouseEvent) => {
|
||||||
|
if (ref.current) {
|
||||||
|
const rect = ref.current.getBoundingClientRect();
|
||||||
|
const centerX = rect.left + rect.width / 2;
|
||||||
|
const centerY = rect.top + rect.height / 2;
|
||||||
|
const distanceX = e.clientX - centerX;
|
||||||
|
const distanceY = e.clientY - centerY;
|
||||||
|
|
||||||
|
const absoluteDistance = Math.sqrt(distanceX ** 2 + distanceY ** 2);
|
||||||
|
|
||||||
|
if (isHovered && absoluteDistance <= range) {
|
||||||
|
const scale = 1 - absoluteDistance / range;
|
||||||
|
x.set(distanceX * intensity * scale);
|
||||||
|
y.set(distanceY * intensity * scale);
|
||||||
|
} else {
|
||||||
|
x.set(0);
|
||||||
|
y.set(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', calculateDistance);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('mousemove', calculateDistance);
|
||||||
|
};
|
||||||
|
}, [ref, isHovered, intensity, range]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (actionArea === 'parent' && ref.current?.parentElement) {
|
||||||
|
const parent = ref.current.parentElement;
|
||||||
|
|
||||||
|
const handleParentEnter = () => setIsHovered(true);
|
||||||
|
const handleParentLeave = () => setIsHovered(false);
|
||||||
|
|
||||||
|
parent.addEventListener('mouseenter', handleParentEnter);
|
||||||
|
parent.addEventListener('mouseleave', handleParentLeave);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
parent.removeEventListener('mouseenter', handleParentEnter);
|
||||||
|
parent.removeEventListener('mouseleave', handleParentLeave);
|
||||||
|
};
|
||||||
|
} else if (actionArea === 'global') {
|
||||||
|
setIsHovered(true);
|
||||||
|
}
|
||||||
|
}, [actionArea]);
|
||||||
|
|
||||||
|
const handleMouseEnter = () => {
|
||||||
|
if (actionArea === 'self') {
|
||||||
|
setIsHovered(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseLeave = () => {
|
||||||
|
if (actionArea === 'self') {
|
||||||
|
setIsHovered(false);
|
||||||
|
x.set(0);
|
||||||
|
y.set(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
ref={ref}
|
||||||
|
onMouseEnter={actionArea === 'self' ? handleMouseEnter : undefined}
|
||||||
|
onMouseLeave={actionArea === 'self' ? handleMouseLeave : undefined}
|
||||||
|
style={{
|
||||||
|
x: springX,
|
||||||
|
y: springY,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
1
public/images/down.svg
Normal file
1
public/images/down.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 4.9 KiB |
@ -10,6 +10,10 @@ import { TextShimmer } from '@/components/ui/text-shimmer'
|
|||||||
import { LoginForm } from '@/components/login-form'
|
import { LoginForm } from '@/components/login-form'
|
||||||
import SearchBooks from '@/components/Search/SearchBooks'
|
import SearchBooks from '@/components/Search/SearchBooks'
|
||||||
import Manage from '@/components/Manage/Manage'
|
import Manage from '@/components/Manage/Manage'
|
||||||
|
import { Magnetic } from 'components/motion-primitives/magnetic'
|
||||||
|
import { Button } from '@/components/ui/button'
|
||||||
|
import Image from 'next/image'
|
||||||
|
import HomeHero from '@/components/HomeHero'
|
||||||
|
|
||||||
export default async function HomePage() {
|
export default async function HomePage() {
|
||||||
const headers = await getHeaders()
|
const headers = await getHeaders()
|
||||||
@ -89,68 +93,10 @@ export default async function HomePage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="home">
|
<div className="home">
|
||||||
<div className="relative isolate overflow-hidden py-24 sm:py-32 rounded-md">
|
<HomeHero user={user} />
|
||||||
<img
|
|
||||||
alt=""
|
|
||||||
src="/api/media/file/geniza1.jpg"
|
|
||||||
className="absolute inset-0 -z-10 size-full object-cover"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div
|
|
||||||
aria-hidden="true"
|
|
||||||
className="hidden sm:absolute sm:-top-10 sm:right-1/2 sm:-z-10 sm:mr-10 sm:block sm:transform-gpu sm:blur-3xl"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
clipPath:
|
|
||||||
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
|
|
||||||
}}
|
|
||||||
className="aspect-1097/845 w-[78.5625rem] bg-linear-to-tr from-accent-background to-background opacity-100"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
aria-hidden="true"
|
|
||||||
className="absolute -top-52 left-1/2 -z-10 -translate-x-1/2 transform-gpu blur-3xl sm:top-[-28rem] sm:ml-16 sm:translate-x-0 sm:transform-gpu"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
clipPath:
|
|
||||||
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
|
|
||||||
}}
|
|
||||||
className="aspect-1097/845 w-[78.5625rem] bg-linear-to-tr from-background bg-background"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mx-auto max-w-7xl px-6 lg:px-8 ">
|
|
||||||
<div className="mx-auto max-w-2xl lg:mx-0">
|
|
||||||
<p className="text-base/7 font-semibold text-foreground">Temple Beth-El Beit Midrash</p>
|
|
||||||
|
|
||||||
<h2 className="mt-2 text-5xl font-semibold tracking-tight text-foreground sm:text-7xl text-shadow-lg">
|
|
||||||
<TextShimmer
|
|
||||||
duration={2.2}
|
|
||||||
className="[--base-color:var(--color-emerald-700)] [--base-gradient-color:var(--color-white)] dark:[--base-color:var(--color-emerald-600)] dark:[--base-gradient-color:var(--color-white)]"
|
|
||||||
>
|
|
||||||
Welcome
|
|
||||||
</TextShimmer>
|
|
||||||
<span className="text-shadow-lg text-shadow-background">
|
|
||||||
{user ? <small>{user.firstName}</small> : <small>In</small>}
|
|
||||||
</span>
|
|
||||||
</h2>
|
|
||||||
<div className="mt-8 text-lg font-normal text-pretty text-foreground text-shadow-lg text-shadow-background sm:text-xl/8">
|
|
||||||
<p className="indent-5 italic">
|
|
||||||
Never refuse to lend books to anyone who cannot afford to purchase them, but lend
|
|
||||||
books only to those who can be trusted to return them.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<small className="block text-right">- ibn Tibbon</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{user ? (
|
{user ? (
|
||||||
<Tabs defaultValue="feed" className="p-4">
|
<Tabs id="tabs" defaultValue="feed" className="p-4">
|
||||||
<TabsList className="grid w-full grid-cols-3">
|
<TabsList className="grid w-full grid-cols-3">
|
||||||
<TabsTrigger value="feed">Your Feed</TabsTrigger>
|
<TabsTrigger value="feed">Your Feed</TabsTrigger>
|
||||||
<TabsTrigger value="search">Search</TabsTrigger>
|
<TabsTrigger value="search">Search</TabsTrigger>
|
||||||
|
101
src/components/HomeHero.tsx
Normal file
101
src/components/HomeHero.tsx
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { Magnetic } from 'components/motion-primitives/magnetic'
|
||||||
|
import { Button } from './ui/button'
|
||||||
|
import Image from 'next/image'
|
||||||
|
import { TextShimmer } from './ui/text-shimmer'
|
||||||
|
import { User } from '@/payload-types'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
user: User | null
|
||||||
|
}
|
||||||
|
const HomeHero = (props: Props) => {
|
||||||
|
const { user } = props
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="relative isolate overflow-hidden py-24 sm:py-32 rounded-md">
|
||||||
|
<img
|
||||||
|
alt=""
|
||||||
|
src="/api/media/file/geniza1.jpg"
|
||||||
|
className="absolute inset-0 -z-10 size-full object-cover"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
aria-hidden="true"
|
||||||
|
className="hidden sm:absolute sm:-top-10 sm:right-1/2 sm:-z-10 sm:mr-10 sm:block sm:transform-gpu sm:blur-3xl"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
clipPath:
|
||||||
|
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
|
||||||
|
}}
|
||||||
|
className="aspect-1097/845 w-[78.5625rem] bg-linear-to-tr from-accent-background to-background opacity-100"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
aria-hidden="true"
|
||||||
|
className="absolute -top-52 left-1/2 -z-10 -translate-x-1/2 transform-gpu blur-3xl sm:top-[-28rem] sm:ml-16 sm:translate-x-0 sm:transform-gpu"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
clipPath:
|
||||||
|
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
|
||||||
|
}}
|
||||||
|
className="aspect-1097/845 w-[78.5625rem] bg-linear-to-tr from-background bg-background"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mx-auto max-w-7xl px-6 lg:px-8 ">
|
||||||
|
<div className="mx-auto max-w-2xl lg:mx-0">
|
||||||
|
<p className="text-base/7 font-semibold text-foreground">Temple Beth-El Beit Midrash</p>
|
||||||
|
|
||||||
|
<h2 className="mt-2 text-5xl font-semibold tracking-tight text-foreground sm:text-7xl text-shadow-lg">
|
||||||
|
<TextShimmer
|
||||||
|
duration={2.2}
|
||||||
|
className="[--base-color:var(--color-emerald-700)] [--base-gradient-color:var(--color-white)] dark:[--base-color:var(--color-emerald-600)] dark:[--base-gradient-color:var(--color-white)]"
|
||||||
|
>
|
||||||
|
Welcome
|
||||||
|
</TextShimmer>
|
||||||
|
<span className="text-shadow-lg text-shadow-background">
|
||||||
|
{user ? <small>{user.firstName}</small> : <small>In</small>}
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
<div className="mt-8 text-lg font-normal text-pretty text-foreground text-shadow-lg text-shadow-background sm:text-xl/8">
|
||||||
|
<p className="indent-5 italic">
|
||||||
|
Never refuse to lend books to anyone who cannot afford to purchase them, but lend
|
||||||
|
books only to those who can be trusted to return them.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<small className="block text-right">- ibn Tibbon</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-center h-32">
|
||||||
|
<Magnetic intensity={0.2} springOptions={{ bounce: 0.1 }} actionArea="global" range={200}>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
className="animate-pulse mx-auto block items-center rounded-lg border border-zinc-100/10 bg-transparent text-foreground px-8 py-2 text-sm transition-all duration-200 h-fit"
|
||||||
|
onClick={() => {
|
||||||
|
const tabs = document.getElementById('tabs')
|
||||||
|
tabs?.scrollIntoView({ behavior: 'smooth' })
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src="/images/down.svg"
|
||||||
|
alt="Enter site"
|
||||||
|
width={40}
|
||||||
|
height={40}
|
||||||
|
className="block mx-auto dark:invert"
|
||||||
|
/>
|
||||||
|
<span className="block">Enter</span>
|
||||||
|
</Button>
|
||||||
|
</Magnetic>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HomeHero
|
Loading…
x
Reference in New Issue
Block a user