Finished animations #12
6 changed files with 78 additions and 64 deletions
26
app/components/link-button.tsx
Normal file
26
app/components/link-button.tsx
Normal file
|
@ -0,0 +1,26 @@
|
|||
'use client';
|
||||
import { motion } from "framer-motion"
|
||||
import { useState } from "react";
|
||||
|
||||
const LinkButton = ({text, href, onPress}: {text: string, href: string, onPress?: () => any}) => {
|
||||
const [buttonHovered, setButtonHovered] = useState( false );
|
||||
const [buttonPressed, setButtonPressed] = useState( false );
|
||||
return (
|
||||
<div className="mt-10 flex items-center justify-center gap-x-6 lg:justify-start">
|
||||
<motion.a
|
||||
href={href}
|
||||
className="rounded-md bg-white px-3.5 py-2.5 text-sm font-semibold text-gray-900 shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
|
||||
style={{ marginBottom: '40px' }}
|
||||
animate={{opacity: buttonHovered ? 0.8 : 1}}
|
||||
onMouseEnter={()=>setButtonHovered(true)}
|
||||
onMouseLeave={()=>setButtonHovered(false)}
|
||||
onClick={()=>{setButtonPressed(true); onPress && onPress();}}
|
||||
>
|
||||
{text}
|
||||
</motion.a>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default LinkButton;
|
|
@ -2,6 +2,7 @@
|
|||
import { Transition } from "@headlessui/react";
|
||||
import { motion } from "framer-motion";
|
||||
import { useState } from "react";
|
||||
import LinkButton from "./link-button";
|
||||
|
||||
const Logo = () => {
|
||||
|
||||
|
@ -26,7 +27,7 @@ const Logo = () => {
|
|||
</motion.a>
|
||||
{/* </Transition> */}
|
||||
<div style={{ marginBottom: '40px', marginRight: '40px' }}></div>
|
||||
<motion.a
|
||||
{/* <motion.a
|
||||
href="/about-us"
|
||||
className="rounded-md bg-white text-sm font-semibold text-gray-900 shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
|
||||
style={{ marginBottom: '40px', marginRight: '40px', padding: '10px' }}
|
||||
|
@ -35,7 +36,8 @@ const Logo = () => {
|
|||
onMouseLeave={()=>setAboutButtonHovered(false)}
|
||||
>
|
||||
About us
|
||||
</motion.a>
|
||||
</motion.a> */}
|
||||
<LinkButton text="About us" href="/about-us"/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { ReactComponentElement, ReactNode } from "react";
|
||||
import { ReactComponentElement, ReactNode, useEffect } from "react";
|
||||
import { AnswerType, Question } from "../lib/question";
|
||||
import { SessionState } from "../lib/session";
|
||||
import { CheckCircleIcon, XCircleIcon } from "@heroicons/react/24/outline";
|
||||
import { Button, Transition } from "@headlessui/react";
|
||||
import wait from "../lib/delay";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
const QuestionView = ({ question, setState, state }: { question: Question, setState: any, state: SessionState }) => {
|
||||
|
||||
|
@ -16,6 +18,10 @@ const QuestionView = ({ question, setState, state }: { question: Question, setSt
|
|||
}))
|
||||
}
|
||||
|
||||
useEffect( () => {
|
||||
wait( 1000 );
|
||||
}, [] );
|
||||
|
||||
const handleAnswerClick = (right: boolean) => {
|
||||
setState( (prevState: SessionState) => ({
|
||||
...prevState,
|
||||
|
@ -47,14 +53,16 @@ const QuestionView = ({ question, setState, state }: { question: Question, setSt
|
|||
{question.text}
|
||||
</h1>
|
||||
</Transition>
|
||||
<Transition
|
||||
{/* <Transition
|
||||
show={(question && state.opened[ question.id ]) ? true : false}
|
||||
enter="transition-opacity duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-90"
|
||||
leave="transition-opacity duration-150"
|
||||
leaveFrom="opacity-90"
|
||||
leaveTo="opacity-0">
|
||||
leaveTo="opacity-0"> */}
|
||||
{/* <motion.div animate={{visibility: ((question && state.opened[ question.id ]) ? "visible" : "collapse") }}> */}
|
||||
<motion.div animate={(question && state.opened[ question.id ]) ? {visibility: "visible", opacity: 1} : {visibility: "collapse", opacity: 0}}>
|
||||
<div><p className="mt-6 text-lg leading-8 text-gray-200">
|
||||
{question.answer}
|
||||
</p>
|
||||
|
@ -91,7 +99,7 @@ const QuestionView = ({ question, setState, state }: { question: Question, setSt
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</motion.div>
|
||||
<Transition
|
||||
show={(question && (state.opened[ question.id ])) ? false : true}
|
||||
enter="transition-opacity duration-300"
|
||||
|
@ -100,7 +108,7 @@ const QuestionView = ({ question, setState, state }: { question: Question, setSt
|
|||
leave="transition-opacity duration-150"
|
||||
leaveFrom="opacity-90"
|
||||
leaveTo="opacity-0">
|
||||
<Button className="text-sm font-semibold leading-6 text-gray-200" onClick={handleOpenClick}>
|
||||
<Button className="leading-6 text-black back rounded-md bg-white text-sm font-semibold text-gray-900" onClick={handleOpenClick} style = {{padding: '5px'}}>
|
||||
Show answer
|
||||
</Button>
|
||||
</Transition>
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
import { PlayIcon } from '@heroicons/react/24/outline'
|
||||
'use client';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useState } from 'react';
|
||||
import LinkButton from './link-button';
|
||||
const TopicCard = ({ title, children, link }: { title: string, children: any, link: string }) => {
|
||||
const [clicked, setClicked] = useState( false );
|
||||
const [hovered, setHovered] = useState( false );
|
||||
return (
|
||||
<div className="mx-auto mt-16 max-w-2xl rounded-3xl sm:mt-20 lg:mx-0 lg:flex lg:max-w-none relative isolate overflow-hidden bg-gray-900 px-6 pt-16 shadow-2xl sm:rounded-3xl sm:px-16 md:pt-24 lg:flex lg:gap-x-20 lg:px-24 lg:pt-0" style={{marginTop: '20px', paddingTop: '0px', paddingBottom: '24px'}}>
|
||||
<motion.div className="mx-auto mt-16 max-w-2xl rounded-3xl sm:mt-20 lg:mx-0 lg:flex lg:max-w-none relative isolate overflow-hidden bg-gray-900 px-6 pt-16 shadow-2xl sm:rounded-3xl sm:px-16 md:pt-24 lg:flex lg:gap-x-20 lg:px-24 lg:pt-0" style={{marginTop: '20px', paddingTop: '0px', paddingBottom: '24px'}}
|
||||
animate={{x: hovered? 10 : 0, opacity: clicked ? 0.5 : 1 }} onMouseEnter={()=>setHovered(true)} onMouseLeave={()=>setHovered(false)}>
|
||||
<svg
|
||||
viewBox="0 0 1024 1024"
|
||||
className="absolute left-1/2 top-1/2 -z-10 h-[64rem] w-[64rem] -translate-y-1/2 [mask-image:radial-gradient(closest-side,white,transparent)] sm:left-full sm:-ml-80 lg:left-1/2 lg:ml-0 lg:-translate-x-1/2 lg:translate-y-0"
|
||||
|
@ -25,16 +31,17 @@ const TopicCard = ({ title, children, link }: { title: string, children: any, li
|
|||
className="-mt-2 p-2 lg:mt-0 lg:max-w-md lg:flex-shrink-0 lg:flex lg:flex-col lg:justify-center"
|
||||
style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}
|
||||
>
|
||||
<a
|
||||
{/* <a
|
||||
href={link}
|
||||
className="rounded-md text-sm font-semibold border text-gray-200 shadow-sm hover:text-gray-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
|
||||
style={{ paddingLeft: '50px', paddingRight: '50px', paddingTop: '20px', paddingBottom: '20px'}}
|
||||
>
|
||||
|
||||
Begin
|
||||
</a>
|
||||
</div>
|
||||
</a> */}
|
||||
<LinkButton text="Begin" href={link} onPress={()=>setClicked(true)}/>
|
||||
</div>
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
5
app/lib/delay.ts
Normal file
5
app/lib/delay.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
const wait = (time: number): Promise<void> => {
|
||||
return new Promise( res => setTimeout( res, time ) );
|
||||
}
|
||||
|
||||
export default wait;
|
68
app/page.tsx
68
app/page.tsx
|
@ -1,12 +1,15 @@
|
|||
|
||||
import { Fragment } from 'react'
|
||||
'use client';
|
||||
import { Fragment, useState } from 'react'
|
||||
import { Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react'
|
||||
import { ChevronDownIcon } from '@heroicons/react/20/solid'
|
||||
import Logo from './components/logo';
|
||||
import { motion } from 'framer-motion';
|
||||
import LinkButton from './components/link-button';
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
export default function Example() {
|
||||
const [buttonPressed, setButtonPressed] = useState( false );
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
|
@ -25,10 +28,13 @@ export default function Example() {
|
|||
<Logo/>
|
||||
<Transition
|
||||
appear={true}
|
||||
show={true}
|
||||
show={!buttonPressed}
|
||||
enter="transition-opacity duration-500"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-80">
|
||||
enterTo="opacity-80"
|
||||
leave="transition-opacity duration-150"
|
||||
leaveFrom="opacity-80"
|
||||
leaveTo="opacity-0">
|
||||
<div className="mx-auto max-w-7xl py-24 sm:px-6 sm:py-32 lg:px-8">
|
||||
<div className="relative isolate overflow-hidden bg-gray-900 px-6 pt-16 shadow-2xl sm:rounded-3xl sm:px-16 md:pt-24 lg:flex lg:gap-x-20 lg:px-24 lg:pt-0">
|
||||
<svg
|
||||
|
@ -54,58 +60,18 @@ export default function Example() {
|
|||
Get questions from a multitude of different subjects that include cybersecurity, geography and C++
|
||||
</p>
|
||||
<div className="mt-10 flex items-center justify-center gap-x-6 lg:justify-start">
|
||||
<a
|
||||
{/* <motion.a
|
||||
href="sessionconfig"
|
||||
className="rounded-md bg-white px-3.5 py-2.5 text-sm font-semibold text-gray-900 shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
|
||||
style={{ marginBottom: '40px' }}
|
||||
animate={{opacity: buttonHovered ? 0.8 : 1}}
|
||||
onMouseEnter={()=>setButtonHovered(true)}
|
||||
onMouseLeave={()=>setButtonHovered(false)}
|
||||
onClick={()=>setButtonPressed(true)}
|
||||
>
|
||||
Get started
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
<Transition
|
||||
appear={true}
|
||||
show={true}
|
||||
enter="transition-opacity duration-500"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-80">
|
||||
<div className="mx-auto max-w-7xl py-24 sm:px-6 sm:py-32 lg:px-8">
|
||||
<div className="relative isolate overflow-hidden bg-gray-900 px-6 pt-16 shadow-2xl sm:rounded-3xl sm:px-16 md:pt-24 lg:flex lg:gap-x-20 lg:px-24 lg:pt-0">
|
||||
<svg
|
||||
viewBox="0 0 1024 1024"
|
||||
className="absolute left-1/2 top-1/2 -z-10 h-[64rem] w-[64rem] -translate-y-1/2 [mask-image:radial-gradient(closest-side,white,transparent)] sm:left-full sm:-ml-80 lg:left-1/2 lg:ml-0 lg:-translate-x-1/2 lg:translate-y-0"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<circle cx={512} cy={512} r={512} fill="url(#759c1415-0410-454c-8f7c-9a820de03641)" fillOpacity="0.7" />
|
||||
<defs>
|
||||
<radialGradient id="759c1415-0410-454c-8f7c-9a820de03641">
|
||||
<stop stopColor="#7775D6" />
|
||||
<stop offset={1} stopColor="#E935C1" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div className="mx-auto max-w-md text-center lg:mx-0 lg:flex-auto lg:py-32 lg:text-left">
|
||||
<h2 className="text-3xl font-bold tracking-tight text-white sm:text-4xl">
|
||||
Start using flash cards
|
||||
<br />
|
||||
Begin with our app today.
|
||||
</h2>
|
||||
<p className="mt-6 text-lg leading-8 text-gray-300">
|
||||
Get questions from a multitude of different subjects that include cybersecurity, geography and C++
|
||||
</p>
|
||||
<div className="mt-10 flex items-center justify-center gap-x-6 lg:justify-start">
|
||||
<a
|
||||
href="sessionconfig"
|
||||
className="rounded-md bg-white px-3.5 py-2.5 text-sm font-semibold text-gray-900 shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
|
||||
style={{ marginBottom: '40px' }}
|
||||
>
|
||||
Get started
|
||||
</a>
|
||||
|
||||
</motion.a> */}
|
||||
<LinkButton text="Get started" href="/sessionconfig" onPress={()=>setButtonPressed(true)}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue