NextJs nesting a component into another (not FC) - javascript

I built a module, then broke it up into components and trying to put it back together.
The components work. Some components go inside the div of another component.
I have tried putting them into a div scaffold, but i was then writing more css to bring the module together.
I thought we just do this:
<CompOne>
<CompTwo />
</CompOne>
This gives an error:
Type '{ children: Element; }' has no properties in common with type 'IntrinsicAttributes'.
So maybe the above is write i need to typescript? sorry couldnt find a working example on this.
function WaldoEye() {
return (
<WaldoEyeball>
<WaldoRetina/>
</WaldoEyeball>
)
}
export default WaldoEye
function WaldoEyeball() {
return (
<div className="
flex
relative
items-center
opacity-90
w-40 h-40
rounded-full
shadow-[inset_0_-15px_15px_-3px_rgba(5,10,255,0.3)]
bg-gradient-to-r
from-slate-50
via-slate-100
to-slate-50
">
<div className="
absolute
flex
items-center
opacity-90
w-40 h-40
rounded-full
shadow-[0_60px_55px_-25px_rgba(255,255,255,0.4)]
">
</div>
</div>
)
}
export default WaldoEyeball
function WaldoRetina() {
return (
<>
<div className="
flex
items-center
relative
mx-auto
w-16
h-16
rounded-full
border-2
border-sky-500
bg-gradient-radial
from-cyan-500
via-sky-300
to-sky-500
">
</div>
</>
)
}
export default WaldoRetina

So maybe the above is write i need to typescript? sorry couldnt find a working example on this.
Currently the WaldoEyeball component doesn't expect any children. You will need to adjust the props in the WaldoEyeball component so it accepts children.
const WaldoEyeball: FC = ({ children }) => {
return (
<div className="
flex
relative
items-center
opacity-90
w-40 h-40
rounded-full
shadow-[inset_0_-15px_15px_-3px_rgba(5,10,255,0.3)]
bg-gradient-to-r
from-slate-50
via-slate-100
to-slate-50
">
<div className="
absolute
flex
items-center
opacity-90
w-40 h-40
rounded-full
shadow-[0_60px_55px_-25px_rgba(255,255,255,0.4)]
">
{children} // drop children somewhere to render it
</div>
</div>
);
}

Related

Applying Tailwind overflow behavior to only one child element

I'm using Tailwind to style a React component called "BlurImage" which displays an image by default and then on hover removes the image and displays a button at the bottom of the component and a text box at the top that has overflow-y-auto behavior to allow for scrolling if the text grows too long.
By default, the overflow responsibility is given to the body as discussed here and so I need to give the parent top-level div of BlurImage overflow as well. This causes both the text and the button to be scrollable. Is there a way to avoid having the button scroll?
This seems like it should be a common situation - I can imagine using this concept to write self-contained components that have a header/footer and some scrollable content as well. Unfortunately, I can't find much addressing this in the Tailwind docs or online.
I appreciate any suggestions! Here is the component code that I currently have:
import Image from 'next/image';
import cn from 'classnames';
import { useState } from 'react';
import { ImageInfo } from '../../types/images';
import Button from './button';
import { useRouter } from 'next/router';
type BlurImageProps = {
image: ImageInfo;
};
const BlurImage = ({ image }: BlurImageProps): JSX.Element => {
const [isLoading, setLoading] = useState(true);
return (
<div className="group shrink-0 h-40 w-40 relative aspect-w-1 aspect-h-1 overflow-hidden rounded-lg xl:aspect-w-7 xl:aspect-h-8 drop-shadow-xl bg-black">
<Image
alt=""
src={image.url}
width={512}
height={512}
className={cn(
'flex h-full duration-700 ease-in-out group-hover:opacity-20',
{
'scale-110 blur-2xl grayscale': isLoading,
'scale-100 blur-0 grayscale-0': !isLoading,
},
)}
onLoadingComplete={() => setLoading(false)}
/>
{!isLoading && (
<div className="absolute bottom-0 left-0 overflow-y-scroll items-center justify-center w-full h-full align-middle text-center px-2 opacity-0 duration-700 ease-in-out group-hover:opacity-100">
<a href={image.url}>
<p className="text-base text-white mb-2">
{image.prompt}
</p>
</a>
<Button
className="flex-shrink-0 px-2 py-2 text-sm font-bold text-white rounded bg-green-500"
type="button"
text="click me"
isSubmit={true}
onClick={() => console.log("clicked")}
/>
</div>
)}
</div>
);
};
export default BlurImage;

I'm trying to build a dropdown when cursor hovers on a navbar element pointing to that element with an arrow up

I am using NextJS and Tailwind CSS. I already built the dropdown but I have an issue building the arrow pointing to the specific element. I built the arrow in a previous version using a div rotated at 45deg but then I couldn't make my dropdown take the full screen. Now I have it full screen and I'm wondering how to make the arrow (div) point to the specific element I'm hovering on. I made sure to hide some details about the website because of confidentiality matters. Here's what I'm trying to achieve:
Here's the code I have so far:
import React from 'react'
import { navLinks } from '../data/navdata'
const DropdownHover = ({ index }) => {
return (
<div className="group-hover:block absolute left-0 w-full hidden text-dark-gray mt-8 bg-pink" aria-labelledby="dropdownButton">
<div className="justify-center">
<div className="flex py-10 px-20 text-sm justify-between">
{navLinks[index].hover.map((link, index) => {
return (
<div className="" key={index}>
<p className="text-navbar-gray py-2 uppercase font-semibold">{link.name}</p>
{link.links.map((sublink, index) => {
return(<p className="" key={index}><a className="bg-pink hover:text-red py-2 block whitespace-no-wrap" href={sublink.path}>{sublink.name}</a></p>)
})}
</div>
)
})}
</div>
<div className="flex text-sm px-20 py-10">
<div className='pr-40'>
<p className="text-navbar-gray py-2 uppercase font-semibold">Dummy Data</p>
<p className=""><a className="bg-pink hover:text-red py-2 block whitespace-no-wrap" href="#">Dummy Data</a></p>
<p className=""><a className="bg-pink hover:text-red py-2 block whitespace-no-wrap" href="#">Dummy Data</a></p>
<p className=""><a className="bg-pink hover:text-red py-2 block whitespace-no-wrap" href="#">Dummy Data</a></p>
</div>
<div>
<p className="text-navbar-gray py-2 uppercase font-semibold">Dummy Data</p>
<p className=""><a className="bg-pink hover:text-red py-2 block whitespace-no-wrap" href="#">Dummy Data</a></p>
<p className=""><a className="bg-pink hover:text-red py-2 block whitespace-no-wrap" href="#">Dummy Data</a></p>
<p className=""><a className="bg-pink hover:text-red py-2 block whitespace-no-wrap" href="#">Dummy Data</a></p>
</div>
</div>
</div>
</div>
)
}
export default DropdownHover
in CSS you can simply manipulate left property of the arrow if its positioned absolute:
li :nth-child(1):hover .arrow{
left:20rem;
}
li :nth-child(2):hover .arrow{
left:30rem;
}
but if you want to make it completely responsive, first get OffsetX of every li element then add an event listener on mouseclick.
for a better understanding you can check the following project on code pen.
https://codepen.io/piyushpd139/pen/gOYvZPG

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.

Prevent react from restarting the css animation on route change

In my react app I have a file called SecondaryLayout.js
const SecondaryLayout = ({children, className, ...rest}) => {
return (
<>
<Sidebar />
<main className={['seconday_layout__container', className].join('')} {...rest}>
{children}
</main>
</>
);
};
Sidebar.js:
const Sidebar = () => {
const {user} = useSelector(state => state.auth);
return (
<div className="bg-white py-4 px-2 fixed h-screen shadow-lg secondary_layout__sidebar">
<div className="h-full flex flex-col justify-between items-center">
<Link to="/"><h1 className="font-pacifico text-white px-2.5 py-1 bg-blue-700 rounded-full text-xl -mb-5">P</h1></Link>
<div className="flex flex-col space-y-2">
<NavLink to="/user/dashboard" className="px-2.5 py-1.5 rounded-lg shadow-md" activeClassName="bg-blue-700 shadow-lg text-white"><TemplateIcon className="inline w-4 h-4 -mt-1"/></NavLink>
<NavLink to="/user/listings" className="px-2.5 py-1.5 rounded-lg shadow-md" activeClassName="bg-blue-700 shadow-lg text-white"><ViewListIcon className="inline w-4 h-4 -mt-1"/></NavLink>
<NavLink to="/user/address" className="px-2.5 py-1.5 rounded-lg shadow-md" activeClassName="bg-blue-700 shadow-lg text-white"><LocationMarkerIcon className="inline w-4 h-4 -mt-1"/></NavLink>
<NavLink to="/user/profile" className="px-2.5 py-1.5 rounded-lg shadow-md" activeClassName="bg-blue-700 shadow-lg text-white"><CogIcon className="inline w-4 h-4 -mt-1"/></NavLink>
</div>
<Avatar name={user.username} image={user.image} />
</div>
</div>
);
};
The sidebar has a css animation, and 3 NavLinks, every time I click on a link, the css animation restarts, the behavior that I want is the sidebar to only fade in once, and stay fixed even when I click on a navlink, I tried to wrap my sidebar component with React.memo() but that didn't fix the issue
Edit:
Let's say the user navigates to /user/dashboard, or /user/profile, all these routes should always render the sidebar
Example
Dashboard.js
import React from 'react';
import { Helmet } from 'react-helmet';
import SecondaryLayout from '../../layouts/SecondaryLayout';
const Dashboard = () => {
return (
<SecondaryLayout>
<Helmet>
<title>My Dashboard</title>
</Helmet>
Dashboard
</SecondaryLayout>
);
};
export default Dashboard;
I think I managed to fix the issue, what I was doing is to wrap every component by the secondary layout, what I have done is to move the Secondary Layout to wrap around the route component itself, and not around the component being rendered by the route, i.e: <Route .... /></Route .... />

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