Headless UI & React Dropdown menu changing position when clicked - javascript

I want to create a dropdown menu, for which I'm using HeadlessUI on React with Tailwind.
The menu is working fine, except that when clicked on, the position of the menu changes, as in instead of dropping down perfectly from the menu button it overlays it (I don't know how to describe it exactly).
Here's a before and after, hope you can make out what I'm trying to say:
Before button is clicked
After the button is clicked
The code for the dropdown menu:
function HomeMenu(props) {
return (
<Menu>
<Menu.Button>{props.name}</Menu.Button>
<Transition
enter="transition duration-100 ease-out"
enterFrom="transform scale-95 opacity-0"
enterTo="transform scale-100 opacity-100"
leave="transition duration-75 ease-out"
leaveFrom="transform scale-100 opacity-100"
leaveTo="transform scale-95 opacity-0"
></Transition>
<Menu.Items>
<Menu.Item>
{({ active }) => (
<a href="/account-settings">
Account settings
</a>
)}
</Menu.Item>
<Menu.Item>
{({ active }) => (
<a href="/account-settings">
Logout
</a>
)}
</Menu.Item>
</Menu.Items>
</Menu>
)
}
The div in which the menu is used:
<div className="flex items-center justify-center p-6">
<h1 className="text-6xl px-3 rounded-md shadow-lg py-1 bg-lmgray">
NEW STREET JOURNAL
</h1>
<div className="absolute right-20 mt-2 px-5 rounded-md border-2 border-gray shadow-lg py-1 bg-lmgray" >
<HomeMenu name={props.name}/>
</div>
</div>

<div className="absolute right-20 mt-2 px-5 rounded-md border-2 border-gray shadow-lg py-1 bg-lmgray" >
<HomeMenu name={props.name}/>
</div>
try to remove the classname absolute and try again

Related

fixed element not filling the full width of the screen tailwind

I'm trying to fill the width of a fixed element in Tailwind to full width.
here's a screenshot of the modal:
Modal Screenshot
please ignore the text inside it as I'm still testing stuff out.
my code React:
import { Dialog, Transition } from '#headlessui/react'
import { Fragment, useState } from 'react'
import { Link } from 'react-router-dom'
export default function Modal2() {
let [isOpen, setIsOpen] = useState(true)
function closeModal() {
setIsOpen(false)
}
function openModal() {
setIsOpen(true)
}
return (
<>
<div className=" absolute right-1 top-5 p-2">
<button
type="button"
onClick={openModal}
className="flex rounded-3xl pl-6 pr-6 bb p-2 "
>
Open dialog
</button>
</div>
<Transition appear show={isOpen} as={Fragment}>
<Dialog as="div" className="relative z-10 " onClose={closeModal}>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed flex inset-0 bg-black bg-opacity-80 blur2 " />
</Transition.Child>
<div className="fixed top-20 right-2 overflow-y-auto ">
<div className="flex justify-center p-4 text-center">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className="mb-10 w-full max-w-md transform overflow-hidden rounded-3xl bg-white p-6 text-left align-top shadow-xl transition-all">
<Dialog.Title
as="h3"
className="text-lg font-medium leading-6 text-gray-900"
>
Lorem, ipsum dolor.
</Dialog.Title>
<div className="mt-2">
<p className=" flex flex-col text-md text-gray-500">
<Link to={"/about"} className="py-1 ">dada</Link>
<span className='h bg-red-600'></span>
<Link className="py-1 " to={""}>dada</Link>
<span className='h bg-red-600'></span>
<Link className="py-1 " to={""}>dada</Link>
<span className='h bg-red-600'></span>
<Link className="py-1 " to={""}>dada</Link>
</p>
</div>
<div className="mt-4">
<button
type="button"
className="inline-flex justify-center rounded-md border border-transparent bg-blue-100 px-4 py-2 text-sm font-medium text-blue-900 hover:bg-blue-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
onClick={closeModal}
>
Got it, thanks!
</button>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition>
</>
)
}
this is the line that opens the Modal. I want to make the width stretch to full-screen width. :
<div className="fixed flex inset-0 bg-black bg-opacity-80 blur2 " />
I would appreciate any help.
I tried to use items-stretch and w-full. didn't work. I guess its because it's fixed.
I couldn't reproduce your problem exactly, so this might be off.
If I understand your goal correctly, you're targeting the wrong element. The Dialog component contains your modal, so that's the component which should
have display: flex and justify-content: center to centralize the modal horizontally.
So you should add the classes flex and justify-center to the Dialog component. You can also add items-center to centralize it vertically.
[...]
<Transition appear show={isOpen} as={Fragment}>
<Dialog as="div" className="relative z-10 flex justify-center" onClose={closeModal}>
[...]

Popover on hover vue headllessui

I'm trying to implement the popover from headlessui of vue package with hover. I try to use the mouseenter and mouseleave and the other mouse events but nothing change.
Any solution? There is a better solution? i search on internet I cant nothing about this. I search on headlessui github discussions but nothing.
<template>
<div class="fixed top-16 w-full max-w-sm px-4">
<Popover v-slot="{ open }" class="relative">
<PopoverButton
:class="open ? '' : 'text-opacity-90'"
class="group inline-flex items-center rounded-md bg-orange-700 px-3 py-2 text-base font-medium text-white hover:text-opacity-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75"
>
<span>Solutions</span>
<ChevronDownIcon
:class="open ? '' : 'text-opacity-70'"
class="ml-2 h-5 w-5 text-orange-300 transition duration-150 ease-in-out group-hover:text-opacity-80"
aria-hidden="true"
/>
</PopoverButton>
<transition
enter-active-class="transition duration-200 ease-out"
enter-from-class="translate-y-1 opacity-0"
enter-to-class="translate-y-0 opacity-100"
leave-active-class="transition duration-150 ease-in"
leave-from-class="translate-y-0 opacity-100"
leave-to-class="translate-y-1 opacity-0"
>
<PopoverPanel
class="absolute left-1/2 z-10 mt-3 w-screen max-w-sm -translate-x-1/2 transform px-4 sm:px-0 lg:max-w-3xl"
>
<div
class="overflow-hidden rounded-lg shadow-lg ring-1 ring-black ring-opacity-5"
>
<div class="relative grid gap-8 bg-white p-7 lg:grid-cols-2">
<a
v-for="item in solutions"
:key="item.name"
:href="item.href"
class="-m-3 flex items-center rounded-lg p-2 transition duration-150 ease-in-out hover:bg-gray-50 focus:outline-none focus-visible:ring focus-visible:ring-orange-500 focus-visible:ring-opacity-50"
>
<div
class="flex h-10 w-10 shrink-0 items-center justify-center text-white sm:h-12 sm:w-12"
>
<div v-html="item.icon"></div>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-900">
{{ item.name }}
</p>
<p class="text-sm text-gray-500">
{{ item.description }}
</p>
</div>
</a>
</div>
<div class="bg-gray-50 p-4">
<a
href="##"
class="flow-root rounded-md px-2 py-2 transition duration-150 ease-in-out hover:bg-gray-100 focus:outline-none focus-visible:ring focus-visible:ring-orange-500 focus-visible:ring-opacity-50"
>
<span class="flex items-center">
<span class="text-sm font-medium text-gray-900">
Documentation
</span>
</span>
<span class="block text-sm text-gray-500">
Start integrating products and tools
</span>
</a>
</div>
</div>
</PopoverPanel>
</transition>
</Popover>
</div>
</template>
Seems like common request in HeadlessUI community.
Another solution found this solution on Github that worked fine for me.
for vanilla vue 3 with js the original solution link Github Issue
Nuxt 3 with typescript maintaining accessibility here's the code ↓
<script setup lang="ts">
import { Popover, PopoverButton, PopoverPanel } from '#headlessui/vue'
interface Props {
label: string
hasHref?: boolean
href?: string
}
const props = defineProps<Props>()
const popoverHover = ref(false)
const popoverTimeout = ref()
const hoverPopover = (e: any, open: boolean): void => {
popoverHover.value = true
if (!open) {
e.target.parentNode.click()
}
}
const closePopover = (close: any): void => {
popoverHover.value = false
if (popoverTimeout.value) clearTimeout(popoverTimeout.value)
popoverTimeout.value = setTimeout(() => {
if (!popoverHover.value) {
close()
}
}, 100)
}
</script>
<template>
<Popover v-slot="{ open, close }" class="relative">
<PopoverButton
:class="[
open ? 'text-primary' : 'text-gray-900',
'group inline-flex items-center rounded-md bg-white text-base font-medium hover:text-primary focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2'
]"
#mouseover="(e) => hoverPopover(e, open)"
#mouseleave="closePopover(close)"
>
<span v-if="!hasHref">{{ props.label }}</span>
<span v-else>
<NuxtLink :to="href">
{{ props.label }}
</NuxtLink>
</span>
<IconsChevronDown
:class="[
open ? 'rotate-180 transform text-primary' : '',
' ml-1 h-5 w-5 text-primary transition-transform group-hover:text-primary'
]"
aria-hidden="true"
/>
</PopoverButton>
<transition
enter-active-class="transition ease-out duration-200"
enter-from-class="opacity-0 translate-y-1"
enter-to-class="opacity-100 translate-y-0"
leave-active-class="transition ease-in duration-150"
leave-from-class="opacity-100 translate-y-0"
leave-to-class="opacity-0 translate-y-1"
>
<PopoverPanel
class="absolute left-1/2 z-10 mt-3 ml-0 w-auto min-w-[15rem] -translate-x-1/2 transform px-2 sm:px-0"
#mouseover.prevent="popoverHover = true"
#mouseleave.prevent="closePopover(close)"
>
<div
class="overflow-hidden rounded-lg shadow-lg ring-1 ring-black ring-opacity-5"
>
<div class="relative grid gap-1 bg-white p-3">
<slot> put content here </slot>
</div>
</div>
</PopoverPanel>
</transition>
</Popover>
</template>
It is in the docs:showing-hiding-popover.
open is an internal state used to determine if the component is shown or hidden. To implement your own functionality, you can remove it and use the static prop to always render a component. Then you can mange the visibility with your own state ref and a v-if/v-show. The mouse-action has to be in the upper scope, so it is not triggered leaving the component, e.g. moving the mouse from button to panel.
Below is a modified example from the API documentation:
<template>
<Popover
#mouseenter="open = true"
#mouseleave="open = false"
#click="open = !open"
>
<PopoverButton #click="open = !open">
Solutions
<ChevronDownIcon :class="{ 'rotate-180 transform': open }" />
</PopoverButton>
<div v-if="open">
<PopoverPanel static>
Insights
Automations
Reports
</PopoverPanel>
</div>
</Popover>
</template>
<script setup>
import { ref } from 'vue';
import { Popover, PopoverButton, PopoverPanel } from '#headlessui/vue';
import { ChevronDownIcon } from '#heroicons/vue/20/solid';
const open = ref(false);
</script>

I have to click 2 times to the link in mobile view of my Next JS application in order to change routes

I am building a Next JS application,
On the desktop view, there is no problem, but when it changes to the mobile view after a scroll event or changing routes when I want to click on a link/button, etc. I need to click two times on it. I am suspicious that after an event, the focus on the page is being lost, but there is no overflow or something. I couldn't figure out where the focus was gone.
Here is an example of my code
<Link href={storyUrl}>
<a className="group inline-flex flex-col mb-4 md:mb-11 space-y-2 ">
<div className="flex items-center gap-2">
{isDeleted && (
<div className="group">
<Info className="text-slate-400" />
<div className="hidden rounded-lg p-2 text-xs bg-gray-100 absolute z-50 group-hover:block shadow-md w-48 mt-5">
This story has been deleted or in draft
</div>
</div>
)}
{draft && (
<span className="inline-flex items-center px-3 py-0.5 rounded-full text-sm font-medium bg-purple-50 text-purple-800">
Draft
</span>
)}
<h2
className={`text-slate-900 text-3xl font-semibold leading-9 tracking-md transition ease-in-out duration-150 group-hover:text-purple-700 ${
isDeleted ? 'line-through' : ''
}`}
>
{title}
</h2>
</div>
<p className="text-slate-500 text-sm tracking-sm line-clamp-2 break-words w-full xl:w-[600px] ">
{infoText}
</p>
</a>
</Link>
Thanks for any responses in advance
I tried to change the Links to the router.push but as I thought it was irrelevant.

react modal unable to open

i am trying to use the headless ui modal but i am trying to launch this modal.js from my home.js file.
so in my home.js file i have something like this
function Home() {
const [isOpen, setIsOpen] = useState(false)
const resultCKDArr = Object.values(responseData.ckd).map((item, i)=>
<section class="text-gray-600 body-font overflow-hidden">
<div class="container px-5 py-10 mx-auto">
<button onClick={() => setIsOpen(!isOpen)} class="flex ml-auto text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded">Buy List</button>
</div>
<Modal isOpen={isOpen} setIsOpen={setIsOpen} />
</section>
)
return (
<>
{resultCKDArr }
</>
);
export default Home;
and this is my modal.js copy from headless ui modal as example to test
import React, { Fragment } from "react";
import { Dialog, Transition } from '#headlessui/react'
export default function Modal( props ) {
const { isOpen, setIsOpen } = props
return (
<Transition appear show={isOpen} as={Fragment}>
<Dialog as="div" className="relative z-10" onClose={setIsOpen}>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-black bg-opacity-25" />
</Transition.Child>
<div className="fixed inset-0 overflow-y-auto">
<div className="flex min-h-full items-center justify-center p-4 text-center">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all">
<Dialog.Title
as="h3"
className="text-lg font-medium leading-6 text-gray-900"
>
Payment successful
</Dialog.Title>
<div className="mt-2">
<p className="text-sm text-gray-500">
Your payment has been successfully submitted. We’ve sent
you an email with all of the details of your order.
</p>
</div>
<div className="mt-4">
<button
type="button"
className="inline-flex justify-center rounded-md border border-transparent bg-blue-100 px-4 py-2 text-sm font-medium text-blue-900 hover:bg-blue-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
onClick={setIsOpen}
>
Got it, thanks!
</button>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition>
)
};
maybe this is the error i saw in console
Warning: React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
when i click on the button, i nothing happen, no modal pop out.
In my case there was an error with Dialog.Panel component. Try changing it to a div.

How to keep div size when showing submenu on top Tailwind and React

Using Tailwind an React - When clicking on an icon I show a submenu but it happens that when the container div is shown it becomes bigger in its height automatically.
How can I display this submenu on top of the other elements so that it doesn't make its container div bigger? I tried with fixed but it moves from the place I require it to be displayed.
This is the code:
Table where I show the submenu when clicking:
<div className="flex flex-wrap m-5 mx-auto w-full pr-2 pl-2">
<div className="w-full px-4">
<div className="flex flex-wrap">
<div className="flex flex-col min-w-0 break-words w-full mb-6 shadow-lg rounded bg-white">
<div className="block w-full overflow-x-auto">
<table className="items-center w-full bg-transparent border-collapse">
<thead className="bg-sky-50">
<tr>
<th className="px-6 align-middle border border-solid py-3 text-xs uppercase border-l-0 border-r-0 whitespace-nowrap font-semibold bg-blueGray-50 text-blueGray-500 border-blueGray-100">
Opciones
</th>
</tr>
</thead>
<tbody>
<tr className="hover:bg-slate-100">
<td className="border-t-0 px-6 align-middle border-l-0 text-center border-r-0 text-xs whitespace-nowrap p-4">
<div>
<OptionsDropDown
showDropdown= { showDropdown }
setShowDropdown = { setShowDropdown }
dataDropDown= { dataDropDown }
/>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
Menu Component, Here I have the icon and what I show when I click on it but it widens the parent div:
<div className="flex justify-center">
<button ref={ref}
className="flex items-center
text-black py-2 px-2
text-2xl hover:text-cyan-700"
onClick={ ()=> setShowDropdown(!showDropdown) }>
<FiMoreVertical/>
</button>
{
showDropdown && (
<div className="relative bg-white text-base z-50 py-2 rounded shadow-lg">
{
dataDropDown.map( (option, index) => (
<div key={index}>
{option.edit}
{option.delete}
</div>
))
}
</div>
)
}
</div>
Here is a screenshot of what it looks like right now on click, making the size of the parent div higher:
Here it is before clicking:
Clicking the container div makes it wide:
Using abosulte on submenu
You're most likely looking for absolute positioning using a relative positioning on the direct parent so that the position of the absolute element is anchored to its immediate parent.
Something like this should work:
<div className="relative flex justify-center">
{/* NOTE: the relative positioning above */}
<button ref={ref}
className="flex items-center
text-black py-2 px-2
text-2xl hover:text-cyan-700"
onClick={ ()=> setShowDropdown(!showDropdown) }>
<FiMoreVertical/>
</button>
{
showDropdown && (
<div className="absolute right-0 top-1/2 -translate-y-1/2 bg-white text-base z-50 py-2 rounded shadow-lg">
{/* which allows this to anchor */}
{
dataDropDown.map( (option, index) => (
<div key={index}>
{option.edit}
{option.delete}
</div>
))
}
</div>
)
}
</div>
although I expect you'll have to tweak it slightly to get the results that you want. It might also be worth looking into the blur event to automatically hide the popup once it loses focus.

Categories

Resources