Add routing to navbar (react & tailwind) - javascript

import { useState } from "react";
const App = () => {
const [open, setOpen] = useState(true);
const Menus = [
{ title: "Home", src: "0" },
{ title: "Site1", src: "1", gap: true },
{ title: "Site2 ", src: "2" },
{ title: "Site3", src: "3" },
{ title: "Site4", src: "4" }
];
return (
<div className="flex">
<div
className={` ${
open ? "w-72" : "w-20 "
} bg-gray-800 p-5 pt-8 sticky top-0 left-0 h-[930px] duration-300`}
>
<img
src="./src/assets/control.png"
className={`absolute cursor-pointer -right-3 top-9 w-7 border-dark-purple
border-2 rounded-full ${!open && "rotate-180"}`}
onClick={() => setOpen(!open)}
/>
<div className="flex gap-x-4 items-center">
<img
src="./src/assets/logo.png"
className={`cursor-pointer duration-500 ${
open && "rotate-[360deg]"
}`}
/>
<h1
className={`text-white origin-left font-medium text-xl duration-200 ${
!open && "scale-0"
}`}
>
Site
</h1>
</div>
<ul className="pt-6">
{Menus.map((Menu, index) => (
<li
key={index}
className={`flex rounded-MD p-2 cursor-pointer hover:bg-light-white text-gray-300 text-sm items-center gap-x-4
${Menu.gap ? "mt-9" : "mt-2"} ${
index === 0 && "bg-light-white"
} `}
>
<img src={`./src/assets/${Menu.src}.png`} />
<span className={`${!open && "hidden"} origin-left duration-200`}>
{Menu.title}
</span>
</li>
))}
</ul>
</div>
This is the code that I got after following this tutorial: Tutorial
How can I link my other pages with the navbar? So clicking for example Site1 will direct the user to Site1?
The problem is that I can't use tags or hfref in this case and I have no idea how to solve my problem as I'm just learning react.

Multiple things you can do here.
If you want to stick with clickable buttons, you could use React Router and the, for each Menu add an onClick={() => router.push(Menu.title)} (subject to whatever your actual url is).
Otherwise, in a more native way, you could use a tags instead of span tags, which can receive href={Menu.title}. The downside here is that you'd have to style them again, as a tags have browser-specific default styles.

Have a look and read into the React-router: https://reactrouter.com/en/main/getting-started/tutorial
To create a link use the tag instead of the tag

Related

how to target just one dropdown menu instead of all?

I have a react project where I have 3 dropdown menus side by side and upon clicking one they all will toggle instead of just one. I tried to use a dropdown component but I'm not sure I can get it working correctly with my code. Can you show me how to fix this? I have my code uploaded to code sandbox here link to code due note that it will not display on mobile screens yet so you will need to look at it in the full desktop version of the site.
import { useState } from 'react';
import {
iconHamburger,
iconClose,
iconArrowDark,
iconArrowLight,
logo,
} from '../assets';
import { navLinks } from '../constants';
const Navbar = () => {
const [toggle, setToggle] = useState(false);
return (
<nav className='w-full flex py-6 ml-10 justify-between items-center navbar'>
<img src={logo} alt='blogr' className='w-[75px] h-[30px]' />
<ul className='list-none sm:flex hidden ml-10 justify-start items-center flex-1'>
{navLinks.map((nav, index) => (
<li
key={nav.id}
className={`font-overpass
font-normal
text-[12px] ${index === navLinks.length - 1 ? 'mr-0' : 'mr-10'}
text-white`}>
<a
className='float-left'
onClick={() => setToggle((prev) => !prev)}
href={`#${nav.id}`}>
{nav.title}
<img
className='ml-2 mt-1 cursor-pointer float-right w-[9px] h-[6px]'
src={iconArrowLight}
/>
</a>
<div className={`${toggle ? 'hidden' : 'relative'} mr-10`}>
<ul className='list-none mt-10 absolute'>
{nav.links.map((link, index) => (
<li
key={link.name}
className={`font-overpass text-black cursor-pointer ${
index !== nav.links.length - 1 ? 'mb-4' : 'mb-0'}`}>
{link.name}
</li>
))}
</ul>
</div>
</li>
))}
</ul>
</nav>
);
};
export default Navbar;
navlinks
import { iconArrowLight } from "../assets"
export const navLinks = [
{
id: 'product',
title: 'Product',
img: iconArrowLight,
links: [
{
name: 'Overview'
},
{
name: 'Pricing'
},
{
name: 'Marketplace'
},
{
name: 'Features'
},
{
name: 'Integrations'
},
],
},
{
id: 'company',
title: 'Company',
img: iconArrowLight,
links: [
{
name: 'About'
},
{
name: 'Team'
},
{
name: 'Blog'
},
{
name: 'Career'
},
],
},
{
id: 'connect',
title: 'Connect',
img: iconArrowLight,
links: [
{
name: 'Contact'
},
{
name: 'Newsletter'
},
{
name: 'LinkedIn'
},
],
},
]
All the drop list is sharing one toggle state, therefore they open and close at same time.
You could make each drop list a separate component DropList, each will have an individual state of toggle.
It will allow adding more DropList easier, also keeping the code in Navbar cleaner.
Full example: (live modified project: codesandbox)
import { useState } from "react";
import {
iconHamburger,
iconClose,
iconArrowDark,
iconArrowLight,
logo,
} from "../assets";
import { navLinks } from "../constants";
const Navbar = () => {
return (
<nav className="w-full flex py-6 ml-10 justify-between items-center navbar">
<img src={logo} alt="blogr" className="w-[75px] h-[30px]" />
<ul className="list-none sm:flex hidden ml-10 justify-start items-center flex-1">
{navLinks.map((nav, index, arr) => (
<DropList
key={nav.id}
mr={index === arr.length - 1 ? "mr-0" : "mr-10"}
{...nav}
/>
))}
</ul>
</nav>
);
};
export default Navbar;
export const DropList = ({ id, title, links, mr }) => {
const [toggle, setToggle] = useState(false);
return (
<li
className={`font-overpass
font-normal
text-[12px] ${mr}
text-white`}
>
<a
className="float-left"
onClick={() => setToggle((prev) => !prev)}
href={`#${id}`}
>
{title}
<img
className="ml-2 mt-1 cursor-pointer float-right w-[9px] h-[6px]"
src={iconArrowLight}
/>
</a>
<div className={`${toggle ? "hidden" : "relative"} mr-10`}>
<ul className="list-none mt-10 absolute">
{links.map((link, index, arr) => (
<li
key={link.name}
className={`font-overpass text-black ${
index !== arr.length - 1 ? "mb-4" : "mb-0"
}`}
>
{link.name}
</li>
))}
</ul>
</div>
</li>
);
};
You use the same state to toggle all drop downs. Normally I would recommend to create a new component for the drop down to keep the state. However since you specified that you only want one open at any time, I would recommend to change the state to keep the id of the open nav item.
import { useState } from "react";
import { iconArrowLight, logo } from "../assets";
import { navLinks } from "../constants";
const Navbar = () => {
const [openId, setOpenId] = useState(undefined);
return (
<nav className="w-full flex py-6 ml-10 justify-between items-center navbar">
<img src={logo} alt="blogr" className="w-[75px] h-[30px]" />
<ul className="list-none sm:flex hidden ml-10 justify-start items-center flex-1">
{navLinks.map((nav, index) => (
<li
key={nav.id}
className={`font-overpass
font-normal
text-[12px] ${index === navLinks.length - 1 ? "mr-0" : "mr-10"}
text-white`}
>
<a
className="float-left"
onClick={() =>
setOpenId((prev) => (prev === nav.id ? undefined : nav.id)) // If the id is the same as the previous id, close it
}
href={`#${nav.id}`}
>
{nav.title}
<img
className="ml-2 mt-1 cursor-pointer float-right w-[9px] h-[6px]"
src={iconArrowLight}
/>
</a>
<div
className={`${openId !== nav.id ? "hidden" : "relative"} mr-10`}
>
<ul className="list-none mt-10 absolute">
{nav.links.map((link, index) => (
<li
key={link.name}
className={`font-overpass text-black cursor-pointer ${
index !== nav.links.length - 1 ? "mb-4" : "mb-0"
}`}
>
{link.name}
</li>
))}
</ul>
</div>
</li>
))}
</ul>
</nav>
);
};
export default Navbar;

How to add onhover to Combobox from headlessui

This is the component that must be used.
Here is its source code:
import { Fragment, useState } from 'react'
import { Combobox, Transition } from '#headlessui/react'
import { CheckIcon, SelectorIcon } from '#heroicons/react/solid'
const people = [
{ id: 1, name: 'Wade Cooper' },
{ id: 2, name: 'Arlene Mccoy' },
{ id: 3, name: 'Devon Webb' },
{ id: 4, name: 'Tom Cook' },
{ id: 5, name: 'Tanya Fox' },
{ id: 6, name: 'Hellen Schmidt' },
]
export default function Example() {
const [selected, setSelected] = useState(people[0])
const [query, setQuery] = useState('')
const filteredPeople =
query === ''
? people
: people.filter((person) =>
person.name
.toLowerCase()
.replace(/\s+/g, '')
.includes(query.toLowerCase().replace(/\s+/g, ''))
)
return (
<div className="fixed top-16 w-72">
<Combobox value={selected} onChange={setSelected}>
<div className="relative mt-1">
<div className="relative w-full cursor-default overflow-hidden rounded-lg bg-white text-left shadow-md focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-teal-300 sm:text-sm">
<Combobox.Input
className="w-full border-none py-2 pl-3 pr-10 text-sm leading-5 text-gray-900 focus:ring-0"
displayValue={(person) => person.name}
onChange={(event) => setQuery(event.target.value)}
/>
<Combobox.Button className="absolute inset-y-0 right-0 flex items-center pr-2">
<SelectorIcon
className="h-5 w-5 text-gray-400"
aria-hidden="true"
/>
</Combobox.Button>
</div>
<Transition
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
afterLeave={() => setQuery('')}
>
<Combobox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
{filteredPeople.length === 0 && query !== '' ? (
<div className="relative cursor-default select-none py-2 px-4 text-gray-700">
Nothing found.
</div>
) : (
filteredPeople.map((person) => (
<Combobox.Option
key={person.id}
className={({ active }) =>
`relative cursor-default select-none py-2 pl-10 pr-4 ${
active ? 'bg-teal-600 text-white' : 'text-gray-900'
}`
}
value={person}
>
{({ selected, active }) => (
<>
<span
className={`block truncate ${
selected ? 'font-medium' : 'font-normal'
}`}
>
{person.name}
</span>
{selected ? (
<span
className={`absolute inset-y-0 left-0 flex items-center pl-3 ${
active ? 'text-white' : 'text-teal-600'
}`}
>
<CheckIcon className="h-5 w-5" aria-hidden="true" />
</span>
) : null}
</>
)}
</Combobox.Option>
))
)}
</Combobox.Options>
</Transition>
</div>
</Combobox>
</div>
)
}
My question is: is it possible to show some information for an element when we hover over it?
For example, if we modify the input to this one:
const people = [
{ id: 1, name: 'Wade Cooper', info: 'test1' },
{ id: 2, name: 'Arlene Mccoy', info: 'test2' },
{ id: 3, name: 'Devon Webb', info: 'test3' },
{ id: 4, name: 'Tom Cook', info: 'test4' },
{ id: 5, name: 'Tanya Fox', info: 'test5' },
{ id: 6, name: 'Hellen Schmidt', info: 'test6' },
]
When we hover over first element, to show test1, for the second element test2 and so on.
I am not a react expert(/user). But for the Combobox component is 'active' the key word for hover. You have to conditionally render your displayed text/or the whole span like in pseudocode(however you conditionally render stuff in react):
active ? person.info : person.name

Button Function Not calling From Another file in React

I need to call CutomerDashboard.js file's "toggleIsTrucated" function and "isTruncated" to CustomerNotice.js files button onClick and text change places, How can I call that?
(In this customer dashboard file I'm creating a Read function to show some extent of notice text)
import React, {useState,useEffect} from 'react';
import { Input, Row, Col, Button } from 'antd';
import {fetchDashboardMetrics} from "./DashboardApi";
import {items} from "./DashboardItems";
import axios from 'axios';
import CustomerNotice from "./CustomerNotice";
function Read ({children}) {
const text = children;
const [isTruncated, setIsTrucated] = useState(true);
const result = isTruncated ? text.slice(0,90) : text;
function toggleIsTrucated(){
setIsTrucated(!isTruncated);
}
return (
<div>
{result}....
</div>
);
}
const CustomerDashboard = () => {
const [features, setFeatures] = useState(items);
const source = axios.CancelToken.source()
const [notice, setNotice] = useState(<Read>Customer Notice: Optimism Is Invaluable For The Meaningful Life. With A Firm Belief In A Positive Future You Can Throw Yourself Into The Service Of That Which Is Larger Than You Are. -Martin Seligman-</Read>);
const [noticeVisibility, setNoticeVisibility] = useState(true);
useEffect(() => {
fetchDashboardMetrics(features, setFeatures,source.token)
return (() => {
source.cancel();
})
}, []);
return (
<>
<div className='md:pl-8 sm:pl-0'>
<div className='my-5 '>
<p className='mb-8'>My Account - Dashboard Overview</p>
{noticeVisibility && <CustomerNotice notice={notice} setNoticeVisibility={setNoticeVisibility}/>}
</div>
<ul role="list" className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{features.map((feature) => (
<li key={feature.name} className="col-span-1 bg-white rounded-lg shadow divide-y divide-gray-200 relative">
<div className="w-full flex items-center justify-between p-6 space-x-6">
<div className="flex-1 truncate">
<div className="flex items-center space-x-3 justify-between">
<h3 className="text-gray-900 text-lg truncate">{feature.name}</h3>
{feature.isNew && (
<div className="absolute -top-2 -right-2 p-1 px-4 text-white text-sm bg-red-500">
New
</div>
)}
</div>
</div>
</div>
<div>
<div className={'mx-4 mt-2 mb-3 '}>
{feature.details.map((singleDetail) => {
return (
<div className={'flex justify-between text-base'}>
<span>{singleDetail.name}</span>
<span>{singleDetail.value}</span>
</div>
)
})}
</div>
</div>
</li>
))}
</ul>
</div>
</>
)
}
export default CustomerDashboard;
import React, {useState,useEffect} from 'react';
import {XIcon} from "#heroicons/react/solid";
const CustomerNotice = ({notice, setNoticeVisibility}) => {
return (
<div>
<div className="mt-8 pb-2 sm:pb-5">
<div className="max-w-7xl mx-auto px-2 sm:px-6 lg:px-8">
<div className="p-2 rounded-lg bg-orange-600 shadow-lg sm:p-3">
<div className="flex items-center justify-between flex-wrap">
<div className="w-0 flex-1 flex items-center">
<p className="ml-3 font-medium text-white truncate">
<span className="md:inline">{notice}</span>
</p>
</div>
<div className="order-3 mt-2 flex-shrink-0 w-full sm:order-2 sm:mt-0 sm:w-auto">
<a
href="#"
className="flex items-center justify-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-orange-600 bg-white hover:bg-orange-50"
>
<button onClick={toggleIsTrucated}>{isTruncated ? "Read More" : "Read Less"}</button>
</a>
</div>
<div className="order-2 flex-shrink-0 sm:order-3 sm:ml-2">
<button
onClick={() => setNoticeVisibility(false)}
type="button"
className="-mr-1 flex p-2 rounded-md hover:bg-orange-500 focus:outline-none focus:ring-2 focus:ring-white"
>
<span className="sr-only">Dismiss</span>
<XIcon className="h-6 w-6 text-white" aria-hidden="true"/>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default CustomerNotice;
If this is not possible please suggest me a possible way.
Instead of doing a bunch of hacks, I would recommend simplifying the structure of your components.
import { useState } from 'react'
export default function CustomerDashboard() {
// I am not sure why you want to keep notice in state,
// because in your example you did not call setNotice
const [notice, setNotice] = useState(`
Customer Notice: Optimism Is Invaluable For The Meaningful Life.
With A Firm Belief In A Positive Future You Can Throw Yourself Into The Service
Of That Which Is Larger Than You Are. -Martin Seligman
`)
const [isNoticeVisible, setIsNoticeVisible] = useState(true)
return (
<div>
<h1>My Account - Dashboard Overview</h1>
{isNoticeVisible && (
<CustomerNotice
notice={notice}
setIsNoticeVisible={setIsNoticeVisible}
/>
)}
</div>
)
}
function CustomerNotice(props) {
const { notice, setIsNoticeVisible } = props
const [isTruncated, setIsTruncated] = useState(true)
function toggleIsTruncated() {
setIsTruncated(!isTruncated)
}
return (
<div>
<Read text={notice} isTruncated={isTruncated} />
<button onClick={toggleIsTruncated}>
{isTruncated ? 'Read More' : 'Read Less'}
</button>
<button onClick={() => setIsNoticeVisible(false)}>Dismiss</button>
</div>
)
}
function Read(props) {
const { text, isTruncated } = props
const result = isTruncated ? text.slice(0, 90) : text
return <div>{result}....</div>
}
List of the things that were bad in your code.
Keeping the component instance in the state. It is hard to manage. Even your simple case proves that.
Keeping the toggleIsTruncated function inside the Read component. I think we should keep it outside and pass only 2 props to the Read component. I enable exposed only two things
const { text, isTruncated } = props
As you can see it is easy to maintain and allow us to do whatever we want.
PS. If my review and example were helpful please leave the thumbs up.

Mapping Array of Objects React

So I am currently having a little problem here. So I have this Component that is taking data from JSON, I have Link but is showing the like 20 times more. The same amount of objects that I have in JSON file. I know this is the problem {data.map((postData), it is mapping all the objects in JSON file, when I want that the only (classE, priceE and imageE) to show.
import React from "react";
import data from "./data.json";
import { Link } from "react-router-dom";
function E() {
return (
<div>
{data.map((postData) => {
return (
<div key={postData.id} className="m-4 bg-blue-100 rounded-xl p-8 ">
<div>
<Link
to={`/payment/${postData.id}`}
className="py-1 px-2 text-black-600 h-10 ml-24 mt-32 bg-white w-
36 rounded-full focus:outline-none focus:ring-2 focus:ring-gray-600"
>
Buy Now
</Link>
<img
alt=""
className="w-screen object-contain"
src={postData.imageE}
></img>
<h1 className=" ml-24 md:text-5xl sm:text-5xl top-8">
{postData.classE}
</h1>
<h1 className="text-base font-mono ml-24 top-24">
{postData.priceE}
</h1>
</div>
</div>
);
})}
</div>
);
}
export default E;
JSON file
[
{
"id": 0,
"class": "A-Class",
"Info": "A is the cheapest one ",
"imageA": "./ModImages/Aclass.jpg",
"textA": "fdsd",
"trefuA": "fdsd",
"optionA": "fdsd"
},
{
"id": 1,
"classE": "E-Class",
"imageE": "./ModImages/Eclass.jpg",
"priceE": "$43,600"
}
]
Try this :
{data.filter(d =>
d.classE &&
d.imageE &&
d.priceE) //FILTER FIRST
.map((postData) => { //THEN MAP
//CODE HERE
})
}
You filter the data first, then you map it
If you want to display only the objects with the properties classE, imageE, and priceE, you need to filter out the objects you are not interested in.
Change this line:
{data.map((postData) => {
to:
{data.filter(d =>
d.hasOwnProperty('classE') &&
d.hasOwnProperty('imageE') &&
d.hasOwnProperty('priceE'))
.map((postData) => {
That will display only the objects you want.

Tailwind Flex Box Responsive Grid with Cards issue

Here is where i started with
Preview
Code :
<div class="container my-12 mx-auto">
<div className="flex flex-wrap ">
{error ? <p>{error.message}</p> : null}
{!isLoading ? (
users.map(user => {
const { username, name, email } = user;
return (
<div
key={username}
className="w-full md:w-1/2 lg:w-1/3 my-5"
>
<article class="overflow-hidden rounded-lg shadow-lg">
<img
alt="Placeholder"
className="block h-auto w-full"
src="https://picsum.photos/600/400/?random"
/>
<header class="flex items-center justify-between leading-tight p-2 bg-white invisible lg:visible">
<h1 class="text-lg">{name}</h1>
<p class="text-grey-darker text-sm">
{email}
</p>
</header>
</article>
</div>
Then i tried to make it more spaced out like shown in the codepen example here :
https://codepen.io/codetimeio/pen/RYMEJe
but everytime i try to add some padding and margin it escapes to the next line and i cant figure out why it does that or how i can stop it
Here is the line i updated :
<div key={username} className="w-full md:w-1/2 lg:w-1/3 my-5 mx-5">
Here is my tailwind config file
module.exports = {
theme: {
container: {
center: true
},
screens: {
sm: "640px",
md: "768px",
lg: "1024px",
xl: "1280px"
},
fontFamily: {
display: ["Gilroy", "sans-serif"],
body: ["Graphik", "sans-serif"]
},
extend: {}
},
variants: {},
plugins: []
};
I really want to understand what im doing wrong so that i can use tailwind as my main templating framework
Here is updated code as per the given answer below
<div class="container my-12 mx-auto bg-gray-400">
<div className="flex flex-wrap ">
{error ? <p>{error.message}</p> : null}
{!isLoading ? (
users.map(user => {
const { username, name, email } = user;
return (
<div key={username} className="w-full p-5 md:w-1/2 lg:w-1/3">
<article className="overflow-hidden rounded-lg shadow-lg">
<img alt="Placeholder" className="w-full" src="https://picsum.photos/600/400/?random" />
<header className="flex items-center justify-between leading-tight p-2 bg-white">
<h1 className="text-lg">{name}</h1>
<p className="text-grey-darker text-sm">
{email}
</p>
</header>
</article>
</div>
It's happening because of the extra margins, w-1/3 means ~ width: 33.3333% If you add a margin on top of it, three can't fit in one line.
There are alternative ways (widths taking into account the gutter or gap property), but in exactly this case you could just use padding instead of margins, because you already have a presentational wrapping element around your cards.
Example: https://codepen.io/tlgreg/pen/RmLMOx
Not related, but few notes looking at your code:
Unless you use the old color palette in the config, grey-darker will not work.
img is block by default in v1.
invisible and lg:visible changes visibility, the header will take up space, if that not what you want, it should be hidden and lg:flex.

Categories

Resources