How do i create scrollbar-y inside of Sidebar - javascript

How do I can create a scrollbar inside of this category section.
For example look at the youtube scrollbar.
I want to create it using tailwind css.
Categories
export const categories = [
{
name: "cars",
image:
"https://i.pinimg.com/750x/eb/47/44/eb4744eaa3b3ccd89749fa3470e2b0de.jpg",
},
{
name: "fitness",
image:
"https://i.pinimg.com/236x/25/14/29/251429345940a47490cc3d47dfe0a8eb.jpg",
},
{
name: "wallpapers",
image:
"https://i.pinimg.com/236x/03/48/b6/0348b65919fcbe1e4f559dc4feb0ee13.jpg",
},
{
name: "websites",
image:
"https://i.pinimg.com/750x/66/b1/29/66b1296d36598122e6a4c5452b5a7149.jpg",
},
{
name: "photo",
image:
"https://i.pinimg.com/236x/72/8c/b4/728cb43f48ca762a75da645c121e5c57.jpg"
];
Sidebar
<div className="flex h-full flex-col gap-5 overflow-y-auto">
{categories.slice(0, categories.length).map((item, id) => (
<NavLink
to={`/category/${item.name}`}
className={({ isActive }) =>
isActive ? isActiveStyle : isNotActiveStyle
}
onClick={handleCloseSidebar}
key={id}
>
<img
src={item.image}
alt="category-item"
className="w-8 rounded-full h-8 shadow-sm"
/>
{item.name}
</NavLink>
))}
Update: Added the code (only the categories section). I had a lot of code above and below of this, which I thought would be better to not add since they don't serve any purpose to the scrolling.!
Note: Only the categories section would be scrollable, I don't want the whole sidebar to be scrolled!

Try adding a height or max-height and set overflow-y on your elements container:
.elements-container {
height: 300px;
overflow-y: scroll;
}
If your list of elements exceed the container height, a scrollbar will appear.
Check out the MDN documentation here about overflow-y: https://developer.mozilla.org/fr/docs/Web/CSS/overflow-y

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;

Add routing to navbar (react & tailwind)

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

Stagger Siblings in Framer Motion to perform Share Buttons Animation?

I have a Share Icon that looks like:
I want the first 5 icons to be shown. The 5th icon being the share icon.
I want the rest of the icons to be under share icon & when someone taps or hovers over it (either tap or hover as I don't know what would be best UX in this case), it should open to the right at their usual positions.
SocialShare.jsx
import React from 'react';
import { motion } from 'framer-motion';
import { ShareIcon } from '#heroicons/react/solid';
import {
Facebook,
HackerNews,
Reddit,
Twitter,
LinkedIn,
Pinterest,
Telegram,
Whatsapp,
Pocket
} from '../icons/index';
const isProduction = process.env.NODE_ENV === 'production';
export const SocialShare = ({ title, slug }) => {
const [share, openShare] = React.useState(false);
const [host, setHost] = React.useState('');
React.useEffect(() => {
setHost(window.location.host);
}, []);
const url = `${isProduction ? 'https://' : 'http://'}${host}/${slug}`;
const text = title + url;
const via = 'deadcoder0904';
const sharer = {
facebook: `https://www.facebook.com/sharer/sharer.php?u=${url}`,
twitter: `https://twitter.com/intent/tweet?url=${url}&text=${text}&via=${via}`,
reddit: `https://www.reddit.com/submit?title=${title}&url=${url}`,
hackernews: `https://news.ycombinator.com/submitlink?u=${url}&t=${title}`,
linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${url}`,
pinterest: `https://pinterest.com/pin/create/button/?url=${url}&description=${title}`,
telegram: `https://telegram.me/share/url?url=${url}&text=${text}`,
whatsapp: `https://wa.me/?text=${title}%0D%0A${url}%0D%0A%0D%0A${text}`,
pocket: `https://getpocket.com/edit.php?url=${url}`
};
const variants = {
hidden: {
opacity: 0,
translateX: -16
},
visible: {
opacity: 1,
translateX: 0
}
};
return (
<ul className="flex items-center mt-8 space-x-4">
<li>
<a className="" href={sharer.facebook} title="Share on Facebook">
<Facebook />
</a>
</li>
<li>
<a className="" href={sharer.twitter} title="Share on Twitter">
<Twitter />
</a>
</li>
<li>
<a className="" href={sharer.reddit} title="Share on Reddit">
<Reddit />
</a>
</li>
<li>
<a className="" href={sharer.hackernews} title="Share on Hacker News">
<HackerNews />
</a>
</li>
<motion.li className="cursor-pointer" whileHover={{}}>
<ShareIcon
className="w-6 h-6 text-gray-300"
onClick={() => {
openShare(!share);
}}
/>
</motion.li>
<motion.li
className=""
initial="hidden"
animate="visible"
variants={variants}
transition={{
type: 'tween',
ease: 'easeInOut'
}}
>
<a className="" href={sharer.linkedin} title="Share on LinkedIn">
<LinkedIn />
</a>
</motion.li>
<li>
<a className="" href={sharer.pinterest} title="Share on Pinterest">
<Pinterest />
</a>
</li>
<li>
<a className="" href={sharer.telegram} title="Share on Telegram">
<Telegram />
</a>
</li>
<li>
<a className="" href={sharer.whatsapp} title="Share on Whatsapp">
<Whatsapp />
</a>
</li>
<li>
<a className="" href={sharer.pocket} title="Share on Pocket">
<Pocket />
</a>
</li>
</ul>
);
};
The icons are in a flex container so I can't use staggerChildren. Stagger Children is exactly what I'm looking for but I can't find a solution to this using Flexbox?
Do I need to change the DOM elements by adding a wrapper? But that would break the ul>li combo.
All I want is it all the icons open when I hover on share to the right & when I stop hovering, they come back under share icon & disappear. Basically, share icon functionality like https://codepen.io/romswellparian/full/mJXdqV:
It only should be to the right & first 4 icons should be always shown.
I've made a complete Stackblitz reproduction → https://stackblitz.com/edit/share-animation-framer-motion?file=components%2FSocialShare.jsx
Stagger Children
Flexbox shouldn't prevent you from using staggerChildren.
To use staggerChildren you add it to the transition property of the parent variants. That means the parent of your list items needs to be a motion component, as do the list items you want to animate.
const listVariants = {
hidden: {
transition: {
staggerChildren: 0.1,
staggerDirection: -1
}
},
visible: {
transition: {
staggerChildren: 0.1
}
}
};
const itemVariants = {
hidden: {
opacity: 0,
x: -16
},
visible: {
opacity: 1,
x: 0
}
};
return (
<motion.ul
variants={listVariants}
initial="hidden"
animate={share ? 'visible' : 'hidden'}
>
<motion.li variants={itemVariants}>
<a href={sharer.linkedin} title="Share on LinkedIn">
<LinkedIn />
</a>
</motion.li>
{/* ...etc */}
</motion.ul>
);
Here's a quick fork of your project that does what you're describing:
https://stackblitz.com/edit/share-animation-framer-motion-e5fp5p?file=components/SocialShare.jsx
Animate Presence
If you actually want to remove the items from the DOM when they are hidden, then you need to use AnimatePresence. This requires that you wrap all the items what will enter and exit the DOM in the <AnimatePresence> tag. Each item will need an initial, animate and exit variant that describes the animation, and each item needs a unique key. The last one is easy to forget.
Unfortunately, I don't think StaggerChildren works with AnimatePresence in the way you want it to. To stagger the enter and exit animations you can use Dynamic Variants with a custom prop to define the animation delay for each item.
Short example:
const itemVariants = {
hidden: i => ({
opacity: 0,
x: -16,
transition: {
delay: i * 0.1
}
}),
visible: i => ({
opacity: 1,
x: 0,
transition: {
delay: i * 0.1 // custom prop used to stagger delay
}
})
};
return (
<ul className="flex items-center mt-8 space-x-4">
<AnimatePresence>
{share && (<>
<motion.li
variants={itemVariants}
key="linkedin" /* don't forget key! */
initial="hidden"
animate="visible"
exit="hidden"
custom={1} /* custom prop used to stagger delay */
>
<a href={sharer.linkedin} title="Share on LinkedIn">
<LinkedIn />
</a>
</motion.li>
<motion.li
variants={itemVariants}
key="pinterest"
initial="hidden"
animate="visible"
exit="hidden"
custom={2}
>
<a href={sharer.pinterest} title="Share on Pinterest">
<Pinterest />
</a>
</motion.li>
{/* etc... */}
</>)}
</AnimatePresence>
</ul>
)
Here's the StackBlitz with AnimatePresence:
https://stackblitz.com/edit/share-animation-framer-motion-wjdxhc?file=components/SocialShare.jsx
The way your example is set up, removing elements from the <ul> container causes it to shift (since the container size changes). I fixed that by setting an explicit width, but you could also change the flex alignment of float it or whatever is going to work with your actual project.
Following on from #Cadin answer I've found you can use both staggerChildren and AnimatePresense to provide a smoother animation when rendering lists.
const staggerChildrenVariant = {
animate: {
transition: {
staggerChildren: 0.5,
},
}
};
const childVariant = {
hidden: { height: 0 },
visible: { height: 'auto' }
};
<motion.div variants={staggerChildrenVariant} initial="animate" animate="animate">
<AnimatePresence>
{arr.map((arrItem) => (
<motion.div
key={arrItem.id}
variants={childVariant}
initial="hidden"
animate="visible"
exit="hidden"
>
...
</motion.div>
))}
</AnimatePresence>
</motion.div>

Prevent moving text elements on hover which is +1px font size

I have a row with text elements, in this case they are named "categories", any category that is on hover gets added a class that has font-size 1px bigger than non hover. Font size of my category may be diffrent (all categories have the same font but in config it can be changed) and comes from outside config.
const currentCategoryStyle = {
fontSize: `${parseInt(singleCategory['font-size'].replace('px', '')) + 1}px`,
};
<div className="categories-container d-flex">
{categories.map(category => (
<div
className="single-category"
onMouseEnter={() => {
setCategoryHover(category.id);
}}
onMouseLeave={() => {
setCategoryHover(null);
}}
style={{
...(
categoryHover === category.id &&
currentCategoryStyle),
}}
>
{category.name}
</div>
))}
</div>
But the problem is, that every element moves a bit, when I hover one category, of course because it gets bigger. My question is, what can I do to prevent this? Margin, padding doesn't really work here, any ideas?

How to import style properties from a .json document into a react component?

What I would like to do is to import the width, height and style properties into a react component. I will start by showing the .json document with the object properties:
"manufacturerData": {
"name": "Duesenberg",
"articleCount": 0,
"image": {
"file": "duesenberg.jpg",
"width": "140",
"height": "52",
"exists": true
}
},
I want to import the styles: width and the height into my image card container, that looks like this:
return (
<Card className='my-3 p-3 thumbnail' style={{ width: '14rem' }}>
<a href={`/articles/${articles.id}`}>
<Card.Img src={`/images/${articles.image.file}`} variat='top' />
</a>
</Card>
)
I cant seem to find anything online be straight forward on how to get this done.
I very much appreciate the help.
return (
// by default 'px' units will be assumed. You can add any other unit explicitly
<Card className='my-3 p-3 thumbnail' style={{ width: articles.image.width }}>
// <Card className='my-3 p-3 thumbnail' style={{ width: `${articles.image.width}rem` }}>
<a href={`/articles/${articles.id}`}>
<Card.Img src={`/images/${articles.image.file}`} variat='top' />
</a>
</Card>
)

Categories

Resources