Stack: NextJS, JavaScript, React, Tailwind, Framer Motion
I've scoured the web for answers to this but mostly the answers were saying that the <div> that wraps the image must have a position of relative. I tried this and it didn't change anything.
Code for the text and images:
import React from "react";
import { motion } from "framer-motion";
import Image from "next/image";
export default function Card({ image, name, description }) {
return (
<motion.div
className="flex justify-center mt-5"
initial="hidden"
animate="visible"
variants={{
hidden: { scale: 0.9, opacity: 0 },
visible: {
scale: 1,
opacity: 1,
transition: { delay: 0.3, ease: [0.17, 0.67, 0.83, 0.67] },
},
}}
>
<div className="relative">
<Image
className="rounded-full absolute"
src={image}
width={100}
height={100}
alt="Leader profile picture"
/>
</div>
<div>
<motion.h1
className="text-xl pl-1 font-bourton"
initial="hidden"
animate="visible"
variants={{
hidden: { scale: 0.9, opacity: 0 },
visible: {
scale: 1,
opacity: 1,
transition: { delay: 0.35, ease: [0.17, 0.67, 0.83, 0.67] },
},
}}
>
{name}
</motion.h1>
<motion.p className="text-sm p-3 text-wrap w-48 text-slate-600 font-bourton">
{description}
</motion.p>
</div>
</motion.div>
);
}
The code for the sidebar:
import { useState } from "react";
import { motion } from "framer-motion";
import classnames from "classnames";
import { useRouter } from "next/router";
const path01Variants = {
open: { d: "M3.06061 2.99999L21.0606 21" },
closed: { d: "M0 9.5L24 9.5" },
};
const path02Variants = {
open: { d: "M3.00006 21.0607L21 3.06064" },
moving: { d: "M0 14.5L24 14.5" },
closed: { d: "M0 14.5L15 14.5" },
};
const Sidebar = () => {
const [showSidebar, setShowSidebar] = useState(false);
const [animation, setAnimation] = useState("closed");
const router = useRouter();
const onClick = () => {
setAnimation("moving");
setTimeout(() => {
setAnimation(animation === "closed" ? "open" : "closed");
}, 200);
setShowSidebar(!showSidebar);
};
return (
<>
{showSidebar ? (
<button
className="flex justify-center relative z-100 items-center w-12 h-12 border border-black rounded-full"
onClick={onClick}
>
<svg width="24" height="24" viewBox="0 0 24 24">
<motion.path
stroke="#fff"
animate={animation}
variants={path01Variants}
/>
<motion.path
stroke="#fff"
animate={animation}
variants={path02Variants}
/>
</svg>
</button>
) : (
<button
className="flex justify-center top-12 right-12 items-center w-12 h-12 border border-black rounded-full"
onClick={onClick}
>
<svg width="24" height="24" viewBox="0 0 24 24">
<motion.path
stroke="#000"
animate={animation}
variants={path01Variants}
/>
<motion.path
stroke="#000"
animate={animation}
variants={path02Variants}
/>
</svg>
</button>
)}
<div
className={` bg-unionred border-black opacity-90 top-0 right-0 lg:w-[35vw] sm:w-[50vw] p-10 pl-20 fixed text-white h-full ease-in-out duration-300 ${
showSidebar ? "translate-x-0" : "translate-x-full"
}`}
>
<button
className="flex justify-center absolute top-8 right-8 items-center w-12 h-12 border border-white rounded-full"
onClick={onClick}
>
<svg width="24" height="24" viewBox="0 0 24 24">
<motion.path
stroke="#fff"
animate={animation}
variants={path01Variants}
/>
<motion.path
stroke="#fff"
animate={animation}
variants={path02Variants}
/>
</svg>
</button>
<h3 className="mt-20 text-4xl underline font-bourton text-white">
<button className="underline" onClick={() => router.push("whoweare")}>
Who We Are
</button>
</h3>
<h3 className="mt-20 text-4xl font-bourton text-white">
<button
className="underline"
onClick={() => router.push("ourleaders")}
>
Our Leaders
</button>
</h3>
<h3 className="mt-20 text-4xl font-bourton text-white">
<button className="underline">News</button>
</h3>
</div>
</>
);
};
export default Sidebar;
Before opening sidebar
After opening sidebar
Any help would be appreciated! Thank you.
It looks like you're not applying a z-index to the sidebar itself. That <div> is the container that needs to overlap the main content, so that is where you need to add the utility class.
Additionally, it looks like you're trying to use z-100 which is not one of the Tailwind defaults (see: https://tailwindcss.com/docs/z-index). You can either add that new value to your tailwind.config.js or try z-50, which is the highest default.
For example:
<div className={`z-50 bg-unionred border-black opacity-90 top-0 right-0 lg:w-[35vw] sm:w-[50vw] p-10 pl-20 fixed text-white h-full ease-in-out duration-300 ${showSidebar ? "translate-x-0" : "translate-x-full"}`}>
Related
I'm using next JS with tailwind css and using react-responsive-carousel package.the issue that i'm having is that when i start to swipe the images they're next to each other horizontally. they have no effect (like fade or ease in out) to them while swiping. and i wanted to make animation transition with ease-in-out or fade to it. how can i add fade in out animation instead of its default slide?
<Carousel
swipeScrollTolerance={100}
preventMovementUntilSwipeScrollTolerance
key={house.id}
axis="horizontal"
showStatus={false}
showThumbs={false}
swipeable={true}
className="group "
renderIndicator={(onClickHandler, isSelected, index, label) => {
const defStyle = {
marginLeft: 6,
color: "#b9b9b9",
cursor: "pointer",
};
const style = isSelected
? { ...defStyle, color: "#dcdcdc" }
: { ...defStyle };
return (
<span
style={style}
onClick={onClickHandler}
onKeyDown={onClickHandler}
value={index}
key={index}
role="button"
tabIndex={0}
aria-label={`${label} ${index + 1}`}
>
<FontAwesomeIcon
icon={faCircle}
className="sm:h-[0.35rem] sm:w-[0.35rem] h-2 w-2 font-thin"
/>
</span>
);
}}
renderArrowPrev={(clickHandler, hasPrev) => {
return (
<div
className={`${
hasPrev && !toggle ? "absolute" : "hidden"
} top-0 bottom-0 left-0 flex justify-center items-center p-3 opacity-0 group-hover:opacity-70 cursor-pointer z-10 text-white`}
onClick={clickHandler}
>
<FontAwesomeIcon
icon={faChevronCircleLeft}
className="h-8 w-8 font-thin"
/>
</div>
);
}}
renderArrowNext={(clickHandler, hasNext) => {
return (
<div
className={`${
hasNext && !toggle ? "absolute" : "hidden"
} top-0 bottom-0 right-0 flex justify-center items-center p-3 opacity-0 group-hover:opacity-70 cursor-pointerz-10 text-white`}
onClick={clickHandler}
>
<FontAwesomeIcon
icon={faChevronCircleRight}
className="h-8 w-8 font-thin"
/>
</div>
);
}}
>
{house.img.map((img, index) => {
return (
<Link href={`/living/${house.id}`} key={img.id}>
<div
key={img.id}
className=" w-full relative -z-10 pt-[100%] cursor-pointer"
>
<Image
src={img.img}
alt="profile"
fill
className="w-full h-full top-0 left-0 -z-10 object-cover rounded-2xl ease-in-out duration-200"
/>
</div>
</Link>
);
})}
</Carousel>
I have a mySQL database with about 100 rows, containing the data of products I'm listing in an eCommerce app.
I have them displayed on the front-end by using Axios to get the rows from my back-end SELECT * statement. They are then held as an array state const [rows, setRows] = useState([]);.
These rows are then mapped for each row found in the table, so around 100 maps.
I have noticed with this many results performance has started to become an issue and I've heard similar issues from mapping arrays.
I've benchmarked and tinkered to find that this component is the one causing the performance issues.
I would like any solutions to improve my component's performance, without altering the structure too much.
Heres the component
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import Button from '#mui/material/Button';
export default function ProductCard(props) {
const [rows, setRows] = useState([]);
const isntDesktop = props.isntDesktop; //Constant from parent
const isMobile = window.innerWidth < 768; //Checks if screen is smaller than mobile
const sidebar = props.sidebar; //Sidebar state from parent component
useEffect(() => {
axios.get('http://localhost:8080/products/get/')
.then(res => {
setRows(res.data);
}).catch(err => {
console.log(err);
});
});
return (
rows
.sort((a, b) => a.name.localeCompare(b.name))
//Map products
.map((row, index) => {
return (
/* Adjust structure for mobile */
isMobile ? (
/* Product card */
<div
key={index}
class={`${
sidebar ? 'border-0' : 'border'
} relative mt-6 py-8 px-5 rounded`}
>
{/* Heading */}
<h2 class="text-2xl font-semibold tracking-wide text-gray-700">{row.name}</h2>
<div class="flex items-center sm:items-start space-x-5">
{/* Image */}
<img
src={row.image}
alt="product"
class={`${
sidebar ? 'opacity-50' : 'opacity-100'
} mt-4 min-w-[125px] sm:w-[300px] h-auto`}
/>
<div class="mt-4">
{/* Price */}
<p class="text-2xl sm:text-xl font-semibold sm:font-medium text-gray-600">£ {row.price}</p>
{/* Keywords */}
<ul class="ml-5 mt-2 space-y-1">
<li class="list-disc text-gray-600">{row.keyword_one}</li>
<li class="list-disc text-gray-600" >{row.keyword_two}</li>
<li class="list-disc text-gray-600">{row.keyword_three}</li>
</ul>
</div>
</div>
<div
class={`${
sidebar ? 'bg-none' : 'bg-gray-100'
} mt-4 py-2 px-4 flex justify-center space-x-2.5`}>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 text-sky-500">
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 18.75a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m3 0h6m-9 0H3.375a1.125 1.125 0 01-1.125-1.125V14.25m17.25 4.5a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m3 0h1.125c.621 0 1.129-.504 1.09-1.124a17.902 17.902 0 00-3.213-9.193 2.056 2.056 0 00-1.58-.86H14.25M16.5 18.75h-2.25m0-11.177v-.958c0-.568-.422-1.048-.987-1.106a48.554 48.554 0 00-10.026 0 1.106 1.106 0 00-.987 1.106v7.635m12-6.677v6.677m0 4.5v-4.5m0 0h-12" />
</svg>
<label class="text-gray-600 text-sm">Free delivery on orders over £50</label>
</div>
<div class="mt-4 w-full mx-auto flex flex-col sm:flex-row space-y-2.5 sm:space-y-0 sm:space-x-2.5">
{sidebar && isntDesktop ? (
<Button
type="submit"
variant="contained"
sx={{
opacity: 0.5,
bgcolor: '#6366f1',
textTransform: 'none',
width: '100%',
':hover': {
backgroundColor: '#4f46e5',
},
}}
>
View product
</Button>
) : (
<Button
type="submit"
variant="contained"
sx={{
opacity: 1,
bgcolor: '#6366f1',
textTransform: 'none',
width: '100%',
':hover': {
backgroundColor: '#4f46e5',
},
}}
>
View product
</Button>
)}
<Button
type="submit"
sx={{
border: ' 1px solid #38bdf8',
color: '#38bdf8',
textTransform: 'none',
width: '100%',
':hover': {
color: '#ffffff',
backgroundColor: '#38bdf8',
},
}}
>
Add to cart
</Button>
</div>
</div>
) : (
<div key={index} class="flex mt-6 py-8 px-5 border rounded">
{/* Image */}
<img src={row.image} alt="product" class="mr-4 w-[300px] h-auto" />
<div class="relative w-full px-5 items-center">
{/* Heading */}
<h2 class="text-2xl font-semibold tracking-wide text-gray-700">{row.name}</h2>
<div class="mt-8 flex justify-between">
{/* Keywords */}
<ul class="ml-5 space-y-1">
<li class="list-disc text-gray-600">{row.keyword_one}</li>
<li class="list-disc text-gray-600" >{row.keyword_two}</li>
<li class="list-disc text-gray-600">{row.keyword_three}</li>
</ul>
{/* Promo */}
<div class="flex flex-col ml-5">
{/* Price */}
<p class="text-xl font-medium text-gray-600">£ {row.price}</p>
<div
class={`${
sidebar ? 'bg-none' : 'bg-gray-100'
} mt-4 py-2 px-4 flex space-x-2.5`}>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 text-sky-500">
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 18.75a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m3 0h6m-9 0H3.375a1.125 1.125 0 01-1.125-1.125V14.25m17.25 4.5a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m3 0h1.125c.621 0 1.129-.504 1.09-1.124a17.902 17.902 0 00-3.213-9.193 2.056 2.056 0 00-1.58-.86H14.25M16.5 18.75h-2.25m0-11.177v-.958c0-.568-.422-1.048-.987-1.106a48.554 48.554 0 00-10.026 0 1.106 1.106 0 00-.987 1.106v7.635m12-6.677v6.677m0 4.5v-4.5m0 0h-12" />
</svg>
<label class="text-gray-600 text-sm">Free delivery on orders over £50</label>
</div>
</div>
</div>
<div class="absolute bottom-0 w-full pr-5 flex space-x-2.5">
{sidebar && isntDesktop ? (
<Button
type="submit"
variant="contained"
sx={{
opacity: 0.5,
bgcolor: '#6366f1',
textTransform: 'none',
width: '100%',
':hover': {
backgroundColor: '#4f46e5',
},
}}
>
View product
</Button>
) : (
<Button
type="submit"
variant="contained"
sx={{
opacity: 1,
bgcolor: '#6366f1',
textTransform: 'none',
width: '100%',
':hover': {
backgroundColor: '#4f46e5',
},
}}
>
View product
</Button>
)}
<Button
type="submit"
sx={{
border: ' 1px solid #38bdf8',
color: '#38bdf8',
textTransform: 'none',
width: '100%',
':hover': {
color: '#ffffff',
backgroundColor: '#38bdf8',
},
}}
>
Add to cart
</Button>
</div>
</div>
</div>
)
)
})
)
}
Since you (probably) want to fetch your data once (on mount), you will have to add an empty dependency array to your useEffect to avoid infinitely running a query . You can read more about it here: https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects (In the Note section).
useEffect(() => {
axios.get('http://localhost:8080/products/get/').then(res => {
setRows(res.data);
}).catch(err => {
console.log(err);
});
}, [])
I saw that you render all your data on render and unfortunately, React is notoriously slow when it comes to rendering large lists. You might need to think about windowing your data, aka rendering a chunk of your data when your page first load and render more data as the user scroll further down. There's a great library that handles this for you: https://tanstack.com/virtual/v3/docs/guide/introduction.
I hope this helps!
I customerized the arrow shape and color of swiper js by adding svg, but it did not have any functionality. And I ended up having two sets of arrows on each side, one is my own arrow button which does not function, the other one is built in swiper js arrow which functions well.
so how to replace swiper's arrow with my own arrow that functions well?
import React from "react";
import sportsList from "../utils/sportsList";
import Card from "./Card";
import { Swiper, SwiperSlide } from "swiper/react";
import "swiper/css";
import "swiper/css/navigation";
import { Navigation } from "swiper";
export default function SliderComponent() {
return (
<>
<Swiper
spaceBetween={50}
slidesPerView={4}
onSlideChange={() => console.log("slide change")}
onSwiper={(swiper) => console.log(swiper)}
grabCursor={true}
modules={[Navigation]}
navigation={true}
breakpoints={{
// when window width is >= 320px
320: {
slidesPerView: 1,
spaceBetween: 24,
},
// when window width is >= 480px
480: {
slidesPerView: 2,
spaceBetween: 24,
},
// when window width is >= 640px
640: {
slidesPerView: 6,
spaceBetween: 24,
},
1024: {
slidesPerView: 6,
spaceBetween: 32,
slidesPerGroup: 1,
},
1336: {
slidesPerView: 6,
spaceBetween: 32,
},
}}
className="relative w-10/12 mx-auto flex flex-row"
>
<div className="absolute inset-y-0 left-0 z-10 flex items-center">
<button class="bg-white -ml-2 lg:-ml-4 flex justify-center items-center w-10 h-10 rounded-full shadow focus:outline-none">
<svg
viewBox="0 0 20 20"
fill="currentColor"
class="chevron-left w-6 h-6"
>
<path
fill-rule="evenodd"
d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z"
clip-rule="evenodd"
></path>
</svg>
</button>
</div>
{sportsList.map((item) => {
return (
<SwiperSlide key={item.id}>
<Card item={item} />
</SwiperSlide>
);
})}
<div class="absolute inset-y-0 right-0 z-10 flex items-center">
<button class="bg-white -mr-2 lg:-mr-4 flex justify-center items-center w-10 h-10 rounded-full shadow focus:outline-none">
<svg
viewBox="0 0 20 20"
fill="currentColor"
class="chevron-right w-6 h-6"
>
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"
></path>
</svg>
</button>
</div>
</Swiper>
</>
);
}
To customize previous and next button in swiper.js you should pass NavigationOptions object to navigation instead of true, something like this will do it:
navigation={{
nextEl: ".image-swiper-button-next",
prevEl: ".image-swiper-button-prev",
disabledClass: "swiper-button-disabled"
}}
and then you can use those classnames for your buttons
don't forget to remove this import "swiper/css/navigation"; so it won't override your navigation button styles
you can see an example below
import React from "react";
import sportsList from "../utils/sportsList";
import Card from "./Card";
import { Swiper, SwiperSlide } from "swiper/react";
import "swiper/css";
import "swiper/css/navigation";
import { Navigation } from "swiper";
import "swiper/css/bundle";
import SwiperCore from "swiper";
SwiperCore.use([Navigation]);
export default function SliderComponent() {
return (
<>
<Swiper
spaceBetween={50}
slidesPerView={4}
slidesPerGroup={3}
onSlideChange={() => console.log("slide change")}
onSwiper={(swiper) => console.log(swiper)}
grabCursor={true}
navigation={{
prevEl: ".swiper-button-prev",
nextEl: ".swiper-button-next",
}}
breakpoints={{
// when window width is >= 320px
320: {
slidesPerView: 1,
spaceBetween: 24,
},
// when window width is >= 480px
480: {
slidesPerView: 2,
spaceBetween: 24,
},
// when window width is >= 640px
640: {
slidesPerView: 2,
spaceBetween: 24,
},
1024: {
slidesPerView: 4,
spaceBetween: 32,
slidesPerGroup: 1,
},
1336: {
slidesPerView: 6,
spaceBetween: 32,
},
}}
tag="section"
wrapperTag="ul"
id="main"
style={{
"--swiper-navigation-color": "red",
"--swiper-navigation-size": "150px",
}}
className="relative w-10/12 mx-auto flex flex-row"
>
<div className="absolute inset-y-0 left-0 z-10 flex items-center">
<button className=" swiper-button-prev bg-white -ml-2 lg:-ml-4 flex justify-center items-center w-10 h-10 rounded-full shadow focus:outline-none"></button>
</div>
below is a code for a stepper that displays certain text in each step , the number of the steps is highlighted (by changing the background color of the step number) , and it will display a text beneath it , my issue is i wanted to use framer-motion with AnimatePresense and exit prop for the transitioning from one text to another but its not wokring , i would appreciate the feed back
below is the animation variants
export const instructionVariant = {
hidden: {
opacity: 0,
x: "100vw",
},
visible: {
opacity: 1,
x: 0,
transition: {
type: "spring",
mass: 0.7,
damping: 7,
ease: "easeIn",
},
},
exit: {
x: "-100vw",
opacity: 0,
transition: { duration: 0.5, ease: "easeInOut" },
},
};
export const paragraphVariant = {
hidden: {
opacity: 0,
x: "10hw",
},
visible: {
opacity: 1,
x: 0,
transition: {
type: "spring",
mass: 0.7,
damping: 7,
ease: "easeIn",
},
},
exit: {
x: "-5hw",
opacity: 0,
transition: { duration: 0.5, ease: "easeInOut" },
},
};
below is the code , i used redux-toolkit for this , if i need to ppost the code for it if needed
const Instructions: FC = () => {
const step = useAppSelector((state) => state.instruction.step);
const dispatch = useAppDispatch();
return (
<motion.div
variants={instructionVariant}
initial="hidden"
animate="visible"
exit="exit"
className="h-2/4 w-3/5 bg-primary-200 border-2 border-solid border-black rounded-lg shadow-lg shadow-black relative"
>
<div className="grid grid-rows-[1fr_4fr]">
<div className="flex justify-around items-center m-10">
{data.map((item: InstructionsProps) => {
return (
<>
<div>
<h1
className={
step >= item.id
? "flex justify-center items-center text-4xl rounded-full bg-secondary-100 text-white w-16 h-16 transition-all duration-300 ease-in"
: "flex justify-center items-center text-4xl rounded-full bg-primary-100 text-white w-16 h-16 transition-all duration-300 ease-in"
}
>
{item.id - 1}
</h1>
</div>
<div
className={
step > item.id
? "last:hidden text-secondary-100 transition-all duration-300 ease-in"
: "last:hidden transition-all duration-300 ease-in"
}
>
<FaArrowRight size={20} />
</div>
</>
);
})}
</div>
<AnimatePresence exitBeforeEnter>
<motion.h1
variants={paragraphVariant}
initial="hidden"
animate="visible"
exit="exit"
className="m-10 text-xl font-bold font-rubik"
>
{data[step - 1].description}
</motion.h1>
</AnimatePresence>
</div>
<div className="absolute right-6 bottom-6 flex justify-between items-center w-32">
<button
onClick={() => dispatch(instructionsAction.instructionreverse())}
className="hover:scale-125 active:scale-110 active:text-secondary-200 hover:text-secondary-200 transition-all duration-300 ease-in"
>
<HiOutlineChevronDoubleLeft size={35} />
</button>
<button
onClick={() => dispatch(instructionsAction.instructioncomplt())}
className="hover:scale-125 active:scale-110 active:text-secondary-200 hover:text-secondary-200 transition-all duration-300 ease-in"
>
<HiOutlineChevronDoubleRight size={35} />
</button>
</div>
</motion.div>
);
};
From the AnimatePresence docs:
Direct children must each have a unique key prop so AnimatePresence
can track their presence in the tree.
const MyComponent = ({ isVisible }) => (
<AnimatePresence>
{isVisible && (
<motion.div
key="modal"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/>
)}
</AnimatePresence>
)
So My page looks like below
I have written a logic to open a modal using headlessui on clicking one of the sub hoodie images.
This works perfectly , but after I close it, the scrolling of the page disables and an extra
style="overflow: hidden; padding-right: 16px;"
is getting added to my main html tag.
Below is the reference
How do i stop the modal to automatically apply padding and overflow.
A lot of people have had this problem with bootstrap but i am not able to find this issue resolved in tailwind. Please help
below is the code for my modal
import React from 'react'
import { Dialog, Transition } from '#headlessui/react'
import { Fragment } from 'react'
import {XIcon} from '#heroicons/react/solid'
import {useRecoilState} from 'recoil'
import {openState} from '../../../atoms/modalAtom'
const product = {
name: "Women's Basic Tee",
price: '$32',
rating: 3.9,
reviewCount: 512,
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/product-page-01-featured-product-shot.jpg',
imageAlt: "Back of women's Basic Tee in black.",
colors: [
{ name: 'Black', bgColor: 'bg-gray-900', selectedColor: 'ring-gray-900' },
{ name: 'Heather Grey', bgColor: 'bg-gray-400', selectedColor: 'ring-gray-400' },
],
sizes: [
{ name: 'XXS', inStock: true },
{ name: 'XS', inStock: true },
{ name: 'S', inStock: true },
{ name: 'M', inStock: true },
{ name: 'L', inStock: true },
{ name: 'XL', inStock: true },
{ name: 'XXL', inStock: false },
],
}
export function DesignQuickView ({design}) {
const [open, setOpen] = useRecoilState(openState)
return (
<Transition.Root show={open} as={Fragment}>
<Dialog as="div" className="relative z-10" onClose={() => setOpen(false)}>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="hidden fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity md:block" />
</Transition.Child>
<div className="fixed z-10 inset-0 overflow-y-auto">
<div className="flex items-stretch md:items-center justify-center min-h-full text-center md:px-2 lg:px-4">
{/* This element is to trick the browser into centering the modal contents. */}
<span className="hidden md:inline-block md:align-middle md:h-screen" aria-hidden="true">
</span>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 translate-y-4 md:translate-y-0 md:scale-95"
enterTo="opacity-100 translate-y-0 md:scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 translate-y-0 md:scale-100"
leaveTo="opacity-0 translate-y-4 md:translate-y-0 md:scale-95"
>
<Dialog.Panel className="flex text-base text-left transform transition w-full md:max-w-2xl md:px-4 md:my-8 lg:max-w-4xl">
<div className="w-full relative flex items-center bg-white px-4 pt-14 pb-8 overflow-hidden shadow-2xl sm:px-6 sm:pt-8 md:p-6 lg:p-8">
<button
type="button"
className="absolute top-4 right-4 text-gray-400 hover:text-gray-500 sm:top-8 sm:right-6 md:top-6 md:right-6 lg:top-8 lg:right-8"
onClick={() => setOpen(false)}
>
<span className="sr-only">Close</span>
<XIcon className="h-6 w-6" aria-hidden="true" />
</button>
<div className="w-full grid grid-cols-1 gap-y-8 gap-x-6 items-start sm:grid-cols-12 lg:items-center lg:gap-x-8">
<div className="aspect-w-3 aspect-h-4 rounded-lg bg-gray-100 overflow-hidden sm:col-span-4 lg:col-span-5">
<img src={design.url} alt={design.name} className="object-center object-cover" />
</div>
<div className="sm:col-span-8 lg:col-span-7">
<h2 className="text-xl font-medium text-gray-900 sm:pr-12">Hoodie</h2>
<section aria-labelledby="information-heading" className="mt-1">
<h3 id="information-heading" className="sr-only">
Product information
</h3>
<p className="font-medium text-gray-900">{product.price}</p>
</section>
<section aria-labelledby="options-heading" className="mt-8">
<h3 id="options-heading" className="sr-only">
Product options
</h3>
<form>
{/* Color picker */}
<div>
<h4 className="text-sm font-medium text-gray-900">Color</h4>
{/* <RadioGroup value={selectedColor} onChange={setSelectedColor} className="mt-2">
<RadioGroup.Label className="sr-only">Choose a color</RadioGroup.Label>
<div className="flex items-center space-x-3">
{product.colors.map((color) => (
<RadioGroup.Option
key={color.name}
value={color}
className={({ active, checked }) =>
classNames(
color.selectedColor,
active && checked ? 'ring ring-offset-1' : '',
!active && checked ? 'ring-2' : '',
'-m-0.5 relative p-0.5 rounded-full flex items-center justify-center cursor-pointer focus:outline-none'
)
}
>
<RadioGroup.Label as="span" className="sr-only">
{color.name}
</RadioGroup.Label>
<span
aria-hidden="true"
className={classNames(
color.bgColor,
'h-8 w-8 border border-black border-opacity-10 rounded-full'
)}
/>
</RadioGroup.Option>
))}
</div>
</RadioGroup> */}
</div>
{/* Size picker */}
<div className="mt-8">
<div className="flex items-center justify-between">
<h4 className="text-sm font-medium text-gray-900">Size</h4>
<a href="#" className="text-sm font-medium text-indigo-600 hover:text-indigo-500">
Size guide
</a>
</div>
{/* <RadioGroup value={selectedSize} onChange={setSelectedSize} className="mt-2">
<RadioGroup.Label className="sr-only">Choose a size</RadioGroup.Label>
<div className="grid grid-cols-7 gap-2">
{product.sizes.map((size) => (
<RadioGroup.Option
key={size.name}
value={size}
className={({ active, checked }) =>
classNames(
size.inStock
? 'cursor-pointer focus:outline-none'
: 'opacity-25 cursor-not-allowed',
active ? 'ring-2 ring-offset-2 ring-indigo-500' : '',
checked
? 'bg-indigo-600 border-transparent text-white hover:bg-indigo-700'
: 'bg-white border-gray-200 text-gray-900 hover:bg-gray-50',
'border rounded-md py-3 px-3 flex items-center justify-center text-sm font-medium uppercase sm:flex-1'
)
}
disabled={!size.inStock}
>
<RadioGroup.Label as="span">{size.name}</RadioGroup.Label>
</RadioGroup.Option>
))}
</div>
</RadioGroup> */}
</div>
</form>
</section>
</div>
</div>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition.Root>
)
}
export default DesignQuickView
In the closeModal function, you can override the style of the html tag:
function closeModal() {
setOpen(false);
setTimeout(() => {
const html = document.body.parentNode as HTMLElement | null;
html.classList.add('modal_close');
}, 50);
}
function openModal() {
setOpen(true);
const html = document.body.parentNode as HTMLElement | null;
html.classList.remove('modal_close');
}
<Dialog as="div" className="relative z-10" onClose={closeModal}><
<button
type="button"
className="absolute top-4 right-4 text-gray-400 hover:text-gray-500 sm:top-8 sm:right-6 md:top-6 md:right-6 lg:top-8 lg:right-8"
onClick={closeModal}
>
style.css
.modal_close {
overflow: auto !important;
padding-right: 0px !important;
}