Vue Router Scroll Up to Anchor - vuejs3

I have followed a few post on SO on how to scroll to anchor and have the following code added:
import { route } from 'quasar/wrappers'
import { createRouter, createMemoryHistory, createWebHistory, createWebHashHistory } from 'vue-router'
import routes from './routes'
/*
* If not building with SSR mode, you can
* directly export the Router instantiation;
*
* The function below can be async too; either use
* async/await or return a Promise which resolves
* with the Router instance.
*/
export default route(function (/* { store, ssrContext } */) {
const createHistory = process.env.SERVER
? createMemoryHistory
: (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory)
const Router = createRouter({
scrollBehavior(to, from, savedPosition) {
if (to && to.hash) {
return {
el: to.hash,
top: 75, // avoid blocking the view when having fixed components
behavior: 'smooth'
};
} else if (savedPosition) {
return savedPosition;
} else {
return { top: 75 };
}
},
routes,
// Leave this as is and make changes in quasar.conf.js instead!
// quasar.conf.js -> build -> vueRouterMode
// quasar.conf.js -> build -> publicPath
history: createWebHistory(process.env.VUE_ROUTER_BASE)
})
return Router
})
This works if you are already above the anchor and it will scroll down nice and clean to the anchor. However, say you are past the anchor and you click my navigation button it will not scroll up but instead resets to the top of the page then scrolls down. Just does not feel fluent. So can Vue-Router not scroll up to the anchor? My sidebar menu with router-links is as follows:
<template>
<div class="navigation-links q-pa-lg">
<q-list padding>
<q-item clickable v-ripple class="q-pa-md q-ma-md menu-item"
:to="{name: 'Home', hash: '#Home'}"
:active="(route.name === 'Home' && route.hash === '#Home') || (route.name === 'Home' && route.hash === '')"
active-class="active-menu-item">
<q-item-section avatar>
<q-icon name="fa-solid fa-house" style="font-size:1.25rem"/>
</q-item-section>
<q-item-section>
HOME
</q-item-section>
</q-item>
<q-item clickable v-ripple class="q-pa-md q-ma-md menu-item"
:to="{name: 'Home', hash: '#Services'}"
:active="route.name === 'Home' && route.hash === '#Services'"
active-class="active-menu-item">
<q-item-section avatar>
<q-icon name="fa-solid fa-shirt" style="font-size:1.25rem"/>
</q-item-section>
<q-item-section>
SERVICES
</q-item-section>
</q-item>
<q-item clickable v-ripple class="q-pa-md q-ma-md menu-item"
:to="{name: 'Home', hash: '#OurTeam'}"
:active="route.name === 'Home' && route.hash === '#OurTeam'"
active-class="active-menu-item">
<q-item-section avatar>
<q-icon name="fa-solid fa-users" style="font-size:1.25rem"/>
</q-item-section>
<q-item-section>
OUR TEAM
</q-item-section>
</q-item>
<q-item clickable v-ripple class="q-pa-md q-ma-md menu-item" #click="layoutStore.toggleContactUsDrawer">
<q-item-section avatar>
<q-icon name="fa-solid fa-envelope" style="font-size:1.25rem"/>
</q-item-section>
<q-item-section>
CONTACT US
</q-item-section>
</q-item>
</q-list>
</div>
<div class="socialmedia-links q-pa-lg text-center column no-wrap flex-center self" style="">
<div class="row">
<q-btn flat size="sm" color="primary" round icon="fa-brands fa-google" aria-label="google" class="float-right q-ma-sm no-shadow" #click="openInNewTab(companyGoogleLink)"/>
<q-btn flat size="sm" color="primary" round icon="fa-brands fa-facebook-f" aria-label="facebook" class="float-right q-ma-sm no-shadow" #click="openInNewTab(companyFacebookLink)"/>
<q-btn flat size="sm" color="primary" round icon="fa-brands fa-instagram" aria-label="instagram" class="float-right q-ma-sm no-shadow" #click="openInNewTab(companyInstagramLink)"/>
</div>
<h2 class="text-h5 text-weight-bolder q-mb-xs">CONNECT WITH US</h2>
<h6 class="text-subtitle1 text-accent text-weight-normal q-my-none">If you like what you have seen so far we can guarentee you will appreciate our print quality.</h6>
</div>
</template>
<script setup>
import { ref } from 'vue'
import {storeToRefs} from 'pinia'
import {useLayoutStore} from '../stores/layout-store'
import {useCompanyInfoStore} from '../stores/company-info-store'
import {useRoute, useRouter } from 'vue-router'
//store
const layoutStore = useLayoutStore()
const {contactUsDrawer, navigationDrawer} = storeToRefs(layoutStore)
const companyInfoStore = useCompanyInfoStore()
const {companyFacebookLink, companyGoogleLink, companyInstagramLink} = storeToRefs(companyInfoStore)
const searchText = ref('')
const route = useRoute()
const router = useRouter()
//methods
function openInNewTab(url) {
window.open(url, '_blank').focus();
}
</script>
<style lang="scss" scoped>
.block-wrapper{
max-width: 1024px;
margin-left: auto;
margin-right: auto;
}
.active-menu-item{
background-color: $background-accent;
color: $primary !important;
}
.menu-item{
letter-spacing: .15rem;
font-weight:500;
border-radius: 7px;
color:$dark
}
</style>

Related

feching data dynamically using getServerSideProps in next js but error: Cannot read properties of null (reading 'useContext')

I'm trying to fetch data dynamically according to user choice, here I'm using getServerSide props inside pages/newsPages/[category].tsx
error pointing towards this:
TypeError: Cannot read properties of null (reading 'useContext')
46 export async function getServerSideProps(context: any) {
> 47 | const router = useRouter();
| ^
import { useRouter } from 'next/router'
import React, { useState } from 'react'
import Feeds from '../../components/Feeds'
import Header from '../../components/Header'
export default function category({ newsCategory }:any) {
const [date, setDate] = useState(new Date())
return (
<div className='bg-gray-100'>
<Header />
<main className='mx-5 my-5'>
<div className='bg-white'>
<div className='flex justify-between p-4'>
<h1 className='text-sm font-bold md:text-2xl'>Wellcome to clone BBC.com</h1>
<h1 className='text-sm font-bold md:text-2xl'>{date.toDateString()}</h1>
</div>
{newsCategory.articles.slice(0, 1).map((article:any )=> (
<a key={article.url} href={article.url}
className="relative">
<img className='w-full' src={article.urlToImage ? article.urlToImage: "./ALT.jpg"} alt="" />
<h1
className='text-white absolute bottom-[40%] left-[5%]
lg:text-2xl text-xl'>{article.title}</h1>
<p className='text-white absolute bottom-[30%] left-[5%] line-clamp-1'>{article.description}</p>
</a>
))}
<div className='grid grid-flow-row-dense md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4'>{/*grid start*/}
{newsCategory.articles.slice(1, newsCategory.articles.length).map((article: any) => (
<Feeds
url={article.url}
className=""
key={article.url}
urlToImage={article.urlToImage}
title={article.title}
description={article.description}
/>
))}
</div>
</div>
</main>
</div>
)
}
export async function getServerSideProps(context: any) {
const router = useRouter();
const { category } = router.query
const newsCategory = await fetch(`https://newsapi.org/v2/top-headlines?country=us&category=${category}&apiKey=${process.env.API_KEY}`)
.then(res => res.json())
return {
props: {
newsCategory,
}
}
}
pages/index.tsx
import Head from 'next/head'
import Header from '../components/Header';
import NewsFeed from '../components/NewsFeed';
export default function Home({ news }: any) {
return (
<div className='bg-gray-100 '>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<Header />
<main className='mx-5 my-5'>
<NewsFeed news={news} key={news.article}/>
</main>
</div>
)
}
export async function getServerSideProps(context: any) {
const news = await fetch("https://newsapi.org/v2/top-headlines?country=us&category=business&apiKey=")
.then(res=>res.json());
return {
props: {
news,
}
}
}
header.tsx
pushing to the newspages
<p className='hidden hover:text-blue-500 sm:inline-flex'
onClick={()=> router.push("/")}>Home</p>
<p className='hidden hover:text-blue-500 sm:inline-flex'
onClick={()=> router.push("/newsPages/general")}>News</p>
<p className='hidden hover:text-blue-500 sm:inline-flex'
onClick={()=> router.push("/newsPages/sports")}>Sport</p>
<p className='hidden hover:text-blue-500 sm:inline-flex'
onClick={()=> router.push("/newsPages/entertainment")}>Reel</p>
<p className='hidden hover:text-blue-500 sm:hidden lg:inline-flex'
onClick={()=> router.push("/newsPages/health")}>Worklife</p>
<p className='hidden hover:text-blue-500 sm:hidden lg:inline-flex'
onClick={()=> router.push("/newsPages/science")}>Science</p>
<p className='hidden hover:text-blue-500 sm:hidden lg:inline-flex'
onClick={()=> router.push("/newsPages/future")}>Future</p>
what I did wrong here, can I use getServerSideProps in multiple files inside the pages folder? why here this error occurs,

How to add a class on click, and remove the same class from all other elements?

I have a button navigation and when you click on a button, the active class is added. My goal is for the active class to be added to the button clicked, but remove that class of active on all other buttons if present. The 'About' button will have a class of active on page load.
Not sure how to translate this to React, in JavaScript on click I would remove the class from all the elements in a loop and add a class to the target clicked if it did not already have the active class.
Code Sandbox - https://codesandbox.io/s/toggle-active-on-class-clicked-remove-from-the-rest-r467l1?file=/src/App.js
export default function Header() {
const [active, setActive] = useState(true);
const toggleColor = function (e) {
// on load, 'About' button has active class
// when clicking another menu item add active class, remove active from the rest of buttons
console.log(e.target);
};
return (
<header className="header-img-container">
<nav>
<ul>
<li>
<button onClick={toggleColor} className={active ? "active" : ""}>
About
</button>
</li>
<li>
<button onClick={toggleColor}>Skills</button>
</li>
<li>
<button onClick={toggleColor}>Projects</button>
</li>
<li>
<button onClick={toggleColor}>Words</button>
</li>
</ul>
</nav>
</header>
);
}
There are so many ways to solve that problem. You can try this if it's meet your requirements.
import "./styles.css";
import { useState } from "react";
const list = ["About", "Skills", "Projects", "Words"];
export default function Header() {
const [activeLink, setActiveLink] = useState("About");
return (
<header className="header-img-container">
<nav>
<ul>
{list.map((item) => (
<li key={item}>
<button
onClick={() => setActiveLink(item)}
className={activeLink === item ? "active" : ""}
>
{item}
</button>
</li>
))}
</ul>
</nav>
</header>
);
}
Create a state like this
const [active, setActive] = useState({About: true, Skills: false, Projects: false, Words: false})
А change local parameter to add a class to element. For example
<li>
<button onClick={() => {
setActive({...active, About: false, Skills: true, Projects: false,
Words: false })
}}>Skills</button>
</li>
There are many possible approaches, here is a basic example that uses an object type active state to store the value for each list item.
const [active, setActive] = useState({ About: true })
The list data is stored in an array so it can be mapped in the JSX part of the component.
const itemList = ["About", "Skills", "Projects", "Words"]
While index is not an ideal key it is used here just for example purpose.
{
itemList.map((item, index) => (
<li key={index}>
<button
onClick={() => toggleColor(item)}
className={active[item] ? "active" : ""}
>
{item}
</button>
</li>
));
}
toggleColor sets value for active, and it specify that active should always be in the format of {About: true}, {Skills: true} and such. The !!! covers the case when certain keys are not existing in the object.
const toggleColor = function (item) {
setActive((prev) => {
return { [item]: !!!prev[item] };
});
};
Below is the full example, it runs in the snippet for convenience.
function Header() {
const [active, setActive] = React.useState({ About: true });
const itemList = ["About", "Skills", "Projects", "Words"];
const toggleColor = function (item) {
// on load, 'About' button has active class
// when clicking another menu item add active class, remove active from the rest of buttons
setActive((prev) => {
return { [item]: !!!prev[item] };
});
};
return (
<header className="header-img-container">
<nav>
<ul>
{itemList.map((item, index) => (
<li key={index}>
<button
onClick={() => toggleColor(item)}
className={active[item] ? "active" : ""}
>
{item}
</button>
</li>
))}
</ul>
</nav>
</header>
);
}
const App = () => {
return (
<div>
<Header />
</div>
);
};
ReactDOM.render(<App />, document.querySelector("#root"));
.App {
font-family: sans-serif;
text-align: center;
}
button {
padding: 6px;
}
.active {
border: 1px solid pink;
color: hotpink;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.production.min.js"></script>

Reactjs sidebar doesn't collapse and dropdown doesn't open

I am trying to achieve two things:
(1) each time I click on the red arrow icon in the sidebar, I want the sidebar to collapse or open. From the below video, you'd see that the active and inactive states are already there. However, the sidebar doesn't collapse on inactive.
(2) each time I click on the Content menu, which is a drowndown menu, it doesn't open the submenu. Also, from the below video, you'd notice that the active and inactive states are already there. However, the dropdown still doesn't open on active.
Below is the video that clearly shows the error:
https://www.loom.com/share/6e0488101cee4c5b9bac7ded782b8807
Docs.js Page
import React from "react";
import { Helmet } from "react-helmet";
import SideMenu from "../docs/SideMenu";
const Docs = () => {
return (
<div className="">
<Helmet>
<title>Docs :: MyApp</title>
<meta name="description" content="MyApp" />
</Helmet>
<SideMenu />
</div >
)
};
export default Docs
SideMenu.js Component
import React, { useState } from "react";
import { Helmet } from "react-helmet";
import * as Icon from "react-bootstrap-icons";
import MenuItems from "./MenuItems";
const SideMenu = () => {
const [inActive, setInActive] = useState(false)
return (
<div className="">
<div className={`side-menu ${inActive ? "inActive" : ""}`}>
<Helmet>
<title>Docs :: MyApp</title>
<meta name="description" content="MyApp" />
</Helmet>
<div className="top-section">
<div className="logo">
<img src="/assets/media/logos/naked.png" alt="MyApp" />
</div>
<div onClick={() => setInActive(!inActive)} className="toggle-back">
{inActive ? (<Icon.ArrowLeftSquareFill />) : (<Icon.ArrowRightSquareFill />)}
</div>
</div>
<div className="search-bar">
<button className="search-bar-btn">
<Icon.Search />
</button>
<input type="text" placeholder="search" />
</div>
<div className="divider"></div>
<div className="main-menu">
<ul>
{menuItems.map((menuItem, index) => (
<MenuItems
key={index}
name={menuItem.name}
to={menuItem.to}
subMenu={menuItem.subMenu || []} />
))}
{/*<li>
<a className="menu-item">
<Icon.ArrowRightSquareFill className="menu-icon" />
<span>Dashboard</span>
</a>
</li>
<MenuItems
name={"Content"}
subMenu={[
{ name: 'Courses' },
{ name: 'Videos' },
]}
/>
<li>
<a className="menu-item">
<Icon.ArrowRightSquareFill className="menu-icon" />
<span>Support</span>
</a>
</li>*/}
</ul>
</div>
<div className="side-menu-footer">
<div className="avatar">
<img src="/assets/media/avatars/aa/brooks_lloyd.png" alt="MyApp" />
</div>
<div className="user-info">
<div className="font-size-h6">Title</div>
<div className="font-size-sm">Subtitle</div>
</div>
</div>
</div>
</div>
);
};
export default SideMenu
const menuItems = [
{ name: "Dashboard", to: "/" },
{ name: "Content", to: "/", subMenu: [{ name: "Courses" }, { name: "Videos" }], },
{ name: "Design", to: "/" },
];
MenuItems.js Component
import React, { useState } from "react";
import * as Icon from "react-bootstrap-icons";
const MenuItems = (props) => {
const { name, subMenu } = props;
const [expand, setExpand] = useState(false);
return (
<div className="">
<li>
<a onClick={() => setExpand(!expand)} className="menu-item">
<Icon.ArrowRightSquareFill className="menu-icon" />
<span>{name}</span>
</a>
{
subMenu && subMenu.length > 0 ? (
<ul className={`sub-menu ${expand ? "active" : ""}`}>
{subMenu.map((menu, index) =>
<li key={index}>
<a className="sub-menu">
<Icon.ArrowRightSquareFill className="menu-icon" />
{menu.name}
</a>
</li>
)}
</ul>) : null}
</li>
</div>
);
};
export default MenuItems
Docs.css File that contains the suspected errors, which are the side-menu and sub-menu lines:
.side-menu {
position: fixed;
background: #000;
width: 300px;
height: 100%;
box-sizing: border-box;
padding: 30px 20px;
transition: width .2s ease-in;
}
.side-menu.inactive {
width: 80px;
}
.side-menu .main-menu .sub-menu {
color: #333;
margin-left: 20px;
border-left: 1px solid #666;
box-sizing: border-box;
padding-left: 30px;
max-height: 0;
overflow: hidden;
transition: max-height .2s ease-in;
}
.side-menu .main-menu .sub-menu.active {
max-height: 200px;
}

How to change overflow style to hidden when modal opens

I am trying to create react app and i want overflow to hidden when button is pressed,
i want to display contact form on modal but overflow is running everything i can scroll even with modal is open
Here is my navbar with button
import React from "react";
import { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { FaBars, FaTimes } from "react-icons/fa";
import "./Navbar.css";
import { IconContext } from "react-icons/lib";
import Button from "./Button";
import Modal from "./Contact Form/Modal";
import { motion, AnimatePresence } from "framer-motion";
export default function Navbar() {
const [click, setClick] = useState(false);
const [button, setButton] = useState(true);
const [modalOpen, setModalOpen] = useState(false);
function closeModal() {
setModalOpen(false);
}
function openModal() {
setModalOpen(true);
}
function handleClick() {
setClick(!click);
}
function closeMobileMenu() {
setClick(false);
}
function showButton() {
if (window.innerWidth <= 960) {
setButton(false);
} else {
setButton(true);
}
}
function closeMobileMenu() {
setClick(false);
}
useEffect(() => {
showButton();
}, []);
window.addEventListener("resize", showButton);
return (
<>
<IconContext.Provider value={{ color: "#fff" }}>
<div className="navbar">
<div className="navbar-container container">
<Link to="/" className="navbar-logo" onClick={closeMobileMenu}>
LOGO
</Link>
<div className="menu-icon" onClick={handleClick}>
{click ? <FaTimes /> : <FaBars />}
</div>
<ul className={click ? "nav-menu active" : "nav-menu"}>
<li className="nav-item">
<Link to="/" className="nav-links" onClick={closeMobileMenu}>
Home
</Link>
</li>
<li className="nav-item">
<Link to="/" className="nav-links" onClick={closeMobileMenu}>
About Us
</Link>
</li>
<li className="nav-item">
<Link to="/" className="nav-links" onClick={closeMobileMenu}>
Contact Us
</Link>
</li>
{/* Main button */}
<li className="nav-btn">
{button ? (
<Link to='' className="btn-link">
<motion.button
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
className="btn--outline"
onClick={() => (modalOpen ? closeModal() : openModal())} //modal opens
>
CONTACT US
</motion.button>
</Link>
) : (
<Link
to=""
className="btn-link"
onClick={closeMobileMenu}
>
<motion.button
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
className="btn--mobile"
buttonSize='btn--mobile'
buttonStyle='btn--outline'
onClick={() => {(modalOpen ? closeModal() : openModal())}} //modal opens
>
Contact US
</motion.button>
</Link>
)}
</li>
</ul>
</div>
</div>
<AnimatePresence
// Disable any initial animations on children that
// are present when the component is first rendered
initial={false}
// Only render one component at a time.
// The exiting component will finish its exit
// animation before entering component is rendered
exitBeforeEnter={true}
// Fires when all exiting nodes have completed animating out
onExitComplete={() => null}
>
{modalOpen && <Modal modalOpen={modalOpen} handleClose={closeModal} />}
</AnimatePresence>
</IconContext.Provider>
</>
);
}
I was trying to do it use State but it didn't work. I use style effect but i didn't manage to achieve it
You will make a boolean state with an initial value set to false.
When the button is clicked you will update the state from false to true and I will recommend it for you instead of making it true. then you will use this state easily with your style property and do what you need.
change the style bellow based on your needs.
import { useState } from "react";
export default function App() {
const [clicked, setClicked] = useState(false);
return (
<div className="App">
<h1 style={{ display: clicked ? "none" : "block" }}>Hello
CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<button onClick={() => setClicked(!clicked)}>click</button> <= here instead of making it true. I made it the oppesite of the previous value. to toggle between them.
</div>
);
}

How do i bind a css stylesheet to only 1 react component?

I am using a React Template, where i inserted a shopping cart site using some react and Simple HTML (The code is below the question). Also i have a index.css file, which contains the css for the shopping cart. My goal is to implement the css only in the shopping cart site.
I tried to "import './index.css' inside my shopping cart.
The problem was as follows: After rendering the shopping cart, the css was applied to every other site as well.
How is it possible to use the css code only in the shopping cart?
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link } from 'react-router-dom';
import { addToCart, removeFromCart } from '../../frontend/actions/cartActions';
import MessageBox from '../../frontend/MessageBox';
import { useLocation } from 'library/hooks/useLocation';
export default function CartScreen(props) {
// Namen
var a = window.location.href;
var b = a.substring(a.indexOf("?")+1)
const productId = b;
console.log(productId)
// redux store
const cart = useSelector((state) => state.cart);
const { cartItems, error } = cart;
const dispatch = useDispatch();
useEffect(() => {
if (productId) {
dispatch(addToCart(productId));
}
}, [dispatch, productId]);
const removeFromCartHandler = (id) => {
// delete action
dispatch(removeFromCart(id));
};
const checkoutHandler = () => {
props.history.push('/signin?redirect=shipping');
};
return (
<div className="row top">
<div className="col-2">
<h1>Shopping Cart</h1>
{error && <MessageBox variant="danger">{error}</MessageBox>}
{/* display cart or message if empty */}
{cartItems.length === 0 ? (
<MessageBox>
Cart is empty. <Link to="/">Go Shopping</Link>
</MessageBox>
) : (
<ul>
{cartItems.map((item) => (
<li key={item.product}>
<div className="row">
<div>
<img
src={item.image}
alt={item.name}
className="small"
></img>
</div>
<div className="min-30">
<Link to={`/product/${item.product}`}>{item.name}</Link>
</div>
<div>
<select
value={item.qty}
onChange={(e) =>
dispatch(
addToCart(item.product, Number(e.target.value))
)
}
>
{[...Array(item.countInStock).keys()].map((x) => (
<option key={x + 1} value={x + 1}>
{x + 1}
</option>
))}
</select>
</div>
<div>${item.price}</div>
<div>
<button
type="button"
onClick={() => removeFromCartHandler(item.product)}
>
Delete
</button>
</div>
</div>
</li>
))}
</ul>
)}
</div>
<div className="col-1">
<div className="card card-body">
<ul>
<li>
<h2>
Subtotal ({cartItems.reduce((a, c) => a + c.qty, 0)} items) : $
{cartItems.reduce((a, c) => a + c.price * c.qty, 0)}
</h2>
</li>
<li>
<button
type="button"
onClick={checkoutHandler}
className="primary block"
disabled={cartItems.length === 0}
>
Proceed to Checkout
</button>
</li>
</ul>
</div>
</div>
</div>
);
}
import './index.css'
give the div in the html a specific name: <div className="cartScreen">
in css, specify the element where the style should be used:
body .cartScreen{
margin: 0;
height: 100vh;
font-size: 1.6rem;
font-family: Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

Resources