Skip to content

Commit

Permalink
feat: landing page + typewriter + number animate
Browse files Browse the repository at this point in the history
  • Loading branch information
Jordan-Gilliam committed May 28, 2024
1 parent b2653ce commit 87de5d4
Show file tree
Hide file tree
Showing 50 changed files with 1,879 additions and 251 deletions.
44 changes: 44 additions & 0 deletions apps/www/__registry__/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,28 @@ export const Index: Record<string, any> = {
subcategory: "undefined",
chunks: []
},
"typewriter": {
name: "typewriter",
type: "components:ui",
registryDependencies: undefined,
component: React.lazy(() => import("@/registry/default/ui/typewriter")),
source: "",
files: ["registry/default/ui/typewriter.tsx"],
category: "undefined",
subcategory: "undefined",
chunks: []
},
"animated-number": {
name: "animated-number",
type: "components:ui",
registryDependencies: undefined,
component: React.lazy(() => import("@/registry/default/ui/animated-number")),
source: "",
files: ["registry/default/ui/animated-number.tsx"],
category: "undefined",
subcategory: "undefined",
chunks: []
},
"text-animate-demo": {
name: "text-animate-demo",
type: "components:example",
Expand Down Expand Up @@ -313,6 +335,28 @@ export const Index: Record<string, any> = {
subcategory: "undefined",
chunks: []
},
"typewriter-demo": {
name: "typewriter-demo",
type: "components:example",
registryDependencies: ["typewriter"],
component: React.lazy(() => import("@/registry/default/example/typewriter-demo")),
source: "",
files: ["registry/default/example/typewriter-demo.tsx"],
category: "undefined",
subcategory: "undefined",
chunks: []
},
"animated-number-demo": {
name: "animated-number-demo",
type: "components:example",
registryDependencies: ["animated-number"],
component: React.lazy(() => import("@/registry/default/example/animated-number-demo")),
source: "",
files: ["registry/default/example/animated-number-demo.tsx"],
category: "undefined",
subcategory: "undefined",
chunks: []
},
"authentication-01": {
name: "authentication-01",
type: "components:block",
Expand Down
13 changes: 9 additions & 4 deletions apps/www/app/(app)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import Image from "next/image"
import { Suspense } from "react"
import Link from "next/link"

import { siteConfig } from "@/config/site"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
import { FeaturesSection } from "@/components/animate/feature-card"
import { Announcement } from "@/components/announcement"
import { ExamplesNav } from "@/components/examples-nav"
import { Icons } from "@/components/icons"
import {
PageActions,
Expand Down Expand Up @@ -55,12 +55,17 @@ export default function IndexPage() {
</PageActions>
</PageHeader>

<section className="inset-x-0 hidden w-full md:block">
<section className=" w-full block">
<div className="flex flex-col items-center justify-center overflow-hidden rounded-lg ">
<TweetGrid tweets={tweets} />
<Suspense>
<TweetGrid tweets={tweets} />
</Suspense>
</div>
</section>
</div>
<section className=" w-full block bg-neutral-900 mt-12 rounded-tr-[364px]">
<FeaturesSection />
</section>
</BgNoiseWrapper>
{/* <div className="bg-noise" /> */}
</div>
Expand Down
Binary file added apps/www/assets/cult-seo-og.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/www/assets/cults.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/www/assets/feature-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/www/assets/feature-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/www/assets/feature-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/www/assets/feature-4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/www/assets/feature-5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/www/assets/feature-6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/www/assets/texture-card.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/www/assets/threed.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
266 changes: 266 additions & 0 deletions apps/www/components/animate/animated-number.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
"use client"

import { useEffect, useState } from "react"
import { MotionValue, motion, useSpring, useTransform } from "framer-motion"
import { Minus, Plus } from "lucide-react"
import { toast } from "sonner"

import { GradientHeading } from "@/registry/default/ui/gradient-heading"
import TextureCard, {
TextureCardContent,
TextureCardHeader,
TextureCardStyled,
} from "@/registry/default/ui/texture-card"

import { Button } from "../ui/button"
import { Slider } from "../ui/slider"

interface AnimatedNumberProps {
value: number
mass?: number
stiffness?: number
damping?: number
precision?: number
format?: (value: number) => string
onAnimationStart?: () => void
onAnimationComplete?: () => void
}

export function AnimatedNumber({
value,
mass = 0.8,
stiffness = 75,
damping = 15,
precision = 0,
format = (num) => num.toLocaleString(),
onAnimationStart,
onAnimationComplete,
}: AnimatedNumberProps) {
const spring = useSpring(value, { mass, stiffness, damping })
const display: MotionValue<string> = useTransform(spring, (current) =>
format(parseFloat(current.toFixed(precision)))
)

useEffect(() => {
spring.set(value)
if (onAnimationStart) onAnimationStart()
const unsubscribe = spring.onChange(() => {
if (spring.get() === value && onAnimationComplete) onAnimationComplete()
})
return () => unsubscribe()
}, [spring, value, onAnimationStart, onAnimationComplete])

return <motion.span>{display}</motion.span>
}

export function BasicExample() {
const [value, setValue] = useState(1000)

return (
<div>
<AnimatedNumber value={value} />
<Button
size="sm"
variant="ghost"
className="border border-primary/10 rounded-full "
onClick={() => setValue(value + 1000)}
>
<Plus className="h-4 w-4" />
</Button>
</div>
)
}

export function PrecisionExample() {
const [value, setValue] = useState(1234.5678)

return (
<TextureCardStyled>
<TextureCardHeader className="pl-3">
<GradientHeading size="xxs">Precision</GradientHeading>
</TextureCardHeader>
<TextureCardContent>
<div className="flex gap-2">
<div className="text-2xl font-bold">
<AnimatedNumber value={value} precision={2} />
</div>
<Button
size="sm"
variant="ghost"
className="border border-primary/10 rounded-full ml-auto py-5"
onClick={() => setValue(value + 123.456)}
>
<Plus className="h-4 w-4" />
</Button>
</div>
</TextureCardContent>
</TextureCardStyled>
)
}

export function FormatExample() {
const [value, setValue] = useState(1000)

const customFormat = (num: number) => `$${num.toFixed(2)}`

return (
<TextureCardStyled>
<TextureCardHeader className="pl-3">
<GradientHeading size="xxs">Format</GradientHeading>
</TextureCardHeader>
<TextureCardContent>
<div className="flex gap-2">
<div className="text-2xl font-bold">
<AnimatedNumber value={value} format={customFormat} />
</div>
<Button
size="sm"
variant="ghost"
className="border border-primary/10 rounded-full ml-auto py-5"
onClick={() => setValue(value + 250)}
>
<Plus className="h-4 w-4" />
</Button>
</div>
</TextureCardContent>
</TextureCardStyled>
)
}

export function HooksExample() {
const [value, setValue] = useState(1000)

const handleAnimationStart = () => {
toast("🏁 Animation started ")
}

const handleAnimationComplete = () => {
toast("✅ Animation completed ")
}

return (
<TextureCardStyled>
<TextureCardHeader className="pl-3">
<GradientHeading size="xxs">Callbacks</GradientHeading>
</TextureCardHeader>
<TextureCardContent>
<div className="flex gap-2">
<div className="text-2xl font-bold">
<AnimatedNumber
value={value}
onAnimationStart={handleAnimationStart}
onAnimationComplete={handleAnimationComplete}
/>
</div>
<Button
size="sm"
variant="ghost"
className="border border-primary/10 rounded-full ml-auto py-5"
onClick={() => setValue(value + 500)}
>
<Plus className="h-4 w-4" />
</Button>
</div>
</TextureCardContent>
</TextureCardStyled>
)
}

export function CustomSpringExample() {
const [value, setValue] = useState(1000)
const [mass, setMass] = useState(1)
const [stiffness, setStiffness] = useState(100)
const [damping, setDamping] = useState(40)

const handleValueChange =
(setter: (value: number) => void, minValue: number) =>
(values: number[]) => {
const newValue = Math.max(values[0], minValue)
setter(newValue)
}

return (
<TextureCardStyled className="w-full">
<TextureCardHeader className="px-3">
<GradientHeading size="sm">Custom Spring Properties</GradientHeading>
</TextureCardHeader>
<TextureCardContent className="flex flex-col sm:flex-row justify-between items-center gap-8">
<div
className="text-6xl font-bold mr-auto flex"
style={{ minWidth: "150px", textAlign: "right" }}
>
<AnimatedNumber
value={value}
mass={mass}
stiffness={stiffness}
damping={damping}
/>
</div>

<div className="flex flex-col gap-3 px-2">
<Button
className="border border-primary/10 rounded-full py-5"
onClick={() => setValue(value + 500)}
>
<Plus className="h-4 w-4 mr-2" />
Increase
</Button>
<Button
className="border border-primary/10 rounded-full py-5"
disabled={value <= 500}
onClick={() => setValue(value - 300)}
>
<Minus className="h-4 w-4 mr-2" />
Decrease
</Button>
</div>
<div className="ml-auto w-full">
<div className="flex flex-col gap-4">
<div className="ml-auto w-full">
<label>Mass: {mass}</label>
<Slider
defaultValue={[mass]}
max={5}
step={0.1}
onValueChange={handleValueChange(setMass, 0.1)}
/>
</div>
<div className="ml-auto w-full">
<label>Stiffness: {stiffness}</label>
<Slider
defaultValue={[stiffness]}
max={200}
step={1}
onValueChange={handleValueChange(setStiffness, 1)}
/>
</div>
<div className="ml-auto w-full">
<label>Damping: {damping}</label>
<Slider
defaultValue={[damping]}
max={50}
step={1}
onValueChange={handleValueChange(setDamping, 1)}
/>
</div>
</div>
</div>
</TextureCardContent>
</TextureCardStyled>
)
}

export function AnimatedNumberExamples() {
return (
<div className=" max-w-xl gap-4 py-6 mx-auto ">
<div className="w-full flex flex-col justify-between">
<CustomSpringExample />
<div className="flex flex-col sm:flex-row">
<PrecisionExample />
<FormatExample />
<HooksExample />
</div>
</div>
</div>
)
}
Loading

0 comments on commit 87de5d4

Please sign in to comment.