How to update card name when clicking on card in React JS - reactjs

I am building this app in React Js where i can search for a specific card and once i click on that card the card info section gets updated with the card name, description and image. I have 1 parent component called Decks that contains 2 child components called Search and within Search i have another component called CardsFiltered. The issue i am facing is passing data from child component to its parent component up the tree. How can i tackle this issue? Any idea's will be appreciated.
Decks.js
import React, { useEffect, useState } from 'react'
import { DragDropContext } from 'react-beautiful-dnd'
import CardsSearch from './CardsSearch'
import Search from './Search'
function Decks(props) {
const image = require('../images/blue-eyes-white-dragon.png')
const [name, setName] = useState()
useEffect(() => {
window.addEventListener('storage', (e) => {
const localName = localStorage.getItem('items')
console.log(localName)
this.setName(localName)
})
}, [])
return (
<div className='h-screen w-full flex flex-row'>
<div className='flex flex-row h-full w-full'>
<div className='flex flex-col h-full w-[55rem] gap-6 p-4'>
<div className='flex border-solid border-2 border-black p-2'>
<h1 onChange={(e) => setName(e.target.value)}>{name}</h1>
</div>
<div className='flex justify-center w-[16rem]'>
<img src={image} loading="lazy" />
</div>
<div className='flex flex-row border-black border-solid border-2 p-2'>
<p>Card Desc</p>
</div>
</div>
<div className='flex flex-col w-[100rem] gap-3 p-4'>
<div className='flex flex-row justify-between border-black border-solid border-2 p-2'>
<div>
<p>Main [40]</p>
</div>
<div className='flex flex-row gap-3'>
<p>NM[6]</p>
<p>EM[17]</p>
<p>SP[13]</p>
<p>TR[4]</p>
</div>
</div>
<div className='flex flex-row flex-wrap border-black border-2 border-solid'>
<DragDropContext>
<img src={image} alt="Card" className='w-20' />
<img src={image} alt="Card" className='w-20' />
<img src={image} alt="Card" className='w-20' />
<img src={image} alt="Card" className='w-20' />
<img src={image} alt="Card" className='w-20' />
</DragDropContext>
</div>
<div className='flex flex-row justify-between border-black border-solid border-2 p-2'>
<div>
<p>Extra [11]</p>
</div>
<div className='flex flex-row gap-3'>
<p>FM[6]</p>
<p>SM[6]</p>
<p>XYZ[2]</p>
<p>RM[1]</p>
</div>
</div>
<div className='flex flex-row flex-wrap border-black border-2 border-solid'>
<img src={image} alt="Card" className='w-20' />
<img src={image} alt="Card" className='w-20' />
<img src={image} alt="Card" className='w-20' />
<img src={image} alt="Card" className='w-20' />
<img src={image} alt="Card" className='w-20' />
</div>
<div className='flex flex-row justify-between border-black border-solid border-2 p-2'>
<div>
<p>Side [13]</p>
</div>
<div className='flex flex-row gap-3'>
<p>NM[6]</p>
<p>EM[17]</p>
<p>SP[13]</p>
<p>TR[4]</p>
</div>
</div>
<div className='flex flex-row flex-wrap border-black border-2 border-solid'>
<img src={image} alt="Card" className='w-20' />
<img src={image} alt="Card" className='w-20' />
<img src={image} alt="Card" className='w-20' />
</div>
</div>
<div className='flex flex-col w-[45rem] overflow-y-auto'>
<div className='flex flex-col'>
<Search getName={name => setName(name)} />
</div>
</div>
</div>
</div>
)
}
export default Decks
Search.js
import React, { useState, Suspense } from 'react'
import { useRef } from 'react'
//import CardsFiltered from './CardsFiltered'
const CardsFiltered = React.lazy(() => import('./CardsFiltered'))
function Search(props) {
const [inputText, setInputText] = useState("")
let inputHandler = (e) => {
var lowerCase = e.target.value.toLowerCase()
setInputText(lowerCase)
}
const clickPoint = useRef();
const handleFocus = () => {
clickPoint.current.style.display = "none";
};
const handleBlur = () => {
clickPoint.current.style.display = "block";
};
const [cardName, setCardName] = useState()
return (
<>
<div className="items-center py-6 px-4 flex justify-center" >
<div className="relative mr-3">
<div className="absolute top-3 left-3 items-center" ref={clickPoint}>
<svg className="w-5 h-5 text-gray-500" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fillRule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" clipRule="evenodd"></path></svg>
</div>
<input
type="text"
className="block p-2 pl-10 w-70 text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:pl-3"
placeholder="Search Here..."
onFocus={handleFocus}
onBlur={handleBlur}
onChange={inputHandler}
/>
</div>
</div>
<div>
<Suspense fallback={<div>Loading...</div>}>
<CardsFiltered input={inputText} onMouseOver={() => props.getName(cardName)} getCardName={cardName => setCardName(cardName)}/>
</Suspense>
</div>
</>
)
}
export default Search
CardsFiltered.js
import React, { useEffect, useState } from 'react'
import Cards from '../../JsonData/FormattedData.json'
import { AiFillStar } from 'react-icons/ai';
function CardsFiltered(props) {
const filteredData = Cards.data.filter((e) => {
if (props.input === '') {
return e
}
else {
//console.log(e)
return e.name.toLowerCase().includes(props.input)
}
})
let lastIndex = 50
const [name, setName] = useState()
useEffect(() => {
localStorage.setItem('items', JSON.stringify(name))
//console.log(name)
}, [name])
return (
<>
<div className='flex justify-center align-middle px-4 overflow-y-auto overflow-x-hidden font-normal'>
<div className='box-border h-auto w-[35rem] border-2 border-slate-600 flex flex-col justify-center align-middle'>
{filteredData.splice(0, lastIndex).map((card) => {
return (
<div key={card.id} onMouseOver={() => setName(card.name)} className='flex h-full w-full justify-center align-middle box-border border-2 gap-3 overflow-auto myCard'>
<div className='flex h-40 w-56 justify-center align-middle'>
<img src={card.card_images[0].image_url_small} className='w-auto h-auto' alt="Yugioh Card Image" loading='lazy'
/>
</div>
<div className='flex flex-col justify-center align-middle w-full'>
<h4><b>{card.name}</b></h4>
<p>{card.attribute}/{card.race}</p>
<span><AiFillStar/> {card.level}</span>
<p>{card.atk}/{card.def}</p>
</div>
</div>
)
})}
</div>
</div>
</>
)
}
export default CardsFiltered
I understand this can be done with the state hook and/or with props but so far no luck using props.

Related

reactjs remember me functionality on localstorage

I'm having a problem here when creating a remember me function in Reactjs.
So, I managed to enter the username, password and isChecked into localstorage.
However, when I logged out, all the data in the local storage was deleted including the username, password and isChecked.
How do you make the username, password and isChecked persist after logging out? Thank You
MyCode =
import React, { useState } from "react";
import Logo from "../../../../assets/images/logo.png";
import BackgroundProfile from "../../../../assets/images/background-profile.png";
import { VscKey } from "react-icons/vsc";
import { Link } from "react-router-dom";
import { signin } from "../../../../service/account";
import { toast } from "react-toastify";
export default function CardLogin(props) {
const {
labelText,
inputText,
loginType,
firstOptionLogin,
secondOptionLogin,
labelIcon,
firstRoute,
secondRoute,
firstIcon,
secondIcon,
} = props;
const [username, setUsername] = useState(() =>
localStorage.checkbox ? localStorage.username : ""
);
const [password, setPassword] = useState(() =>
localStorage.checkbox ? localStorage.password : ""
);
const [isChecked, setIsChecked] = useState(() => !!localStorage.checkbox);
const initialValue = {
username: username,
password: password,
nik: "",
};
const [isLoading, setIsLoading] = useState(false);
const handleSubmit = () => {
if (isChecked && username !== "") {
localStorage.username = username;
localStorage.password = password;
localStorage.checkbox = isChecked ? "1" : "";
}
const data = new URLSearchParams();
console.log(loginType);
if (loginType === "username") {
data.append("username", initialValue.username);
data.append("password", initialValue.password);
data.append("grant_type", "password");
}
setIsLoading(true);
signin(data, loginType)
.then((response) => {
if (response?.code === 200) {
console.log(response);
localStorage.setItem(
"accessToken",
JSON.stringify(response.data.accessToken)
);
localStorage.setItem(
"userSession",
JSON.stringify(response.data.accessTokenExpiresAt)
);
window.location.reload();
} else {
toast.error(response.message);
}
})
.catch((error) => {
console.log(error);
})
.finally(() => {
setIsLoading(false);
});
};
return (
<div
style={{
backgroundSize: "100% 75%",
backgroundRepeat: "no-repeat",
height: "100%",
backgroundImage: `url(${BackgroundProfile})`,
}}
>
{/* Button Back on The Top of Page */}
<button>
<div className="rounded-full w-5 md:w-10 h-5 md:h-10 p-3 relative top-2 left-2">
{/* <FaArrowLeft className="text-[#DD2729] text-xs md:text-base absolute top-1/2 left-1/2 -translate-y-1/2 -translate-x-1/2" /> */}
</div>
</button>
{/* Logo & Card Login */}
<img
src={Logo}
className="absolute top-6 bottom-6 right-4 w-2/5 md:w-1/5"
alt="Logo"
/>
<div className="mx-auto mt-8 w-3/5 md:w-2/5 bg-white px-5 py-3 md:px-8 md:py-5 rounded-md drop-shadow-xl">
<button>
{/* <FaArrowLeft className="text-[#DD2729] text-xs md:text-base" /> */}
</button>
<article
style={{ fontFamily: "ubuntu" }}
className="text-center text-[#808285] text-xs md:text-lg"
>
Selamat Datang! <br /> Silakan masuk untuk mulai menggunakan aplikasi
</article>
{/* Form Login*/}
<form onSubmit={handleSubmit} className="pt-3 md:pt-5">
<div>
<label
htmlFor={loginType}
className="text-[#424242] text-xs md:text-sm "
>
{labelText}
</label>
<div className="flex">
<div
className={`w-10 flex items-center justify-center bg-blue-lighter border-y border-l border-[#9E9E9E] rounded-l text-blue-dark`}
>
{labelIcon}
</div>
<input
id="username"
name="username"
type="text"
placeholder={inputText}
value={username}
onChange={(e) => setUsername(e.target.value)}
className={`w-full border-l-0 border-[#9E9E9E] rounded-r text-xs md:text-lg font-sans`}
/>
</div>
</div>
<div className="pt-2 md:pt-3">
<label
htmlFor="password"
className="text-[#424242] text-xs md:text-sm"
>
Password
</label>
<div className="flex">
<div
className={`w-10 flex items-center justify-center bg-blue-lighter border-y border-l border-[#9E9E9E] rounded-l text-blue-dark `}
>
<VscKey className="text-[#A8A8A8] text-xl" />
</div>
<input
id="password"
name="password"
type="password"
placeholder="Masukkan kata sandi anda"
value={password}
onChange={(e) => setPassword(e.target.value)}
className={`w-full border-l-0 border-[#9E9E9E] rounded-r text-xs md:text-lg font-normal `}
/>
</div>
</div>
{/* End of Form Login */}
{/* Remember me & Forgot Password */}
<div className="flex pt-3 items-center justify-between">
<div className="flex items-center gap-1.5">
<input
type="checkbox"
checked={isChecked}
name="lsRememberMe"
onChange={(e) => setIsChecked(e.target.checked)}
className="rounded"
/>
<label htmlFor="" className="text-xs md:text-sm">
Remember me
</label>
</div>
<div className="">
<Link
to="/forgot-password"
className="text-xs md:text-sm underline underline-offset-1 font-bold"
>
Forgot password
</Link>
</div>
</div>
<input
type="submit"
value={isLoading ? "Loading" : "Masuk"}
disabled={isLoading}
className="bg-[#EA001E] hover:bg-[#F55151] active:bg-[#BA0D0D] w-full text-white font-bold text-md rounded-lg p-2 mt-4 shadow-xl cursor-pointer text-xs md:text-sm"
/>
</form>
{/* End of Remember me & Forgot Password */}
{/* Login Option */}
<div className="mt-3">
<h4 className="login-selection text-center text-xs md:text-sm">
or login with
</h4>
</div>
<div className="flex items-center justify-center gap-2 mt-4">
<Link to={firstRoute}>
<div className="flex items-center justify-around gap-2 bg-white shadow-xl rounded-full py-2 px-3 md:px-8">
<img
src={firstIcon}
alt="Login Icons"
className="w-2/6 md:w-4/5"
/>
<div className="font-bold text-xs md:text-sm uppercase">
{firstOptionLogin}
</div>
</div>
</Link>
<Link to={secondRoute}>
<div className="flex items-center justify-around gap-2 bg-white shadow-xl rounded-full py-2 px-3 md:px-10">
<img
src={secondIcon}
alt="Login Icons"
className="w-2/6 md:w-4/5"
/>
<div className="font-bold text-xs md:text-sm uppercase">
{secondOptionLogin}
</div>
</div>
</Link>
</div>
{/* End of Login Option */}
{/* Alternative Register */}
<div className="flex items-center justify-center gap-2 mt-4 text-xs md:text-sm">
<p>Don't have an account?</p>
<Link
to="/signup"
className="text-[#DE1B1B] font-bold text-xs md:text-sm"
>
Sign Up
</Link>
</div>
{/* End of Alternative Register */}
</div>
</div>
);
}
CASE CLOSED, in the form section onSubmit={handleSubmit} I delete it to just form, then onSubmit={handleSubmit} I move it to button to become onClick={handleSubmit}.
thank you for answering my question. i appreciate it

How to build carousel tailwind css flexbox with react slick

I first time build carousel with tailwind css and react slick.
Post component has own height calc(100vh - 65px). after I use flexbox like this:
data.map((i, index) => (
<div
key={index}
className="min-h-screen h-direct w-full mb-5 flex flex-col items-center p-2 md:px-6"
ref={index === data.length - 1 ? lastBookElementRef : null}
>
<Post
index={index}
item={i}
isModal={false}
t={t}
user={user}
/>
</div>
))
on Post page:
/* eslint-disable jsx-a11y/media-has-caption */
/* eslint-disable jsx-a11y/img-redundant-alt */
/* eslint-disable react/jsx-props-no-multi-spaces */
/* eslint-disable no-unused-expressions */
import { ArrowLeftOutlined, ArrowRightOutlined, DeleteOutlined, FileSyncOutlined, HeartFilled, HeartOutlined, LikeOutlined, UserOutlined } from '#ant-design/icons';
import axios from 'axios';
import Link from 'next/link';
import React, { useEffect, useRef, useState } from 'react';
import ReactPlayer from 'react-player';
import Slider from 'react-slick';
import { BallTriangle } from 'react-loader-spinner';
import calculateTime from '../calculateTime';
import { useSocket } from '../context/socket';
import Modal from './Modal';
const Post = ({ item, isModal, user, t }) => {
const [myLikes, setMyLikes] = useState(item.likes);
const [isOpen, setIsOpen] = useState(false);
const socket = useSocket();
const parentRef = useRef(null);
const scrollRef = useRef(null);
const [imageLoading, setImageLoading] = useState(item.files);
const imageLoaded = (id) => {
setImageLoading((prev) => prev.filter((t) => t.path !== id));
};
const [currentItem, setCurrentItem] = useState(0);
const settings = {
dots: false,
infinite: true,
speed: 500,
slidesToShow: 1,
slidesToScroll: 1,
initialSlide: 0,
swipeToSlide: true,
autoplay: false,
arrows: false,
lazyLoad: true,
};
const slide = useRef();
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
const addOrRemoveLike = async (postId) => {
const isLike = myLikes.filter((like) => like.user._id === user._id).length > 0;
try {
await axios({
method: 'PUT',
url: `${process.env.SERVER}/posts/${isLike ? 'unliked' : 'liked'}/${postId}`,
withCredentials: true,
});
if (!isLike) {
const users = [item.user._id];
if (item.user._id !== user._id) socket.emit('newnotification', { users });
}
isLike ? setMyLikes((prev) => prev.filter((p) => p.user._id !== user._id)) : setMyLikes((prev) => ([{ user }, ...prev]));
} catch (error) {
console.error(error);
}
};
const handleScroll = (direction) => {
const { current } = scrollRef;
setCurrentItem((prev) => (prev + 1) % (item.files.length));
const scrollAmount = current.offsetWidth ? current.offsetWidth : 100;
if (direction === 'left') {
current.scrollLeft -= scrollAmount;
} else {
current.scrollLeft += scrollAmount;
}
};
return (
<div className={`flex-1 w-[60%] border-b gap-3
md:w-full ${isModal ? 'flex-row' : 'flex-col'} flex items-center justify-start`}
>
{/* Top */}
<div className=" flex w-full flex-row items-center gap-3 ">
<div className="border overflow-hidden rounded-full w-14 h-14 sm:w-10 sm:h-10 flex items-center justify-center bg-nft-gray-2 dark:bg-nft-gray-3">
{
item.user.logo.length > 0
? <img src={`${process.env.SERVER}/${item.user.logo}`} className="w-full h-full object-cover" />
: <UserOutlined className="text-2xl sm:text-xl" />
}
</div>
<div className="flex-1 flex flex-row items-center justify-between gap-2">
<div className="flex-1 flex flex-col justify-start ">
<Link href={`user/${item.user.username}`}>
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a className="text-blue-600 dark:text-blue-500 font-medium"> {item.user.username} </a>
</Link>
<div className="w-10/12">
<h1 className="text-xs font-thin">
{item.location}
</h1>
<h1 className="text-xs font-thin">
<span className="font-medium">posted: </span>
{calculateTime(item.date, t)}
</h1>
</div>
</div>
{
user._id === item.user._id && (
<div>
<DeleteOutlined className="transition-all duration-500 text-xl cursor-pointer hover:text-nft-black-1 dark:hover:text-nft-gray-1" />
</div>
)
}
</div>
</div>
{/* Bottom */}
<div className="flex-1 w-full flex flex-col gap-1">
{/* FIles */}
<div
className="flex-2 w-full border bg-nft-gray-1 dark:bg-nft-black-1
relative
"
>
<ArrowLeftOutlined
className="absolute top-1/2 -translate-y-1/2 left-0 -translate-x-1/2
bg-nft-black-1 text-white text-xl flex justify-center items-center p-1 rounded-full cursor-pointer
hover:text-2xl transition-all duration-500
dark:bg-nft-gray-3 z-[5]
"
onClick={() => slide.current.slickPrev()}
/>
<ArrowRightOutlined
className="absolute top-1/2 -translate-y-1/2 right-0 translate-x-1/2
bg-nft-black-1 text-white text-xl flex justify-center items-center p-1 rounded-full cursor-pointer
hover:text-2xl transition-all duration-500
dark:bg-nft-gray-3 z-[5]
"
onClick={() => slide.current.slickNext()}
/>
<div className="h-[70%] w-full border bg-nft-gray-1 dark:bg-nft-black-1">
<div className="relative h-full">
<Slider {...settings} ref={slide}>
{
item.files.map((i, item) => (
<div key={item} className="w-full h-full flex justify-center items-center ">
{
i.type.startsWith('image')
? (
<img
onLoad={() => imageLoaded(i.path)}
src={`${process.env.SERVER}/${i.path}`}
className={` object-cover
${imageLoading.filter((t) => t.path === i.path).length > 0 && 'hidden'}
`}
alt="image"
/>
)
: (
<video
src={`${process.env.SERVER}/${i.path}`}
className="object-contain"
controls
controlsList="nodownload"
/>
)
}
</div>
))
}
</Slider>
</div>
</div>
</div>
{/* Statistics */}
<div className="flex-1 flex flex-col gap-2">
<div className="flex w-full flex-row justify-between items-center">
<div className=" flex flex-row gap-3 transition-all duration-500 items-center justify-start relative">
{
myLikes.filter((like) => like.user._id === user._id).length > 0
? (
<HeartFilled
className={`text-2xl text-red-500 dark:text-red-600 ${user.id === item.user._id && 'cursor-pointer'}`}
onClick={() => addOrRemoveLike(item._id)}
/>
)
: (
<HeartOutlined
className="text-2xl"
onClick={() => addOrRemoveLike(item._id)}
/>
)
}
{
myLikes.length > 0 && (
<div
className={`text-sm font-medium ${user._id === item.user._id && 'cursor-pointer '}`}
onClick={() => { setIsOpen(user._id === item.user._id); }}
>
{numberWithCommas(myLikes.length).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')} likes
</div>
)
}
</div>
<div className="text-sm relative before:content-[''] before:absolute before:block before:w-full before:h-[2px]
before:bottom-0 before:left-0 before:bg-blue-500 dark:before:bg-blue-600
before:hover:scale-x-100 before:scale-x-0 before:origin-top-left
before:transition-all before:ease-in-out before:duration-300
cursor-pointer text-blue-500 dark:text-blue-600
"
>Go to this post
</div>
</div>
<div className="flex flex-row gap-2 flex-wrap">
{item.tags.map((i) => (
<div key={i} className="flex flex-row items-center bg-blue-500 px-1 text-white dark:bg-blue-600 transition-all duration-500">
<p className="text-sm ">#{i}</p>
</div>
))}
</div>
<div className="flex flex-row">
<p className="text-sm truncate whitespace-nowrap overflow-hidden">{item.description}</p>
</div>
</div>
</div>
{
isOpen && (
<Modal
setClose={setIsOpen}
header="Likes"
>
{
myLikes.map((i, index) => (
<div
key={index}
className={` ${index !== myLikes.length - 1 && 'mb-2'} flex flex-row justify-center items-center cursor-pointer`}
>
<div className="w-full flex-1 p-2 shadow dark:bg-nft-black-2 flex flex-row items-center justify-between">
<div className="flex flex-row gap-5 flex-1 items-center">
<div className="h-10 w-10 rounded-full flex justify-center items-center bg-nft-gray-1 dark:bg-nft-black-1 overflow-hidden">
{
i.user.logo.length > 0
? (
<img
src={`${process.env.SERVER}/${i.user.logo}`}
className="w-full h-full object-cover"
/>
)
: <UserOutlined />
}
</div>
<div className="flex flex-col items-start justify-between">
<p>{i.user.username}</p>
{((i.user.firstName.length > 0) || (i.user.lastName.length > 0))
&& (
<h1
className="font-medium text-sm"
>{`${i.user.firstName} ${i.user.lastName}`}
</h1>
)}
</div>
</div>
</div>
</div>
))
}
</Modal>
)
}
</div>
);
};
export default Post;
I use react-slick for carousel. But height dont get. I parent div heigth h-[70%]. When I see without images everything good but after add react slick carousel images height increased. How do this?

TailwindCSS fade in Element on click

So I'm making this app and I need to fade in the menu when I click the button. I have it rendering on click using state, but I can't get it to fade in / fade out on click. When I edit the opacity value inside Chrome Dev Console the transition works fine, but when I want to change it using state it doesn't.
Any help? Thanks in advance!
import React, { useState } from "react";
import { useRouter } from "next/router";
import { MenuIcon, XIcon } from "#heroicons/react/outline";
function Header() {
const router = useRouter();
const [popCard, setPopCard] = useState("hidden");
const [fade, setFade] = useState(true);
const handleMenuClick = () => {
setPopCard("inline-block");
setFade(true);
};
const handleXClick = () => {
setPopCard("hidden");
setFade(false);
};
return (
<div className="text-center">
<header className="sticky z-50 top-0 shadow-md bg-white border-b p-5">
<div className="flex justify-between items-center">
<h1
className="text-6xl text-red-500 cursor-pointer"
onClick={() => router.push("/")}
>
Velvet
</h1>
<MenuIcon
className="h-8 text-red-500 cursor-pointer"
onClick={handleMenuClick}
/>
</div>
</header>
<div
className={
popCard +
" w-[60%] flex-col border my-10 pb-3 rounded-3xl shadow-lg transition duration-300 ease-in-out " +
`${fade === true ? "opacity-100" : "opacity-0"}`
}
>
<div className="flex justify-end">
<XIcon
className="h-6 text-red-500 cursor-pointer mt-2 mr-2 opacity-70"
onClick={handleXClick}
/>
</div>
<div className="space-y-8 text-3xl text-center mt-5 mb-10 text-red-500">
<h1>Contac</h1>
<h1>About Us</h1>
</div>
</div>
</div>
);
}
export default Header;
codesandbox: Sandbox
Just to be clear, I want the menu card to fade in when I click the menu button, and I want the menu card to fade out when I click the close button.
The solution is, you need to add duration, like this:
`transition-all duration-200 ${fade ? "opacity-100" : "opacity-0"}`
Here is my forked sandbox you had given, I've removed extra inline CSS, so it may become evident.
Here is the complete code:
function Header() {
const [popCard, setPopCard] = useState("hidden");
const [fade, setFade] = useState(false);
const handleMenuClick = () => {
setPopCard("inline-block");
setFade(true);
};
const handleXClick = () => {
setPopCard("hidden");
setFade(false);
};
console.log(fade, "fade");
return (
<div className="text-center">
<header className="sticky z-50 top-0 shadow-md bg-white border-b p-5">
<div className="flex justify-between items-center">
<h1 className="text-6xl text-red-500 cursor-pointer">Velvet</h1>
<button
className="text-3xl border rounded-lg px-5"
onClick={handleMenuClick}
>
Menu
</button>
</div>
</header>
<div className="p-10">
<div
className={`transition-all duration-200 ${
fade ? "opacity-100" : "opacity-0"
}`}
>
<div className="flex justify-end">
<button className="mt-2 mr-2 border p-2" onClick={handleXClick}>
Close
</button>
</div>
<div className="space-y-2 text-3xl text-center mt-5 mb-10 mx-5 text-red-500">
<h1>Kontakt</h1>
<h1>O Velvetu</h1>
</div>
</div>
</div>
</div>
);
}
export default Header;
Sandbox: https://codesandbox.io/s/sweet-swartz-mr3nru?file=/pages/index.js:41-1396

React Stepper change Status onClick

I am new to Ract and building a multi step form in Next.js, where I also use Context. So my project structure is pretty wild and I don't get how / where to change the steps.status when moving to next step in the stepper. So far I have my context, managing half of states, basically the formData, but also the 'original' state of my stepper:
import { useState, createContext, useContext } from "react";
export const FormContext = createContext();
export default function FormProvider({ children }) {
const [data, setData] = useState({});
const [steps, setSteps] = useState([
{ name: 'Vertrag', status: 'current' },
{ name: 'Dateneingabe', status: 'upcoming' },
{ name: 'Bestätigung', status: 'upcoming' },
]);
const setFormValues = (values) => {
setData((prevValues) => ({
...prevValues,
...values,
}));
};
return (
<FormContext.Provider value={{ steps, data, setFormValues }}>
{children}
</FormContext.Provider>
);
}
export const useFormData = () => useContext(FormContext);
In Stepper.js I therefore import my formData:
import { CheckIcon } from '#heroicons/react/solid'
import { useContext } from 'react'
import { FormContext } from "../context";
// status: 'complete', 'current', 'upcoming'
function classNames(...classes) {
return classes.filter(Boolean).join(' ')
}
export default function Stepper() {
const formData = useContext(FormContext);
const steps = formData.steps
return (
<nav aria-label="Progress">
<div className="flex items-center flex-col">
<ol className="flex items-center sm:flex-col md:flex-row mx-auto mt-32 mb-8">
{steps.map((step, stepIdx) => (
<li key={step.name} className={classNames(stepIdx !== steps.length - 1 ? 'pr-16 sm:pr-32' : '', 'relative')}>
{step.status === 'complete' ? (
<>
<div className="absolute inset-0 flex items-center" aria-hidden="true">
<div className="h-0.5 w-full bg-yellow-500" />
</div>
<a
href="#"
className="relative w-8 h-8 flex items-center justify-center bg-yellow-500 rounded-full hover:bg-yellow-500"
>
<span className="h-9 flex flex-col items-center">
<span className="relative top-2 z-10 w-8 h-8 flex items-center justify-center rounded-full group-hover:bg-indigo-800">
<CheckIcon className="w-5 h-5 text-white" aria-hidden="true" />
</span>
<span className="text-xs font-semibold tracking-wide text-gray-600 mt-8">{step.name}</span>
</span>
</a>
</>
) : step.status === 'current' ? (
<>
<div className="absolute inset-0 flex items-center" aria-hidden="true">
<div className="h-0.5 w-full bg-gray-200" />
</div>
<a
href="#"
className="relative w-8 h-8 flex items-center justify-center bg-white border-2 border-yellow-500 rounded-full"
aria-current="step"
>
<span className="h-9 flex flex-col items-center">
<span className="z-10 w-8 h-8 flex items-center justify-center rounded-full group-hover:bg-indigo-800">
<span className="relative h-2.5 w-2.5 bg-yellow-500 rounded-full relative" style={{top: '0.8rem'}} />
</span>
<span className="text-xs font-semibold tracking-wide text-gray-600" style={{marginTop: '2.72rem'}}>{step.name}</span>
</span>
</a>
</>
) : (
<>
<div className="absolute inset-0 flex items-center" aria-hidden="true">
<div className="h-0.5 w-full bg-gray-200" />
</div>
<a
href="#"
className="group relative w-8 h-8 flex items-center justify-center bg-white border-2 border-gray-300 rounded-full hover:border-gray-400"
>
<span className="h-9 flex flex-col items-center">
<span className="z-10 w-8 h-8 flex items-center justify-center rounded-full">
<span className="relative h-2.5 w-2.5 bg-transparent rounded-full group-hover:bg-gray-300" style={{top: '0.8rem'}} />
</span>
<span className="text-xs font-semibold tracking-wide text-gray-600" style={{marginTop: '2.72rem'}}>{step.name}</span>
</span>
</a>
</>
)}
</li>
))}
</ol>
</div>
</nav>
)
Moreover I have index.js page, where all the components come together
import { useState } from "react";
import Head from "next/head";
import Stepper from '../components/Stepper'
import styles from "../styles/styles.module.scss";
import FormCard from "../components/FormCard";
import Navbar from "../components/Navbar";
import { CheckIcon } from '#heroicons/react/solid'
import {
PersonalInfo,
ConfirmPurchase,
ContractInfo,
} from "../components/Forms";
import FormCompleted from "../components/FormCompleted";
function classNames(...classes) {
return classes.filter(Boolean).join(' ')
}
const App = () => {
const [formStep, setFormStep] = useState(0);
const nextFormStep = () => setFormStep((currentStep) => currentStep + 1);
const prevFormStep = () => setFormStep((currentStep) => currentStep - 1);
const [activeStep, setActiveStep] = useState(0);
const handleNextStep = () => {
setActiveStep((prevActiveStep) => prevActiveStep + 1);
};
const handlePrevoiusStep = () => {
setActiveStep((prevActiveStep) => prevActiveStep - 1);
};
return (
<div>
<Head>
<title>Next.js Multi Step Form</title>
</Head>
< Navbar />
< Stepper activeStep={activeStep} />
<div className={styles.container}>
<FormCard currentStep={formStep} prevFormStep={prevFormStep}>
{formStep >= 0 && (
<ContractInfo formStep={formStep} nextFormStep={nextFormStep} />
)}
{formStep >= 1 && (
<PersonalInfo formStep={formStep} nextFormStep={nextFormStep} />
)}
{formStep >= 2 && (
<ConfirmPurchase formStep={formStep} nextFormStep={nextFormStep} />
)}
{formStep > 2 && <FormCompleted />}
</FormCard>
</div>
<div className="mt-1 mb-5 sm:mt-8 sm:flex sm:justify-center lg:justify-center">
<div className="rounded-md shadow">
<a role="button" tabIndex={0}
onClick={ () => { prevFormStep(); handlePrevoiusStep() }}
className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-white bg-yellow-500 hover:bg-yallow-600 md:py-4 md:text-lg md:px-10"
>
Back
</a>
</div>
<div className="mt-3 sm:mt-0 sm:ml-3">
<a
onClick={ () => { nextFormStep(); handleNextStep() }}
className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-white bg-yellow-500 hover:bg-yallow-600 md:py-4 md:text-lg md:px-10"
>
Next
</a>
</div>
</div>
</div>
);
};
export default App;
As you see, Stepper is managed in three different files. But I am at least capable to change the activeStep index when clicking on Buttons, which already was a huge challenge for me. So now I also need the design to change. So that activeStep gets the step.status === 'current'. All stepps index < activeStep index should get step.status === 'complete' and logically all stepps index > activeStep index - step.status === 'upcoming'. Now I tried to handle this in index.js, but of course get back step is undefined, even though it is defined in Stepper.js through context.

Post.js is not showing the blog post...Are there any errors here?

There should be a block of blog post here but its empty:
Please check the map and the other syntaxes, I am not getting it.
import React,{useState,useEffect} from "react";
import { Link } from "react-router-dom";
import sanityClient from "../client.js";
This is the export default function below
export default function Post(){
const [postData, setPost] = useState(null);
useEffect(() => {
sanityClient
.fetch(`*[_type =="post"]{
title,
slug,
mainImage{
asset->{
_id;
url
},
alt
}
}`)
.then((data) => setPost(data))
.catch(console.error);
},[]);
I think there is something missing in postData map.
return (
<main className="bg-green-100 min-h-screen p-12">
<section className="container mx-auto">
<h1 className="text-5xl flex justify-center cursive">Blog Posts Page</h1>
<h2 className="text-lg text-gray-600 flex justify-center mb-12">Welcome to my world!</h2>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
{postData && postData.map((post,index) => (
<article>
<Link to={"/post/" + post.slug.current} key={post.slug.current}>
<span className="block h-64 relative rounded shadow leading-snug bg-white border-l-8 border-green-400" key={index}>
<img src={post.mainImage.asset.url} alt={post.mainImage.alt} className="w-full h-full rounded-r object-cover absolute" />
<span className="block relative h-full flex justify-end items-end pr-4 pb-4">
<h3 className="text-gray-800 text-lg font-blog px-3 py-4 bg-red-700 text-red-100 bg-opacity-75 rounded">{post.title}</h3>
</span>
</span>
</Link>
</article>
))}
</div>
</section>
</main>
)
}

Resources