Close Popover with mapped array HeadlessUI/Next.js - next.js

Cannot figure out how to get popover to close when next Link is clicked inside. Used popover from tailwindui and added next Link, tried the headlessUI closing popover using as={Link}, no luck.
<div className='max-w-7xl mx-auto grid gap-y-6 px-4 py-6 sm:grid-cols-2 sm:gap-8 sm:px-6 sm:py-8 lg:grid-cols-4 lg:px-8 lg:py-12 xl:py-16'>
{about.map((item) => (
<Popover.Button key={item.name}>
<Link href={item.href}>
<a
href={item.href}
className='-m-3 p-3 flex flex-col justify-between rounded-lg hover:bg-gray-50'>
<div className='flex md:h-full lg:flex-col'>
<div className='flex-shrink-0'>
<span className='inline-flex items-center justify-center h-10 w-10 rounded-md bg-cyan-500 text-white sm:h-12 sm:w-12'>
<item.icon
className='h-6 w-6'
aria-hidden='true'
/>
</span>
</div>
<div className='ml-4 md:flex-1 md:flex md:flex-col md:justify-between lg:ml-0 lg:mt-4'>
<div>
<p className='text-base font-medium text-gray-900'>
{item.name}
</p>
<p className='mt-1 text-sm text-gray-500'>
{item.description}
</p>
</div>
<p className='mt-2 text-sm font-medium text-cyan-600 lg:mt-4'>
Learn more
<span aria-hidden='true'>
→
</span>
</p>
</div>
</div>
</a>
</Link>
</Popover.Button>```
Help please, what am i missing?

I just ran into this exact issue.
I got it working by doing two things referenced separately in the Headless UI docs. This involved creating a custom wrapper component for Next's Link and exposing the close() render prop on the Popover Panel.
A minimal example using the solution is listed first followed by an explanation with references to the docs.
Solution/Example
import { forwardRef } from 'react'
import Link from 'next/link'
import { Popover } from '#headlessui/react'
const MyLink = forwardRef((props, ref) => {
let { href, children, ...rest } = props
return (
<Link href={href}>
<a ref={ref} {...rest}>
{children}
</a>
</Link>
)
})
function Example() {
return (
<Popover>
{({ open }) => (
<>
<Popover.Button>Open Popover</Popover.Button>
<Popover.Panel>
{({ close }) => (
<MyLink href='/profile' onClick={() => close()}>
Profile
</MyLink>
)}
</Popover.Panel>
</>
)}
</Popover>
)
}
Explanation
Step 1
Expose the close() render prop inside your Popover Panel, per the alternative solution suggested in the docs.
<Popover.Panel>
{({ close }) => (
We will call this in our link to manually close the popup when the link is clicked.
Step 2
Create your own custom MyLink component (or whatever name you want) that wraps Link and forwards props to the child a element. As explained in the Headless UI docs, Next's Link component doesn't forward unknown props by default and Headless needs to do this in order to work by adding the necessary event listeners.
const MyLink = forwardRef((props, ref) => {
let { href, children, ...rest } = props
return (
<Link href={href}>
<a ref={ref} {...rest}>
{children}
</a>
</Link>
)
})

Related

How to make video responsive like Image component of react?

I'm facing a strange problem while setting width and height of the video tag. I'm using Image component (from next/image), which is setting its height automatically.
It always shows height of 413px even if I use image with 150x150 dimensions. I'm not seeing any absolute height rule in CSS inspector even for parent elements or this Image component.
Actually it is the desired behavior (width/height) of the Image. I'm trying to do the same thing with the video tag. But it shrinks itself down to actual height of the video if video is smaller in height.
Video should not change it's height because it shares same CSS properties and has same container as Image. I'm also using object-fit:cover property.
Code for the component is:
import React, { useState } from 'react';
import Image from 'next/image';
import { IExclusiveCard } from '../../../typings/types';
import Link from 'next/link';
import useNFTMetadata from '../../../hooks/useNftMetaData';
import { BsFillPlayFill, BsPauseFill } from 'react-icons/bs';
const ExclusiveCard: React.FC<IExclusiveCard> = ({
id,
photo_name,
metadataUri,
cardInfo,
cardType,
cardInfoBg,
title,
desc,
collectionType,
asset_url,
thumbnail,
}) => {
let { data } = useNFTMetadata(metadataUri as string);
if (!metadataUri?.trim() && thumbnail && asset_url) {
data = {
asset_url,
thumbnail,
};
}
const [videoPaused, setVideoPaused] = useState(true);
let href = '';
if (collectionType === 'continuous') {
href = `/exclusives/${id}/auction`;
} else if (collectionType === 'editions') {
href = `/editions/${id}/mint`;
}
const image = data?.thumbnail || data?.image || photo_name;
const description = desc || data?.description || '';
return (
<Link href={href}>
<a className='h-full block'>
<div
className="token-card--items h-full w-[382px] md:w-full md:min-w-[216px] md:max-w-[216px] md:h-[317px] lg:w-[290px] laptop-x:w-[300px] laptop-m:w-[350px] desktop-m:w-[350px] rounded-3xl overflow-hidden bg-primary lg:rounded-2xl"
key={id}
>
<div className="token-img-cont-box h-full relative">
<div className="tokens-img-wrap h-full relative">
<div className="exclusive--card-img h-full w-full relative md:h-[317px]">
{/* If thumbnail exists, show video instead of an image */}
{!videoPaused && data?.asset_url ? (
<video
style={{
height: '100%',
objectFit: 'cover',
}}
src={data?.asset_url}
autoPlay
loop
muted
onEnded={() => setVideoPaused(true)}
/>
) :
image ? (
<Image
// src={image}
src={"https://picsum.photos/id/1/150/150"}
alt="tokens-card-img"
className="token-card-img h-full w-full object-cover"
width="382px"
height="520px"
/>
) : (
<div className="slim-nft-card-placeholder-bg rounded-[20px]"></div>
)}
</div>
<div className="tokens-card-top-cont flex justify-end items-start absolute top-0 left-0 p-5 w-full md:p-2">
<span
className={[
'card-updates-info text-fig-15 uppercase text-secondary block text-center font-primary font-normal rounded-[100%] w-[62px] h-[62px] leading-[62px] laptop-x:text-fig-12 laptop-x:w-[45px] laptop-x:h-[45px] laptop-x:leading-[45px] lg:w-[36px] lg:h-[36px] lg:text-fig-xs lg:leading-[36px]',
`bg-${cardInfoBg}`,
].join(' ')}
>
{cardInfo}
</span>
</div>
<div className="token-card-content-box pt-[13px] pb-4 px-6 absolute bottom-0 left-0 z-50 lg:px-4 w-full">
<h3 className="card-title font-primary font-normal text-fig-32 text-left text-secondary uppercase mb-4 laptop-x:text-fig-24 lg:text-fig-base lg:mb-2">
{title || data?.properties?.title}
</h3>
<p className="desc mb-8 text-fig-xs text-left text-secondary font-primary font-normal md:text-fig-xs md:mb-2">
{/* Truncate desc or data?.description if it is greater than 30 characters */}
{description.length > 50
? description.slice(0, 50) + '...'
: description}
</p>
<div
className={`token-cards-bottom flex ${
data?.thumbnail ? 'justify-between' : 'justify-end'
} w-full`}
>
{data?.thumbnail && (
<h3 className="card-type font-primary font-normal text-fig-32 text-right laptop-x:text-fig-24 text-white uppercase lg:text-fig-15 lg:mb-0">
{videoPaused ? (
<BsFillPlayFill
onClick={(ev) => {
ev.preventDefault();
setVideoPaused(false);
}}
className="text-4xl"
/>
) : (
<BsPauseFill
onClick={(ev) => {
ev.preventDefault();
setVideoPaused(true);
}}
className="text-4xl"
/>
)}
</h3>
)}
<h3 className="card-type font-primary font-normal text-fig-32 text-right laptop-x:text-fig-24 text-darkgray uppercase lg:text-fig-15 lg:mb-0">
{cardType}
</h3>
</div>
</div>
</div>
</div>
</div>
</a>
</Link>
);
};
export default ExclusiveCard;
I'm not sure what I'm missing here. What should I do to get same size of video like Image when I click play icon?

Switch state based change transtion doesnt work tailwind

I made a custom switch in tailwind problem is that when it witch transiton doesnt work I think its becouse of this line
${isSwitched ? ' right-1 ' : 'left-1'}
import type { StateUpdater } from "preact/hooks"
type SwitchButtonProps = {
textLeft: String,
textRight: String,
isSwitched: boolean,
setIsSwitched: StateUpdater<boolean>
}
export const SwitchButton = ({ textLeft, textRight, isSwitched, setIsSwitched }: SwitchButtonProps) => {
return (
<div class="mx-8 shadow rounded border h-10 mt-4 flex p-1 relative items-center bg-gray-200">
<div class="w-full flex justify-center"
onClick={() => setIsSwitched(false)}
>
<button>{textLeft}</button>
</div>
<div class="w-full flex justify-center"
onClick={() => setIsSwitched(true)}>
<button>{textRight}</button>
</div>
<span
class={` bg-white shadow text-gray-800 flex items-center justify-center w-1/2 rounded h-8 transition-all top-[4px] absolute
${isSwitched ? ' right-1 ' : 'left-1'}
`}>
{isSwitched ? textRight : textLeft}
</span>
</div>
)
}

Hover behind an element in react js

I'm having trouble with a design that consists in a 6 columns grid and a text ahead it. When I hover the text, the grid child has to change its background to an image (only the column hovered). It works fine, but I have the problem hovering behind the text... it seems impossible. Is there any solution? thanks!
const [dissapear, setDissapear] = useState([20]);
return (
<PageLayout
className="bg-softBlack"
>
<div className="relative h-[480px]">
<h1
className="absolute top-32 uppercase bg-transparent text-gray-10 text-[9.028vw] leading-[0.9] tracking-[-0.06em]"
style={{ zIndex: 1000, mixBlendMode: "difference" }}
>
welcome <br />
to the LAB.
</h1>
<div className="w-full h-full absolute left-0 top-0 grid grid-cols-6">
{array.map((e, idx) => (
<div
key={idx}
className="bg-black relative"
onMouseEnter={() => setDissapear((prev) => [...prev, idx])}
>
{dissapear.some((e) => e === idx) && (
<Image src={e} alt="background" layout="fill" />
)}
</div>
))}
</div>
</div>
</PageLayout>
I'm using next js, thanks
No sure to be sure to understand, but you seems using tailwindcss.
maybe group in tailwind doc might be what you are look for.

React and tailwind: making tooltip

I am trying to make tooltips for icons in my header, and right now I have made a component to make the icon and tooltip into one div, with tailwind styles in that
JSX Component:
const HeaderIcon = ({ icon, redirect = window.location.pathname.replace('/', ""), text = ""}:IconProps) => (
<div className="group relative flex items-center justify-center h-12 w-12 mt-2 mb-2 mx-5 cursor-pointer">
<button onClick={() => goto(redirect)} className="items-center inline-flex">{icon}</button>
<span className="group-hover:visible absolute rounded-md shadow-md text-white bg-gray-900 text-xs font-bold transition-all duration-100 p-2 text-center min-w-max invisible">
{text}
</span>
</div>
)
Here goto is a redirect function, icon is a JSX element from react-feather (icon library), redirect is a string telling the button where to redirect to, and text is the tooltip text. What tailwind classes should I use to position the tooltips under the icons, centered.
If there is any other things you may want then just ask and I will edit the code in.
you need to create a new variant group-hover for the plugin visibility in the Tailwind configuration:
// tailwind.config.js
module.exports = {
// ...
variants: {
extend: {
visibility: ['group-hover'],
}
},
}

Tailwind css don't show hover state when input is in focus

On the following div:
<div className='border border-gray-400
text-gray-600
hover:border-gray-600
focus:border-green-500
focus-within:border-green-500'>
I want the hover state not to happen when an element is an in-focus state. How can I make it so?
Thanks!
Use the css :not selector, e.g. hover(:not focus):border-gray-600
Try this, border need to define width, so in this example I gave just 2 when it hovered.
check official Doc
<div className='hover:border-2 border-gray-400 text-gray-600
hover:border-gray-600
focus:border-green-500 focus-within:border-green-500'>
export const FramelessInput = ({ name }) => {
const [focused, setFocused] = useState(false);
return (
<div
className={classNames(
'w-[200px] rounded-sm border border-transparent text-gray-600 focus-within:border-green-500',
!focused && 'hover:border-gray-600'
)}
>
<input
type="text"
placeholder={name}
onFocus={(e) => setFocused(true)}
onBlur={(e) => setFocused(false)}
className="block text-sm leading-4 font-sans p-3 h-10 focus:outline-none"
/>
</div>
);
};
Try use property "ring"
<div className='border border-gray-400
text-gray-600
ring-green-500
focus-within:ring-2'>

Resources