TailwindCSS: Dropdown not working with group and group-focus in Nextjs - javascript

I want to display an array once a button is clicked. By default it is invisible. Just like in drop-downs in menu bars. I'm using group and group-focus. The array is getting invisible but on button click it's not reappearing!
<div className="group">
<button className="pointer-events-auto">Ingredients</button>
<ul className="invisible group-focus:visible grid sm:grid-cols-3 text-justify sm:text-sm text-xs grid-cols-2 gap-x-3 gap-y-1 list-decimal list-inside">
{menu.ingredients.map(item => <li className="" key={item}>{ item }</li>)}
</ul>
</div>
I've set the visibility for group-focus in next.config.js but still it's not working:
module.exports = {
...,
variants: {
extend: {
backgroundColor: ['group-focus'],
visibility: ['hover', 'focus', 'group-focus'],
},
},
plugins: [],
}

import React, { useState } from 'react'
function component() {
const [show, setShow] = useState(false)
const handleShow = () => {
(show ? setShow(false) : setShow(true))
}
return (
<div>
<div className="group">
<button onClick={handleShow} className="pointer-events-auto">Ingredients</button>
<div className={show ? "block" : "hidden"}>
<ul className="invisible group-focus:visible grid sm:grid-cols-3 text-justify sm:text-sm text-xs grid-cols-2 gap-x-3 gap-y-1 list-decimal list-inside">
{menu.ingredients.map(item => <li className="" key={item}>{item}</li>)}
</ul>
</div>
</div>
</div>
)
}
export default component
This will work fine.

Related

Make div with react components as children disappear if I click outside of it

I want to make a form that disappears if I click outside of it.
Form Component:
const CreateTaskPopup = (props) => {
const ref = query(collection(db, "tasks"));
const mutation = useFirestoreCollectionMutation(ref);
useEffect(() => {
const closeTaskPopup = (event) => {
if (event.target.id != "addForm") {
props.setTrigger(false)
}
}
document.addEventListener('click', closeTaskPopup);
return () => document.removeEventListener('click', closeTaskPopup)
}, [])
return (props.trigger) ? (
<>
<div className="bg-darkishGrey my-4 p-1 mx-3 ml-16 cursor-pointer block"
id="addForm">
<div className="flex justify-between">
<div className="flex break-all items-center ">
<Image src={fileIcon} className="w-6 mx-2"/>
<div>
<Formik
initialValues={{
taskName: "",
taskIsDone: false,
parentId: props.parentId ? props.parentId : "",
hasChildren: false,
}}
onSubmit={(values) => {
mutation.mutate(values);
props.setTrigger(false);
}}
>
<Form>
<div className="">
<TextInput
placeholder="Type a name"
name="taskName"
type="text"
/>
</div>
</Form>
</Formik>
</div>
</div>
<div className="flex items-center">
</div>
</div>
</div>
</>
) : null
}
export default CreateTaskPopup
Text Input Component:
import { useField } from "formik";
const TextInput = ({ label, ...props}) => {
const [field, meta] = useField(props);
return (
<div>
<label id="addForm" className="text-lightestGrey text-xl block"
htmlFor={props.id || props.name}>
{label}
</label>
<input id="addForm" className="bg-darkishGrey text-xl text-almostWhite my-2
outline-none w-10/12 rounded-sm p-1 mx-3" {...field} {...props} />
{meta.touched && meta.error ? <div>{meta.error}</div>: null}
</div>
);
};
export default TextInput;
I tried giving an id to the elements inside it but it's not the best solution as it has components from the Formik library to which I can't assign an id. I don't know what would be the best solution for this problem.

How to get the name of the user that was clicked and send it to another component?

I'm having a problem that I can't solve. I have a component that is currently rendering the users that are in my database, which calls CarouselUsers.jsx, so far so good, it is showing correctly.
But my goal is that after I click on one of these users that were listed, his name appears in a sidebar, which is in another component, but I am not able to do that, can you help me?
CarouselUsers.jsx
import React, { useState, useEffect } from 'react';
import * as Styled from './style.jsx';
import {
collection,
getDocs,
} from "firebase/firestore";
import { Swiper, SwiperSlide } from "swiper/react";
import { db } from '../../Data/Firebase.jsx';
import "swiper/css";
import euTeste from '../../assets/teste.jfif'
import SideBarProfile from '../../components/SideBarProfile/SideBarProfile.jsx';
export default function CarouselUsers() {
const [profile, setProfile] = useState(false)
const openProfile = () => {
setProfile(profile => !profile)
}
// USERS IN THE DB
const [users, setUsers] = useState([])
const usersCollectionRef = collection(db, "usuarios")
useEffect(() => {
const getUsers = async () => {
const data = await getDocs(usersCollectionRef);
setUsers(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
};
getUsers();
}, []);
// USERS IN THE DB
return (
<>
<Styled.CarouselUsers>
{/* MEMBROS CARROSEL */}
<div className="boxMembros">
<div className="titulo">
<h6>Membros</h6>
</div>
<Swiper
spaceBetween={10}
slidesPerView={3}
>
{users.map((user) => {
return (
<>
<SwiperSlide>
<div className="box"style={{ background: `linear-gradient(to bottom,rgba(0, 0, 0, 0.4) 0,rgba(0,0,0,.6) 100%),url(${euTeste})` }} onClick={openProfile} key={user.nome}>
<div className="infoBottom">
<div className="info">
{/* GET THE USERNAME */}
<h6>{user.nome}</h6>
{/* GET THE USERNAME */}
</div>
</div>
</div>
</SwiperSlide>
</>
);
})}
</Swiper>
</div>
{/* MEMBROS CARROSEL */}
</Styled.CarouselUsers>
<SideBarProfile profile={profile} openProfile={openProfile} />
</>
)
}
SideBarProfile.jsx
import React from 'react'
import { XCircle,WhatsappLogo } from "phosphor-react";
import * as Styled from './style.jsx';
export default function SideBarProfile({openProfile,profile}) {
return (
<Styled.SideBarProfile>
<div className={profile ? 'col-md-3 boxLeftWrapper open' : 'col-md-3 boxLeftWrapper close'} profile={profile}>
<div className="boxAll">
<div className="header d-flex justify-between align-items-center">
<div className="titulo">
<h1>Perfil</h1>
</div>
<div className="close">
<button onClick={openProfile}>
<XCircle/>
</button>
</div>
</div>
<div className="boxBodyUser text-left">
<div className="boxThis">
<div className="foto">
<img alt="Usuário" className='img-fluid ativo' />
</div>
<div className="nome text-center">
<h5>{/* SHOW USERNAME */}</h5>
</div>
<div className="status ativo">
<span>Ativo</span>
</div>
<div className="ministerios">
<ul className="pl-0 list-none mb-0">
<li>Teatro</li>
<li>Mídias Sociais</li>
</ul>
</div>
<div className="boxContato mt-5">
<div className="whatsapp d-flex items-center justify-center gap-2">
<WhatsappLogo/>
<span>Mensagem</span>
</div>
</div>
</div>
</div>
</div>
</div>
</Styled.SideBarProfile>
)
}
You can add an onClick event in your CarouselUsers component that grab the Inner Text in <h6>{user.nome}</h6> and pass it as props to SideBarProfile component .
like this :
CarouselUsers.jsx :
export default function CarouselUsers() {
const [profile, setProfile] = useState(false)
const [selectedUser, setSelectedUser] = useState("")
const handleClick = (event) => {
setSelectedUser(event.target.innerText);
}
// rest of your code
return (
......
{/* GET THE USERNAME */}
<h6 onClick={handleClick} >{user.nome}</h6>
{/* GET THE USERNAME */}
.... rest of your code
<SideBarProfile profile={profile} openProfile={openProfile}
setSelectedUser = {setSelectedUser} />
)
}
SideBarProfile.jsx :
export default function SideBarProfile({openProfile,profile, setSelectedUser}) {
return (
......
<div className="nome text-center">
<h5>{setSelectedUser}</h5>
</div>
....
)

How do I use setstate on multiple and one wont affect the other?

I am creating Accodion which will have multiple items with TITLE and BODY.
When a title is clicked, I want the setShow to true. But when Item A is true, I don't want item B to be true at same time. I will be fetching those data from the Server and I don't know how my items i will have in the future.
const Accordion = ({ title, text }) => {
const [show, setShow] = React.useState(false);
return (
<>
<div className="accordion-wrapper">
<div
onClick={() => setShow(!show)}
className="accordion-title cursor-pointer flex flex-row flex-1 justify-between items-center"
>
<div className="title-text">
<h2>{title}</h2>
</div>
<div className="title-icon"></div>
</div>
<div className="accordion-content">{text}</div>
</div>
</>
);
};
export default Accordion;

How to pass a state from component to a parent - the App.js (click outside to close navbar)

I would like to pass a state from component Navbar.js to the App.js to do when I click outside the SideMenu() in closes the navbar. I passed the State props from parent to a child Navbar.js and it doesn't close when I click outside Navbar
-Defined const [isSideMenuOpen, setIsSideMenuOpen] = useState(false); in App.js
-Added props to pass to a child component <Navbar setIsSideMenuOpen={setIsSideMenuOpen} isSideMenuOpen={isSideMenuOpen} />
-Destructuring setIsSideMenuOpen and isSideMenuOpen in Navbar.js (check the code below)
App.js
import "../styles/globals.css";
import Navbar from "../comps/Navbar";
import { useState, useEffect, useRef } from "react";
let useClickOutside = (handler) => {
let domNode = useRef();
useEffect(() => {
let maybeHandler = (event) => {
if (!domNode.current?.contains(event.target)) {
handler();
}
};
document.addEventListener("mousedown", maybeHandler);
return () => {
document.removeEventListener("mousedown", maybeHandler);
};
});
return domNode;
};
function MyApp({ Component, pageProps }) {
const [isSideMenuOpen, setIsSideMenuOpen] = useState(false);
let domNode = useClickOutside(() => {
setIsSideMenuOpen(false);
});
return (
<div className="relative">
<Navbar
setIsSideMenuOpen={setIsSideMenuOpen}
isSideMenuOpen={isSideMenuOpen}
domNode={domNode} // NEWLINE! passing to the SideMenu() function
/>
<div ref={domNode}>
<Component {...pageProps} />
</div>
</div>
);
}
export default MyApp;
../components/Navbar.js
import { useState, useEffect, useRef } from "react";
import { HiMenuAlt1, HiOutlineX } from "react-icons/hi";
export default function Navbar({ isSideMenuOpen, setIsSideMenuOpen }) {
const showSideMenu = () => {
isSideMenuOpen ? setIsSideMenuOpen(false) : setIsSideMenuOpen(true);
};
return (
<div className="absolute z-40 w-full h-8 text-cyan-600 flex flex-row justify-between items-center text-2xl ">
<div className="brand-logo text-xl font-bold px-2 font-extrabold text-cyan-600">
Trener
</div>
<ul className="md:flex hidden menu-list text-xl font-bold ">
<li className="menu-list-item px-2">
Strona główna
</li>
<li className="menu-list-item px-2">
O mnie
</li>
<li className="menu-list-item px-2">
Plany treningowe
</li>
</ul>
<button
onClick={() => {
showSideMenu();
}}
className="lg:hidden menu-button"
>
{isSideMenuOpen ? (
<HiOutlineX className="w-8 h-8 px-2 bg-red-600" />
) : (
<HiMenuAlt1 className="w-8 h-8 px-2" />
)}
</button>
{isSideMenuOpen ? SideMenu() : ""}
</div>
);
}
// NEW LINE - Destructuring props domNode
function SideMenu({domNode}) {
return (
<div className="fixed z-20 w-1/2 sm:w-1/4 lg:hidden bg-gray-300 top-8 right-0 p-3" ref={domNode}>
//NEW LINE - assign ref={domNode}
{/* sagsa */}
<ul className="menu-list flex flex-col text-lg font-bold">
<li className="menu-list-item py-2 hover:bg-white hover:text-blue-700">
Strona główna
</li>
<li className="menu-list-item py-2 hover:bg-white hover:text-blue-700">
O mnie
</li>
<li className="menu-list-item py-2 hover:bg-white hover:text-blue-700">
Plany treningowe
</li>
</ul>
</div>
);
}
UPDATE:
I added domNode={domNode} // NEWLINE! passing a prop to the Navbar.js -> SideMenu() function
// NEW LINE - Destructuring props domNode
function SideMenu({domNode}) {
return (
<div className="fixed z-20 w-1/2 sm:w-1/4 lg:hidden bg-gray-300 top-8 right-0 p-3" ref={domNode}>
//NEW LINE - assign ref={domNode}
// (...)
Occured an error:
Unhandled Runtime Error TypeError: Cannot read properties of undefined (reading 'domNode') in function SideMenu({ domNode }) {
That's because you've defined setIsSideMenuOpen in child component which is Navbar.js
What you can do define
const [isSideMenuOpen, setIsSideMenuOpen] = useState(false)
in the App.js and then pass it as props to Navbar. That way you can handle the isSideMenuOpen from both the parent and child
You can't pass the state and it's setter Function form child component to parent component,
There are following approches you can do
Define the state, and its Setter function in App.js and then pass both state and it's setter as a props to Navbar.js and then use them here as a props.
Use State Lift up concept Make a combine parent component and and define
const [isSideMenuOpen, setIsSideMenuOpen] = useState(false);
here and pass this as a props to both component's App.js and Navbar.js.
You can simple use redux to avoid state passing issue.

How would I change the state in the parent component from the child in React?

I have a parent component that contains a modal which I want to show/hide onClick of an element in a child component. How can I now basically call that handler in the parent company from the child component click?
My idea was to somehow provide the handler via the props from the parent to the child:
// parent components
import PlannerDetails from "./PlannerDetails";
import PlannerTools from "./PlannerTools";
import {useState} from "react";
const PlannerWrapper = () => {
const [showTools, setShowTools] = useState(false)
const toolsHandler = () => {
setShowTools(true)
}
return (
<div className="wrapper w-6/6 h-full flex bg-dotted bg-sx-white">
<div className="right-wrapper w-3/6 h-full p-8">
<div className="details-wrapper w-full h-full bg-sx-white-plain rounded-2xl shadow-sx-shadow">
<PlannerDetails/>
</div>
</div>
{showTools ? <PlannerTools/> : null}
</div>
)
}
export default PlannerWrapper
// child component
import imageEdit from "../../assets/images/edit.svg"
const PlannerDetails = (props) => {
return (
<div className="details-wrapper">
<div className="details-head flex border-b-1 border-b-gray">
<div className="text-2xl text-sx-purple-dark p-4">Offer Details</div>
<div className="icon-wrapper flex">
<img src={imageEdit} className="w-4 cursor-pointer"
onClick={props.toolsHandler}/> // <--------- click event here
</div>
</div>
</div>
)
}
export default PlannerDetails
Updated approach
// Parent
import PlannerDetails from "./PlannerDetails";
import PlannerTools from "./PlannerTools";
import {useState} from "react";
const PlannerWrapper = () => {
const [showTools, setShowTools] = useState(false)
const toolsHandler = () => {
console.log('test')
setShowTools(true)
}
return (
<div className="wrapper w-6/6 h-full flex bg-dotted bg-sx-white">
<div className="right-wrapper w-3/6 h-full p-8">
<div className="details-wrapper w-full h-full bg-sx-white-plain rounded-2xl shadow-sx-shadow">
<PlannerDetails toolsHandler={toolsHandler} />
</div>
</div>
{showTools ? <PlannerTools/> : null}
</div>
)
}
export default PlannerWrapper
// Child
import imageEdit from "../../assets/images/edit.svg"
const PlannerDetails = (toolsHandler, ...props) => {
return (
<div className="details-wrapper">
<div className="details-head flex border-b-1 border-b-gray">
<div className="text-2xl text-sx-purple-dark p-4">Offer Details</div>
<div className="icon-wrapper flex">
<img src={imageEdit} className="w-4 cursor-pointer" alt="editTools" onClick={props.toolsHandler}/>
</div>
</div>
</div>
)
}
export default PlannerDetails
You are very close to the answer, You just need to pass the function in your parent component to the child.
like this
<PlannerDetails toolsHandler={toolsHandler} />
the first toolsHandler is the prop property in the child component, the second toolsHandler is the function in parents component.

Categories