State Update prevents classList.toggle() - javascript

I am making a timeslot-picker component and want to toggle the class of each individual timeslot-item so it changes color and also add the timeslot information to an array of objects. If I update the state array with an onClick function the classList.toggle function doesn't seem to work, as none of the timeslots change color. I would appreciate any help on how to combine both functionalites. This is the part of my component required for the functionality:
<ul className="w-[180px] h-[400px] overflow-y-scroll flex flex-col gap-1 pt-4 relative">
<div className="text-center ">{day.day}</div>
{timeslots.map((timeslot) => (
<li key={Math.random()} className="mt-3">
<button
onClick={(e) => {
e.currentTarget.classList.toggle('bg-violet-500');
setRequestedTimeslots([
...requestedTimeslots,
{
day: day.day,
start: timeslot.start,
end: timeslot.end,
},
]);
}}
className="px-4 py-2 bg-[#F5F5F5] rounded-xl w-full text-center"
>
{timeslot.start} - {timeslot.end}
</button>
</li>
))}
</ul>

Related

Drag and Drop ghost trailing on drop w/React

I am having this problem when I try to reorder elements by dragging them. The reorder works but the functionality of changing places is not performing as desired. When you drag an element/image from a position x to position y it will change the position and it will stay there but on drop handler a "ghost" section it tries to go to the old position and the delete button on the overlay disappears.
I've provided a CodeSandbox link.
This is the component throwing the error.
as oposed to
I tried adding more event handlers like :
onDragOver
onDragDrop
onDragStart
onDragEnter
onDragLeave
and tried to add some logic to metigate the transition of images, but nothing worked.
{imageList.map((image, index, isDragging) => (
<div
key={index}
className="image-item w-1/2 sm:w-40 h-36 sm:p-2 px-1 pt-2"
draggable
onDragStart={(e) => handleDragStart(e, index)}
onDragEnd={(e) => handleDragEnd(e)}
onDragEnter={(e) => handleDragEnter(e, index)}
onDragLeave={(e) => handleDragLeave(e)}
onDragOver={(e) => handleDragOver(e, index)}
onDrop={(e) => handleDrop(e, index)}
>
<div
className={`border relative bg-white h-full transition duration-200 ease-in-out `}
>
{/* //!IMAGE */}
<span
className={` w-full h-full bg-center bg-contain bg-no-repeat `}
style={
{
// backgroundImage: `url(${image.dataURL})`
}
}
>
<img src={image.dataURL} width="200" alt="" />
</span>
{/* OVERLAY */}
<div className="z-10 overlay absolute w-full h-full flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity duration-300 bg-black bg-opacity-50">
<div className="flex">
<button
type="button"
onClick={() => onImageRemove(index)}
title=""
>
<span className="w-5 h-5">
<button>delete</button>
</span>
</button>
</div>
</div>
</div>
</div>
))}

How to design a collapse component in reactjs with tailwind css

I am trying to create a collapse section component design in react JS with TailwindCSS, in the component, there will be an edit button to open and close the info section, and the edit info button will be next to the input field. The design is done(screenshot 1) but some adjustments are needed in the styling, which causes confusion about how to put the edit info button and info section code in a single component so that the design looks like the below screenshot.
<div>
<div
className={[
'flex',
'justify-between',
'relative',
'lg:px-20',
'xl:px-40',
'py-6',
].join(' ')}
>
<div className="flex items-center sm:flex-col lg:flex-row ">
<div className="flex flex-col lg:flex-row md:flex-row items-center">
<StreamTokenInputField />
//EDIT INFO BUTTON
<button
className="button"
onClick={() => setIsCollapseTrue(!isCollapseTrue)}
>
{i18n.t(Edit Info)}
{isCollapseTrue ? (
<IoIosArrowUp className="font-extrabold ml-2 text-lg" />
) : (
<FiChevronDown className="font-extrabold ml-2 text-lg" />
)}
</button>
</div>
</div>
</div>
// INFO SECTION
<div
className={[
'container',
'mx-auto',
'md:w-full',
'w-96',
'py-8',
'my-4',
'lg:py-20',
'lg:px-40',
'bg-skin-card',
'rounded-3xl',
!isCollapseTrue && 'hidden',
].join(' ')}
>
{/* OTHER CODES */}
</div>
</div>
screenshot 1
I have tried like this
<div
className={[
'flex',
'justify-between',
'relative',
'lg:px-20',
'xl:px-40',
'py-6',
].join(' ')}
>
<div className="flex items-center sm:flex-col lg:flex-row ">
<div className="flex flex-col lg:flex-row md:flex-row items-center">
<StreamTokenInputField />
<button
className={[
'flex',
'button',
'button-green',
'xl:px-6',
'md:px-2',
'lg:px-10',
'lg:my-6',
'md:my-6',
'mx-4',
'justify-center',
'uppercase',
'font-semibold',
].join(' ')}
onClick={() => setIsCollapseTrue(!isCollapseTrue)}
>
{i18n.t(buttonName)}
{isCollapseTrue ? (
<IoIosArrowUp className="font-extrabold ml-2 text-lg" />
) : (
<FiChevronDown className="font-extrabold ml-2 text-lg" />
)}
</button>
<div
className={[
'container',
'mx-auto',
'md:w-full',
'w-96',
'py-8',
'my-4',
'lg:py-20',
'lg:px-40',
'bg-skin-card',
'rounded-3xl',
!isCollapseTrue && 'hidden',
].join(' ')}
>
{/* OTHER CODES */}
</div>
</div>
</div>
</div>
However, the output shows like this: I want the info section to appear below the edit info button just like in the screenshot above.
If you want info section appear below button. You don't need lg:flex-row
So your code will be
<div className="flex items-center flex-col">
[...]
</div>
Also tailwind uses mobile-first breakpoint system. So you don't need to add sm: to sm:flex-col.
Same here <div className="flex flex-col lg:flex-row md:flex-row items-center">. You don't need to add lg:flex-row once you added md:flex-row. Any larger size than md will follow same class defined as md:
More information can be found here mobile-first-breakpoint-system
You can make use of the detail tag. Modify the animation anyhow you want or perhaps hide the default icon and specify a custom icon.
<details className="open:bg-white border-b open:ring-1 open:ring-black/5 open:shadow-lg p-6 rounded-lg transform-gpu delay-75 duration-100 ease-in-out ">
<summary className="leading-6 text-slate-900 dark:text-white font-semibold select-none">
Why do they call it Ovaltine?
</summary>
<div className="mt-3 text-sm leading-6 text-slate-600 dark:text-slate-400">
<p>The mug is round. The jar is round. They should call it Roundtine.</p>
</div>
</details>

How to distinguish user clicks between different buttons?

I have rendered 3 plan options from an object array I get from the backend.
If the plan being rendered is
cheaper, the user's subscription then its corresponding button would say downgrade,
costlier, the button would say upgrade, if it is the same then the button would say current.
This logic for rendering is working fine.
But when users click on a button I am not able to identify which option they clicked. I need to update the handle click events based on the plan being rendered.
Currently, I am iterating through the tiers map, and for each tier, I am rendering the button with appropriate text. The function to display the text basically checks for the tiered pricing and returns upgrade or downgrade or current plan. I need a way to update the handle click just like the text.
How can I dynamically update the handle click events based on the tier being rendered?
const [confirmation, setConfirmation] = useState(false);
const handleConfirmation = () => {
setConfirmation(true);
console.log('button is clicked');
};
<div className='px-8 lg:px-24 grid grid-cols-3 gap-8'>
{tiers.map((tier) => (
<div
key={tier.title}
className='relative p-8 bg-white border border-gray-200 rounded-2xl shadow-sm flex flex-col'
>
<div className='flex-1'>
<h3 className='text-xl font-semibold text-gray-900'>{tier.title}</h3>
{tier.value ? (
<p className='absolute top-0 py-1.5 px-4 bg-grays-600 rounded-full text-xs font-semibold uppercase tracking-wide text-white transform-translate-y-1/2'>
VALUE PLAN
</p>
) : tier.recommended ? (
<p className='absolute top-0 py-1.5 px-4 bg-blue-500 rounded-full text-xs font-semibold uppercase tracking-wide text-white transform -translate-y-1/2'>
RECOMMENDED PLAN
</p>
) : (
''
)}
<Button
text={determineCTAText(tier)}
size='fullwidth'
variant='primary'
className='m-auto mt-8 text-center block'
handleClick={handleConfirmation}>
</Button>
}
You can modify the handleConfirmation function to receive a tier object and return a function instead. This will create a closure over the tier value and let you access it when the button's callback is called.
const handleConfirmation = (tier) => {
// Returns the callback function that will be called, with the specific `tier` value in scope
return () => {
setConfirmation(true);
// You can access any `tier` property here
console.log(`button for tier ${tier.title} was clicked`);
};
}
return (
<div className='px-8 lg:px-24 grid grid-cols-3 gap-8'>
{tiers.map((tier) => (
{/* Omitted rest of the JSX for simplicity */}
<Button
text={determineCTAText(tier)}
size='fullwidth'
variant='primary'
className='m-auto mt-8 text-centerblock'
handleClick={handleConfirmation(tier)}>
</Button>
}
</div>
)

I'm trying to build a dropdown when cursor hovers on a navbar element pointing to that element with an arrow up

I am using NextJS and Tailwind CSS. I already built the dropdown but I have an issue building the arrow pointing to the specific element. I built the arrow in a previous version using a div rotated at 45deg but then I couldn't make my dropdown take the full screen. Now I have it full screen and I'm wondering how to make the arrow (div) point to the specific element I'm hovering on. I made sure to hide some details about the website because of confidentiality matters. Here's what I'm trying to achieve:
Here's the code I have so far:
import React from 'react'
import { navLinks } from '../data/navdata'
const DropdownHover = ({ index }) => {
return (
<div className="group-hover:block absolute left-0 w-full hidden text-dark-gray mt-8 bg-pink" aria-labelledby="dropdownButton">
<div className="justify-center">
<div className="flex py-10 px-20 text-sm justify-between">
{navLinks[index].hover.map((link, index) => {
return (
<div className="" key={index}>
<p className="text-navbar-gray py-2 uppercase font-semibold">{link.name}</p>
{link.links.map((sublink, index) => {
return(<p className="" key={index}><a className="bg-pink hover:text-red py-2 block whitespace-no-wrap" href={sublink.path}>{sublink.name}</a></p>)
})}
</div>
)
})}
</div>
<div className="flex text-sm px-20 py-10">
<div className='pr-40'>
<p className="text-navbar-gray py-2 uppercase font-semibold">Dummy Data</p>
<p className=""><a className="bg-pink hover:text-red py-2 block whitespace-no-wrap" href="#">Dummy Data</a></p>
<p className=""><a className="bg-pink hover:text-red py-2 block whitespace-no-wrap" href="#">Dummy Data</a></p>
<p className=""><a className="bg-pink hover:text-red py-2 block whitespace-no-wrap" href="#">Dummy Data</a></p>
</div>
<div>
<p className="text-navbar-gray py-2 uppercase font-semibold">Dummy Data</p>
<p className=""><a className="bg-pink hover:text-red py-2 block whitespace-no-wrap" href="#">Dummy Data</a></p>
<p className=""><a className="bg-pink hover:text-red py-2 block whitespace-no-wrap" href="#">Dummy Data</a></p>
<p className=""><a className="bg-pink hover:text-red py-2 block whitespace-no-wrap" href="#">Dummy Data</a></p>
</div>
</div>
</div>
</div>
)
}
export default DropdownHover
in CSS you can simply manipulate left property of the arrow if its positioned absolute:
li :nth-child(1):hover .arrow{
left:20rem;
}
li :nth-child(2):hover .arrow{
left:30rem;
}
but if you want to make it completely responsive, first get OffsetX of every li element then add an event listener on mouseclick.
for a better understanding you can check the following project on code pen.
https://codepen.io/piyushpd139/pen/gOYvZPG

take out name of dynamically created cards react

I had created some cards from an array of objects, now I want to show popup data for each card differently.
here is my code of printing
{data.carData.map( single=>(<li>{ <div onClick={handleClick}><Card single={single}/></div>} </li>))}
for this, I want the card name onClick event, but when I pass anything in the handleClick react throw an error of too many re-renders.
if I do the same onClick event inside the card component and log it prints all the cards names one by one
here is what is inside the card:
function Card({single}) {
const [flowName, setflowName] = useState();
const handleClick=(e)=>{
setflowName(e);
return
}
console.log("loging name:",flowName);
return (
<div className="main mb-2 z-0" onClick={handleClick(e=>single?.flowName)} >
<div className=" inner_main_container pt-4 px-8 bg-white shadow-lg rounded-lg ">
<div className="flex justify-between">
<div className="flex flex-row align-center">
</div>
{single?.badge.map(badge=>(
<div className={"badge"} key={badge}>
<span className={badge==="Growth"?" badgeClrG":(badge==="Content"?"content":"badgeClrO")} key={badge}>{badge}</span>
</div>
) )}
</div>
<div className="flex justify-center">
<img src={single?.image} alt={single?.flowName} />
</div>
<div >
<div className={"mt-2 flex justify-center"}>
<h1>{single?.flowName}</h1>
</div>
</div>
</div>
</div>
) } export default Card
I suspect you're doing this when you're trying to pass a value:
single=>(<li>{ <div onClick={handleClick(value)}><Card single={single}/></div>} </li>))}
Every time this is rendered, the function is invoked straight away, which causes another render and so on...
The solution is to wrap your handleClick call in another function:
single=>(<li>{ <div onClick={()=>handleClick(value)}><Card single={single}/></div>} </li>))}

Categories

Resources