How to render SVG component using props? - javascript

I'm trying to render an SVG using props of a function. Any help on how to achieve this?
Here is the code:
import React from "react";
import Camera from "../icons/Camera.svg";
export default function Button(props) {
console.log(props.icon);
return (
<button
type="button"
className="mx-auto mb-2.5 flex items-center gap-2 rounded-lg border border-gray-300 px-3.5 py-2 text-sm font-medium text-gray-700 drop-shadow-sm"
>
{props.icon} /* SVG ICONS GOES HERE*/
{props.label}
</button>
);
}

Could try:
import React from "react";
import Camera from "../icons/Camera.svg";
export default function Button({icon, label}) {
return (
<button
type="button"
className="mx-auto mb-2.5 flex items-center gap-2 rounded-lg border border-gray-300 px-3.5 py-2 text-sm font-medium text-gray-700 drop-shadow-sm"
>
{icon && <img src={Camera} alt={label} />}
</button>
);
}

Here is how you can add an Icon
https://codesandbox.io/s/react-playground-forked-x4vsge?file=/index.js
However, a few things when you are creating these components.
If the Icons source is an API (CMS) then you should have some predefined icons and should have your icons library build and pass just icon names.
If you have very limited icons build SVG icons as components and use them

When you use your Button component just use the SVG as a JSX
import Camera from "../icons/Camera.svg";
export default function App() {
return <Button icon={<Camera />} label="Click Me" />;
}

Related

Focus state shouldn't change, React.JS + TailwindCSS

I have a question related to tailwindcss and React. I have a property that if a <NavItem /> is focused then the background and the text will change. <LanguageSwitcher /> is another component, and if it is focused, I want it to not change focus state of other items in navbar. <LanguageSwitcher /> doesn't affect URL.
Here's my code for item in navigation bar and images of how <LanguageSwitcher /> affects focus state of other items:
import React from "react";
import { Link } from "react-router-dom";
interface Props {
title: string;
url: string;
icon: JSX.Element;
}
const NavItem: React.FC<Props> = ({ title, url, icon }) => {
//https://tailwindcss.com/docs/hover-focus-and-other-states#pseudo-classes
return (
<Link
to={url}
className="text-center items-center flex-col justify-center font-medium px-1 text-white capitalize hover:font-semibold select-none group"
>
<div className="flex items-center justify-center mb-1 rounded-3xl px-2 py-1 group-hover:bg-btnHover group-focus:bg-btnActive group-focus:text-textActive transition-colors duration-75">
{icon}
</div>
<span>{title}</span>
</Link>
);
};
export default NavItem;
Is it possible? If so, how can I do it, maybe with useLocation from react-router-dom or directly with tailwindcss?
"Focus" generally means the UI element the user can currently interact with. I think you are conflating "focus state" with "active state". I'm guessing you want the Link to remain "active" while other UI elements have, or might not have, "focus", and are being interacted with. Switch to using the NavLink component and conditionally apply the "active" state CSS classname.
Here's an example*:
import React from "react";
import { NavLink } from "react-router-dom";
interface Props {
title: string;
url: string;
icon: JSX.Element;
}
const NavItem: React.FC<Props> = ({ title, url, icon }) => {
//https://tailwindcss.com/docs/hover-focus-and-other-states#pseudo-classes
return (
<NavLink
to={url}
className="text-center items-center flex-col justify-center font-medium px-1 text-white capitalize hover:font-semibold select-none group"
>
{({ isActive }: { isActive: boolean }) => (
<>
<div
className={
[
"flex items-center justify-center mb-1 rounded-3xl px-2 py-1 group-hover:bg-btnHover transition-colors duration-75"
isActive ? "group-focus:bg-btnActive group-focus:text-textActive" : null
]
.filter(Boolean)
.join(" ")
}
>
{icon}
</div>
<span>{title}</span>
</>
)}
</NavLink>
);
};
export default NavItem;
*Note: I've just guessed/plucked the CSS classnames that looked like they were related to the "focus/active" CSS styling, you will want to double-check the actual classes in your implementation.

React web app : Mapping <Popover.Button> and <Popover.Panel> separately

I want to render this popover panel
<Popover.Panel className="absolute z-10 inset-x-0 transform shadow-xl pb-2 w-max">
<div>
<Dropdown subCategory={mainCatArray}/>
</div>
</Popover.Panel>
same as this popover button render in this below code. Because I want to render a separate popover panel with the popover button, please help me to do that. (I cannot render in one categoryObj because I want to render this popover button horizontally, but if I use a single map for the entire popover component popover button renders it vertically.
(Simply What I want to do is I want to render one <Popover.Panel> to one <Popover.Button>)
import { Fragment, useEffect, useState } from "react";
import { Popover, Transition } from "#headlessui/react";
import Dropdown from "./dropdown";
import { categoryObj } from "../../components/item/categoriesobj";
function classNames(...classes) {
return classes.filter(Boolean).join(" ");
}
const CatDropDown = () => {
const [mainCatArray, setMainCatArray] = useState([]);
return (
<>
<Popover className="z-0 relative">
{({ open }) => (
<>
<div>
<div className=" z-10 bg-white">
<div className="w-2/3 flex gap-5 px-4 py-6 sm:px-6 lg:px-8 ">
{categoryObj.map((singleMainCategory) => (
<Popover.Button
className={classNames(
open ? "text-gray-900" : "text-gray-900",
"group bg-white rounded-md inline-flex items-center text-sm font-susty focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-white"
)}
>
<span
key={singleMainCategory.id}
onClick={() => {
setMainCatArray(singleMainCategory.subCategory);
}}
>
<span>{singleMainCategory.name}</span>
</span>
</Popover.Button>
))}
</div>
</div>
</div>
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="opacity-0 -translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 -translate-y-1"
>
<Popover.Panel className="absolute z-10 inset-x-0 transform shadow-lg pb-2">
<div>
<Dropdown subCategory={mainCatArray}/>
</div>
</Popover.Panel>
</Transition>
</>
)}
</Popover>
</>
);
};
export default CatDropDown;

ReactJS perform a function before going to the route of link after clicking on a link

I've created my navbar such that, when I open it in a mobile device, body gets overflow-hidden class applied. When the navbar is closed, overflow-hidden is removed from body.
Problem encountered: The function which removes overflow-hidden from body doesn't get run when I click on the link and it navigates to another view. Which means, on the new page, body has overflow hidden so the user can't really scroll anywhere. Opening and closing the navbar fixes that since it removes overflow hidden from the body.
I'm using Laravel Breeze with ReactJS.
// NavLink.js
import React, { useEffect, useCallback } from "react";
import { useNavigate } from "react-router-dom";
import { Link } from "#inertiajs/inertia-react";
export default function NavLink({ href, active, children, className = "" }) {
return (
<Link
href={href}
className={
(active
? "inline-flex items-center px-1 pt-1 border-b-2 border-[#ed8686] text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-[#E05D5D] transition duration-150 ease-in-out dark:text-gray-400"
: "inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 transition duration-150 ease-in-out dark:text-gray-300 dark:hover:text-gray-500 dark:hover:border-gray-300 dark:focus:outline-none dark:focus:text-gray-500 dark:focus:border-gray-300") +
` ${className}`
}
>
{children}
</Link>
);
}
// NavBar.js
import ApplicationLogo from "#/Components/ApplicationLogo";
import DarkModeButton from "#/Components/DarkModeButton";
import NavLink from "#/Components/NavLink";
import { Link } from "#inertiajs/inertia-react";
import React, { useEffect, useRef, useState } from "react";
import { MdClose, MdMenu } from "react-icons/md";
const NavBar = ({ dark, setDark }) => {
const [scrollTop, setScrollTop] = useState("");
const navRef = useRef();
const mobileNavRef = useRef();
const toggleNav = (e) => {
e.preventDefault();
const navBar = mobileNavRef.current;
navBar.classList.toggle("active");
document.body.classList.toggle("overflow-hidden");
};
const hideNav = (e) => {
e.preventDefault();
const navBar = mobileNavRef.current;
navBar.classList.remove("active");
document.body.classList.remove("overflow-hidden");
};
// TODO: Too many renders, change to remove extra renders
useEffect(() => {
if (scrollTop >= 80) {
navRef.current.classList.add("scrolled");
} else {
navRef.current.classList.remove("scrolled");
}
const onScroll = (e) => {
setScrollTop(e.target.documentElement.scrollTop);
};
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, [scrollTop]);
return (
<>
<nav className="main-nav" ref={navRef}>
<Link to="/" className="h-full w-auto block py-4 mx-auto">
<ApplicationLogo className="h-full w-auto" dark={!!dark} />
</Link>
<MdMenu
className="fixed right-4 top-4 text-lg cursor-pointer lg:hidden dark:text-white"
onClick={(e) => toggleNav(e)}
/>
<ul className="hidden lg:flex flex-row items-center justify-center">
<li>
<NavLink
href={route("home")}
active={route().current("home")}
children="Home"
className="text-lg my-2 mx-4"
/>
</li>
<li>
<NavLink
href={route("gallery")}
children="Gallery"
active={route().current("gallery")}
className="text-lg my-2 mx-4"
/>
</li>
<li>
<NavLink
// href={route("about")}
children="About"
active={route().current("about")}
className="text-lg my-2 mx-4"
/>
</li>
<li className="cursor-pointer flex items-center justify-center">
<DarkModeButton dark={dark} setDark={setDark} />
</li>
</ul>
</nav>
<ul
className="w-full md:w1/2 lg:w-1/4 flex items-center justify-center fixed inset-0 bg-white h-full flex-col mobile-nav lg:hidden z-50 dark:bg-gray-900"
ref={mobileNavRef}
>
<MdClose
className="absolute top-4 right-4 text-lg cursor-pointer lg:hidden dark:text-white"
onClick={(e) => hideNav(e)}
/>
<Link to="/" onClick={(e) => hideNav(e)}>
<ApplicationLogo
className="h-32 mb-2 w-auto lg:hidden"
dark={dark}
/>
</Link>
<li>
<NavLink
href={route("home")}
active={route().current("home")}
children="Home"
className="text-lg my-2 mx-4"
onClick={(e) => hideNav(e)}
/>
</li>
<li>
<NavLink
href={route("gallery")}
children="Gallery"
active={route().current("gallery")}
className="text-lg my-2 mx-4"
onClick={(e) => hideNav(e)}
/>
</li>
<li>
<NavLink
// href={route("about")}
children="About"
active={route().current("about")}
className="text-lg my-2 mx-4"
onClick={(e) => hideNav(e)}
/>
</li>
<li className="cursor-pointer">
<DarkModeButton dark={dark} setDark={setDark} />
</li>
</ul>
</>
);
};
export default NavBar;
I've made the function hideNav() which has e.preventDefault() but I'm guessing it doesn't work because it doesn't apply directly on the Link tag? I'm not sure
Unsure how to tackle this problem
I've thought about using useNavigate but I am not using react router dom, inertiajs is handling the routing for me, I just set the routes in laravel web.php file.
I've come up with a solution,
It's not the ideal solution but this is what I can think of
I added a prop to NavLink component and based on if that prop is true, I add onclick event handler that removes overflow class from body
import React from "react";
import { Link } from "#inertiajs/inertia-react";
import { Inertia } from "#inertiajs/inertia";
export default function NavLink({
href,
active,
children,
className = "",
mobileLink = false,
}) {
return (
<Link
href={href}
className={
(active
? "inline-flex items-center px-1 pt-1 border-b-2 border-[#ed8686] text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-[#E05D5D] transition duration-150 ease-in-out dark:text-gray-400"
: "inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 transition duration-150 ease-in-out dark:text-gray-300 dark:hover:text-gray-500 dark:hover:border-gray-300 dark:focus:outline-none dark:focus:text-gray-500 dark:focus:border-gray-300") +
` ${className}`
}
onClick={
mobileLink
? (e) => {
e.preventDefault();
document.body.classList.remove("overflow-hidden");
Inertia.visit(href);
}
: undefined
}
>
{children}
</Link>
);
}

Error: Expected `onClick` listener to be a function, instead got a value of `object` type

I am implementing a modal in my react app but face the weird error, Error: Expected `onClick` listener to be a function, instead got a value of `object` type.. What's confusing here is the fact that I have implemented the modal in another part of the application using the same logic and it doesn't produce such error.
Below are important snippets of the code.
index.js
import React, { useState } from "react";
import ProfileSave from "../../components/modal/profileSave";
const test = () => {
const [saveModal, setSaveModal] = useState(false);
const [save, setSave] = useState(false);
return (
<>
<button className="w-[165px] h-[60px] text-center px-4 py-2 text-sm text-white bg-[#3A76BF] rounded-md font-bold text-[16px]" onClick={saveShowModal}>
Save
</button>
{saveModal && (
<ProfileSave
open={saveModal}
handleClose={() => setSaveModal(!saveModal)}
handleSave={handleSave}
/>
)}
</>
export default test
profileSave.js
import React from "react";
const ProfileSave = (open, handleClose, handleModal) => {
return open ? (
<div className="fixed inset-0 z-10 overflow-y-auto">
<div
className="fixed inset-0 w-full h-full bg-black opacity-40"
onClick={handleClose}
></div>
</div>
) : (
""
);
};
export default ProfileSave;
You neet to extract the props that you component recive
Try this :
import React from "react";
const ProfileSave = ({open, handleClose, handleModal}) => {
return open ? (
<div className="fixed inset-0 z-10 overflow-y-auto">
<div
className="fixed inset-0 w-full h-full bg-black opacity-40"
onClick={handleClose}
></div>
</div>
) : (
""
);
};
export default ProfileSave;

How to hide a custom toast notification with Tailwind css and react

Im working on a custom toast notification. But the hard part to me is I've not been able to hide the toast notification correctly. When I make a click to show it the toast has an animation to the left the problem is when the toast is gonna hide just disapear and it does not slide to the right. Im working with react and tailwind css. Here is the code. also leave a link to the case. https://codesandbox.io/s/toast-notification-ucx2v?file=/src/components/toastNotification.jsx
I hope you can help me guys.
-----App component-----
import { useEffect, useState } from "react";
import ToastNotification from "./components/toastNotification";
import "./styles.css";
export default function App() {
const [showToast, setShowToast] = useState(false);
const addToCart = () => {
setShowToast(true);
};
useEffect(() => {
setTimeout(() => {
setShowToast(false);
}, 3000);
}, [showToast]);
return (
<div className="App">
<ToastNotification showNotification={showToast} />
<button
className="w-1/2 p-2 flex items-center justify-center rounded-md bg-indigo-400 text-white"
type="submit"
onClick={addToCart}
>
Add to cart
</button>
</div>
);
}
-----Toast component-----
import React, { useEffect, useState } from "react";
const ToastNotification = ({ showNotification }) => {
useEffect(() => {
setTimeout(() => {}, 5000);
}, []);
return (
<>
{showNotification === false ? null : (
<div className="static z-20">
<div
className={`${
showNotification
? "animate__animated animate__slideInRight absolute top-16 right-4 flex items-center text-white max-w-sm w-full bg-green-400 shadow-md rounded-lg overflow-hidden mx-auto"
: "animate__animated animate__slideOutRight absolute top-16 right-4 flex items-center text-white max-w-sm w-full bg-green-400 shadow-md rounded-lg mx-auto"
}`}
>
<div class="w-10 border-r px-2">
<i class="far fa-check-circle"></i>
</div>
<div class="flex items-center px-2 py-3">
<div class="mx-3">
<p>add to car</p>
</div>
</div>
</div>
</div>
)}
</>
);
};
export default ToastNotification;

Categories