I use navbar component from tailwind ui. It's looks like something like this:
<!-- Profile dropdown -->
<div class="ml-3 relative">
<div>
<button
class="flex text-sm border-2 border-transparent rounded-full focus:outline-none focus:border-white transition duration-150 ease-in-out"
id="user-menu"
aria-label="User menu"
aria-haspopup="true"
>
<img
class="h-8 w-8 rounded-full"
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
alt
/>
</button>
</div>
<!--
Profile dropdown panel, show/hide based on dropdown state.
Entering: "transition ease-out duration-100"
From: "transform opacity-0 scale-95"
To: "transform opacity-100 scale-100"
Leaving: "transition ease-in duration-75"
From: "transform opacity-100 scale-100"
To: "transform opacity-0 scale-95"
-->
<div class="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg">
<div
class="py-1 rounded-md bg-white shadow-xs"
role="menu"
aria-orientation="vertical"
aria-labelledby="user-menu"
>
<a
href="#"
class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out"
role="menuitem"
>Your Profile</a>
<a
href="#"
class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out"
role="menuitem"
>Settings</a>
<a
href="#"
class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out"
role="menuitem"
>Sign out</a>
</div>
</div>
</div>
In this case when I run this code in vue.js navbar dropdown menu status is open by default. How can set status closed by defaul?
Here is preview:
I'm not sure if anyone is following this question right now but I'm sharing my solution.
In the snippet to the dropdown code there was a comment saying:
<!--
Profile dropdown panel, show/hide based on dropdown state.
Entering: "transition ease-out duration-100"
From: "transform opacity-0 scale-95"
To: "transform opacity-100 scale-100"
Leaving: "transition ease-in duration-75"
From: "transform opacity-100 scale-100"
To: "transform opacity-0 scale-95"
-->
It's basically telling the state of dropdown is changing based on classes names so you'd have to make them dynamic like this:
<div :class="`origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg transition ease-${dropdown ? 'out' : 'in'} duration-${dropdown ? '100' : '75'} transform opacity-${dropdown ? '100' : '0'} scale-${dropdown ? '100' : '95'}`">
now the classes will depend on the dropdown value which is just a property of your component that can be changed via a click event like this:
export default {
name: 'TheNavBar',
data() {
return {
dropdown: false,
}
},
}
<div>
<button
id="user-menu"
class="flex text-sm border-2 border-transparent rounded-full focus:outline-none focus:border-white transition duration-150 ease-in-out"
aria-label="User menu"
aria-haspopup="true"
#click="dropdown = !dropdown"
>
</button>
</div>
I was using alpinejs and included it using CDN.
It worked for me when I put the following in the div that was wrapping the whole component:
x-data="{ open: false }"
The below went into the (User profile image) button div
#click="open = true"
And finally, this went into the drop-down div
x-show="open" #click.away="open = false"
I'm exactly using the same component and came here to find an answer :(
Since nobody answered it, here is where I've been:
It's explicitly said that you'll need to use Javascript with some Tailwind UI component like this one.
But I've done with no JS, only a CSS tricks and a slightly different markup and a simpler animation (but still smooth! You can see the fiddle on the bottom of this answer).
The markup: I've just removed the div wraper arround the avatar button to benefits from the ~ CSS selector, and I've added an id #user-menu-dropdown:
<!-- Profile dropdown -->
<div class="ml-3 relative">
<button
class="flex text-sm border-2 border-transparent rounded-full focus:outline-none focus:border-white transition duration-150 ease-in-out"
id="user-menu" aria-label="User menu" aria-haspopup="true">
<img class="h-8 w-8 rounded-full"
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
alt=""/>
</button>
<div id="user-menu-dropdown" class="menu-hidden origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg">
<div
class="py-1 rounded-md bg-white shadow-xs"
role="menu"
aria-orientation="vertical"
aria-labelledby="user-menu"
>
<a
href="#"
class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out"
role="menuitem"
>Your Profile</a>
<a
href="#"
class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out"
role="menuitem"
>Settings</a>
<a
href="#"
class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out"
role="menuitem"
>Sign out</a>
</div>
</div>
</div>
And here is my LESS
#user-menu ~ #user-menu-dropdown
#apply transform
#apply ease-in duration-75 opacity-0 scale-0
#user-menu ~ #user-menu-dropdown:focus-within, #user-menu:focus ~ #user-menu-dropdown
#apply ease-out duration-100 opacity-100 scale-100
And here is the results, using the generated CSS
Hope it will help you
If the Alpine JS is working fine. Just Copy-paste this code and you will be good to go.
<!-- Profile dropdown -->
<div class="ml-3 relative" x-data="{ dropdown: false }">
<div>
<button x-on:click="dropdown = ! dropdown" type="button" class="max-w-xs bg-gray-800 rounded-full flex items-center text-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-white" id="user-menu-button" aria-expanded="false" aria-haspopup="false">
<span class="sr-only">Open user menu</span>
<img class="h-8 w-8 rounded-full" src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="">
</button>
</div>
<div x-show="dropdown" class="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none" role="menu" aria-orientation="vertical" aria-labelledby="user-menu-button" tabindex="-1">
<!-- Active: "bg-gray-100", Not Active: "" -->
Your Profile
Settings
Sign out
</div>
</div>
Explanation of what I do
I just simply added x-data="{ dropdown: false }" in the parent div to hide the menu at the initial state.
Then, I added x-on:click="dropdown = ! dropdown" on the button to toggle the menu.
And lastly, I added x-show="dropdown" in the menu div. to show or hide the dropdown based on its value.
You can use any text at the spot of dropdown. Because its works like a variable.
As pointed out in previous answers one can use Vue.js or some smart CSS to solve the problem. If you don't want to make your page heavy by using Vue or use CSS which has limitations then you can use alpinejs. This is what Tailwindui is using in their demo.
You can use alpinejs either by installing it via yarn or npm or simply install it from CDN.
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine#v2.7.0/dist/alpine.min.js" defer></script>
You don't need to write even one line of javascript. If you installed via npm or yarn then import it to your project.
import "alpinejs";
Open the HTML with navigation code.
Add x-data directive.
<!-- Profile dropdown -->
<div class="ml-3 relative" x-data="open = false">
Now add x-click directive to the element that is clicked to reveal the dropdown.
<button
class="flex text-sm border-2 border-transparent rounded-full focus:outline-none focus:border-white transition duration-150 ease-in-out"
id="user-menu"
aria-label="User menu"
aria-haspopup="true"
x-on:click="open=true"
>
<img
class="h-8 w-8 rounded-full"
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
alt
/>
</button>
Finally modify the div that nests the dropdown elements.
<div class="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg" x-show="open" x-on:click.away="open = false">
x-on:click.away directive will close the dropdown when mouse is clicked anywhere else.
To polish it off you can use Alpine transition directives to the previous div.
<div class="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg" x-show="open" x-on:click.away="open = false" x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0 -translate-y-1" x-transition:enter-end="opacity-100 translate-y-0" x-transition:leave="transition ease-in duration-150" x-transition:leave-start="opacity-100 translate-y-0" x-transition:leave-end="opacity-0 -translate-y-1">
If you have more than two dropdowns then use data directive as under
<div x-data="{openDropdown1: false, openDropdown2: false}"
Tailwind UI only provides example static html. You'll need to use your JS framework (Vue.js) to dynamically generate similar html that varies based on the state of your app. In general you need to:
1- add a boolean state variable that controls whether the menu is currently displayed or not. Initialize it to false so the menu is hidden by default.
data() {
return {
show: false,
}
}
2- Add a click handler to the menu button to toggle this variable.
<button #click="show = !show">Menu</button>
3- Only render the menu when show is true. You can do this with v-if
<div v-if="show" class="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg">
//...menu content
</div>
4- Animate the menu when it gets displayed or hidden. You can do this by wrapping the menu in a Vue.js <transition> component.
<transition
enter-active-class="transition ease-out duration-100"
enter-from-class="transform opacity-0 scale-95"
enter-to-class="transform opacity-100 scale-100"
leave-active-class="transition ease-in duration-75"
leave-from-class="transform opacity-100 scale-100"
leave-to-class="transform opacity-0 scale-95"
>
<div v-if="show" class="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg">
//...menu content
</div>
</transition>
Here is a full working example in Vue 3:
https://codepen.io/mpvosseller/pen/RwoNaVz
Notes:
In Vue 2 you should use enter-class and leave-class instead of enter-from-class and leave-from-class
In React you can use Tailwind's Headless UI React Transition component for similar functionality.
use v-on:click and v-show
<!-- ProfileNavDropdown.vue -->
<template>
<div class="ml-3 relative">
<div v-on:click="isActive = !isActive">
<button
class="flex text-sm border-2 border-transparent rounded-full focus:outline-none focus:border-white transition duration-150 ease-in-out"
id="user-menu"
aria-label="User menu"
aria-haspopup="true"
>
<img
class="h-8 w-8 rounded-full"
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
alt
/>
</button>
</div>
<!--
Profile dropdown panel, show/hide based on dropdown state.
Entering: "transition ease-out duration-100"
From: "transform opacity-0 scale-95"
To: "transform opacity-100 scale-100"
Leaving: "transition ease-in duration-75"
From: "transform opacity-100 scale-100"
To: "transform opacity-0 scale-95"
-->
<div v-show="isActive" class="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg">
<div
class="py-1 rounded-md bg-white shadow-xs"
role="menu"
aria-orientation="vertical"
aria-labelledby="user-menu"
>
<a
href="#"
class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out"
role="menuitem"
>Your Profile</a>
<a
href="#"
class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out"
role="menuitem"
>Settings</a>
<a
href="#"
class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out"
role="menuitem"
>Sign out</a>
</div>
</div>
</div>
</template>
<script>
export default {
data: function () {
return {
isActive: false,
}
},
}
</script>
I had this same issue, here is a solution I discovered from my little research:
<!-- vue template -->
<template>
<div class="relative">
<button #click="isOpen = !isOpen" class="relative z-10 block h-8 w-8 rounded-full overflow-hidden border-2 border-gray-600 focus:outline-none focus:border-white">
<img class="h-full w-full object-cover" src="https://images.unsplash.com/photo-1487412720507-e7ab37603c6f?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=256&q=80" alt="Your avatar">
</button>
<button v-if="isOpen" #click="isOpen = false" tabindex="-1" class="fixed inset-0 h-full w-full bg-black opacity-50 cursor-default"></button>
<div v-if="isOpen" class="absolute right-0 mt-2 py-2 w-48 bg-white rounded-lg shadow-xl">
Account settings
Support
Sign out
</div>
</div>
</template>
<script>
//javascript
export default {
data() {
return {
isOpen: false
}
},
created() {
const handleEscape = (e) => {
if (e.key === 'Esc' || e.key === 'Escape') {
this.isOpen = false
}
}
document.addEventListener('keydown', handleEscape)
this.$once('hook:beforeDestroy', () => {
document.removeEventListener('keydown', handleEscape)
})
}
}
</script>
I hope this helps, you can see more here
Adding to #Andreas Hunter's solution, you may use the more convenient headless-ui (Tailwind CSS) for Vue (click here). It is also available for React. Headless UI is designed to integrate beautifully with Tailwind CSS.
Use Menu component for dropdowns:
<template>
<Menu>
<MenuButton> More </MenuButton>
<MenuItems>
<MenuItem v-slot="{ active }">
<a :class="{ 'bg-blue-500': active }" href="/account-settings"> Account settings </a>
</MenuItem>
<MenuItem v-slot="{ active }">
<a :class="{ 'bg-blue-500': active }" href="/account-settings"> Documentation </a>
</MenuItem>
<MenuItem v-slot="{ active }" disabled>
<span :class="{ 'bg-blue-500': active }"> Invite a friend (coming soon!) </span>
</MenuItem>
</MenuItems>
</Menu>
</template>
<script>
import { Menu, MenuButton, MenuItems, MenuItem } from '#headlessui/vue'
export default {
components: {
Menu,
MenuButton,
MenuItems,
MenuItem,
},
}
</script>
You don't have to worry about its state anymore.
Since tailwind by default handles the dropdown (open and close, even when you click outside the dropdown). It just programmatically hides the dropdown.
I used a javascript one-line solution by simulating a click on the document body. That way, the dropdown automatically close while you still maintain its animations. And you could simulate a click event on the button/element to open the dropdown.
document.querySelector('body').click()
For anyone struggling with this, the solution is much simpler than all the replies above.
Add this class to the div (so it is invisible when the page first loads): "invisible".
Add another class called "profile-menu".
<div class="invisible profile-menu" role="menu" aria-orientation="vertical" aria-labelledby="user-menu-button" tabindex="-1">
In Javascript at the bottom of your page add this:
const navbarBtn = document.querySelector(".close-profile-bar");
const profileMenu = document.querySelector(".profile-menu");
navbarBtn.addEventListener("click", () => {
let results = profileMenu.classList.contains('invisible');
if(results){
profileMenu.classList.remove('invisible');
profileMenu.classList.add('visible');
} else {
profileMenu.classList.remove('visible');
profileMenu.classList.add('invisible');
}
});
The above will just remove or add the invisible or invisible class to the profile div.
Related
I build an app in React and I have this dropdown from Tailwind UI which is opened on click events and I want to make it open on mouse hover too.
<Menu as="div" className="relative inline-block text-left ">
<div>
<Menu.Button className="inline-flex w-full justify-center rounded-md bg-white px-4 py-2 text-sm font-medium text-gray-500 hover:bg-gray-50 focus:outline-none focus:ring-offset-gray-100">
Features
<ChevronDownIcon
className="-mr-1 ml- h-5 w-5"
aria-hidden="true"
/>
</Menu.Button>
</div>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="absolute left-0 z-10 mt-2 w-56 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
<div className="py-1">
<Menu.Item>
{({ active }) => (
<Link
to="#"
className={classNames(
active ? "bg-primary-light-bk text-gray-900" : "text-gray-500",
"block px-4 py-2 text-sm"
)}
>
Feature 1
</Link>
)}
</Menu.Item>
<Menu.Item>
{({ active }) => (
<Link
to="#"
className={classNames(
active ? "bg-primary-light-bk text-gray-900" : "text-gray-500",
"block px-4 py-2 text-sm"
)}
>
Feature 2
</Link>
)}
</Menu.Item>
</div>
</Menu.Items>
</Transition>
</Menu>
What should I change?
You'll need to add onMouseEnter event to your Menu.Button and use handler for this event.
Get argument event and then use event.target in handler or { target } and then use just target. then use .click() method that simulates a mouse click on the target element.
When you hover over Menu.Button onMouseEnter has triggered and .click method fires the element's click event. Menu is opened.
<Menu.Button onMouseEnter={({target})=> target.click()} className="...">
Features
<ChevronDownIcon
className="-mr-1 ml- h-5 w-5"
aria-hidden="true"
/>
</Menu.Button>
If you try this you'll see that when you hover the menu.button two times - menu's opened then closed. That because .click() method fires two times.
You can use "open" state. Wrap all of your code inside Menu
{({ open }) => (your code)}
And add
onMouseEnter={({target})=> open ? "" : target.click()}.
"open" is boolean. True when menu is opened, no need to trigger click event and close it. When "open" is false we'll use our target.click() method!
<Menu as="div" className="relative inline-block text-left ">
{({open}) => (
<div>
<div>
<Menu.Button onMouseEnter={({target})=> open ? "" : target.click()} className="inline-flex w-full justify-center rounded-md bg-white px-4 py-2 text-sm font-medium text-gray-500 hover:bg-gray-50 focus:outline-none focus:ring-offset-gray-100">
Features
<ChevronDownIcon
className="-mr-1 ml- h-5 w-5"
aria-hidden="true"
/>
</Menu.Button>
</div>
... your code ...
</div>
)}
</Menu>
like the title can tell you already, I'm experiencing an issue when trying to create an image modal.
When I click the button that should open the image, it works fine, but when I click away and try to open it again, it does nothing. I couldn't find anything in the console that gives an error.
<script src="https://cdn.tailwindcss.com"></script>
<script defer src="https://unpkg.com/alpinejs#3.x.x/dist/cdn.min.js"></script>
<div x-data="{ lightbox: false, imgModalSrc : '', imgModalAlt : '', imgModalDesc : '' }">
<button class="block w-24 p-3 mb-4 text-white bg-gray-600 rounded" #click="$dispatch('lightbox', { imgModalSrc: 'https://via.placeholder.com/1000x700/066/f0f', imgModalAlt: 'First Image', imgModalDesc: 'Random Image One Description' })">#1</button>
<button class="block w-24 p-3 text-white bg-gray-600 rounded" #click="$dispatch('lightbox', { imgModalSrc: 'https://via.placeholder.com/1000x700/600/099' })">#2</button>
<template #lightbox.window="lightbox = true; imgModalSrc = $event.detail.imgModalSrc; imgModalDesc = $event.detail.imgModalDesc;" x-if="lightbox">
<div x-transition:enter="transition ease-out duration-300" x-transition:enter-start="opacity-0 transform scale-90" x-transition:enter-end="opacity-100 transform scale-100" x-transition:leave="transition ease-in duration-300" x-transition:leave-start="opacity-100 transform scale-100" x-transition:leave-end="opacity-0 transform scale-90" class="fixed inset-0 z-50 flex items-center justify-center w-full p-2 overflow-hidden bg-black bg-opacity-75 h-100">
<div #click.away="lightbox = ''" class="">
<img class="" :src="imgModalSrc" :alt="imgModalAlt">
</div>
</div>
</template>
</div>
Thanks in advance for any help.
You have a strange race condition between the x-if and #click.away: when you click on the button second time it flips lightbox for a moment, then immediately #click.away flips it back, so you wont see the modal.
I'm not 100% sure what causes this, my guess is that since x-if actually removes/readds the respective element from the DOM tree, maybe for the second time its much faster, so at the same time, the #click.away also fires.
Anyways, ff you use <div x-show="lightbox"> instead of <template x-if="lightbox"> it's working fine. The x-show directive does remove the element from the DOM, just hides it with CSS.
<script src="https://cdn.tailwindcss.com"></script>
<script defer src="https://unpkg.com/alpinejs#3.x.x/dist/cdn.min.js"></script>
<div x-data="{ lightbox: false, imgModalSrc : '', imgModalAlt : '', imgModalDesc : '' }">
<button class="block w-24 p-3 mb-4 text-white bg-gray-600 rounded" #click="$dispatch('lightbox', { imgModalSrc: 'https://via.placeholder.com/1000x700/066/f0f', imgModalAlt: 'First Image', imgModalDesc: 'Random Image One Description' })">#1</button>
<button class="block w-24 p-3 text-white bg-gray-600 rounded" #click="$dispatch('lightbox', { imgModalSrc: 'https://via.placeholder.com/1000x700/600/099' })">#2</button>
<div x-show="lightbox" #lightbox.window="lightbox = true; imgModalSrc = $event.detail.imgModalSrc; imgModalDesc = $event.detail.imgModalDesc;">
<div x-transition:enter="transition ease-out duration-300" x-transition:enter-start="opacity-0 transform scale-90" x-transition:enter-end="opacity-100 transform scale-100" x-transition:leave="transition ease-in duration-300" x-transition:leave-start="opacity-100 transform scale-100" x-transition:leave-end="opacity-0 transform scale-90" class="fixed inset-0 z-50 flex items-center justify-center w-full p-2 overflow-hidden bg-black bg-opacity-75 h-100">
<div #click.away="lightbox = ''" class="">
<img class="" :src="imgModalSrc" :alt="imgModalAlt">
</div>
</div>
</div>
</div>
I have the following inertia link component
<inertia-link v-for="project in $page.props.user.favorite_projects" :key="project.id" class="flex items-center px-6 py-2 mb-1 text-sm leading-6 text-gray-300 transition duration-150 ease-in-out group focus:outline-none focus:bg-gray-700 hover:text-white hover:bg-gray-700" :href="route('projects.show',project.slug)">
<div class="w-3 h-3 mr-3 overflow-hidden rounded-full">
<div class="w-full h-full" style="background-color = {{ project.color }};">
</div>
</div>
{{ project.project_name }}
</inertia-link>
Where project.color contains a hex color value like #fc8181, which is used to color the element based on the project.
However, the circle with this color value is not being shown in the dom.
Is there a way to display the project color as background color?
you need to bind the style, try style
:style="{ background-color: project.color }"
I want to access current data iteration from template, and also I need certain id's from iterated element.
<template x-for="(datas, idx) in data">
<tr x-bind:class="idx % 2 == 0 ? 'bg-white' : 'bg-green-100'" class="border-4 border-gray-200">
<td class="text-center py-2 w-2">
<span x-text="`${parseInt(idx) + 1}`"></span>
</td>
<td class="text-center py-2">
<strong x-text="datas.name"></strong>
<br />
<i x-text="`Num : ${datas.num}`"></i>
</td>
<td class="text-center py-2" >
<!--I need access datas.num in this area v--->
<div #click.away="open = false" class="relative" x-data="{open : false}">
<button #click="open = !open" class="bg-gray-300 flex flex-row items-center w-full px-4 py-2 mt-2 text-sm font-semibold text-left bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:focus:bg-gray-600 dark-mode:hover:bg-gray-600 md:w-auto md:inline md:mt-0 md:ml-4 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline">
<span>Action</span>
<svg fill="currentColor" viewBox="0 0 20 20" :class="{'rotate-180': open, 'rotate-0': !open}" class="inline w-4 h-4 mt-1 ml-1 transition-transform duration-200 transform md:-mt-1"><path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
</button>
<div x-show="open" x-transition:enter="transition ease-out duration-100" x-transition:enter-start="transform opacity-0 scale-95" x-transition:enter-end="transform opacity-100 scale-100" x-transition:leave="transition ease-in duration-75" x-transition:leave-start="transform opacity-100 scale-100" x-transition:leave-end="transform opacity-0 scale-95" class="absolute right-0 w-full mt-2 origin-top-right rounded-md shadow-lg md:w-48" style="z-index: 1;">
<div class="px-2 py-2 bg-white rounded-md shadow dark-mode:bg-gray-800">
<a class="block px-4 py-2 mt-2 text-sm font-semibold bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 md:mt-0 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline" href="#"><i class="fas fa-pencil-alt"></i> Edit</a>
<a #click="$parent.deleteData(datas.num)" class="block px-4 py-2 mt-2 text-sm font-semibold bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 md:mt-0 hover:text-white-900 focus:text-white-900 hover:bg-red-200 focus:bg-red-200 focus:outline-none focus:shadow-outline" href="#"><i class="fas fa-trash-alt"></i> Delete</a>
</div>
</div>
</div>
</td>
</tr>
</template>
and here's data definition :
function dataInit(){
return {
data : [
{
num : 10,
name : "josh"
}
],
deleteData(id){
//do something with current id selection
}
}
}
I do can access method of deleteData(id) in parent because using Alpine's magic helper, but I can't access datas.num or $parent.deleteData(datas.num) variable in loop block even they're inside template block. How do I resolve this problem?
Edit :
*Assuming the x-data for data loop is already set in upper element
** What I meant is that, I want to access the x-for in child element ( see comments on html's section). But I can't access it, maybe some scope problems that prevent me to do it. Starting in this area <div #click.away="open = false" class="relative" x-data="{open : false}">
Your deleteData function is within an object, so referring to other elements within that object requires this to refer to itself:
deleteData(id) {
let data = this.data;
}
will return the data variable.
Another thing, you've defined data as an array of objects. So, to access the num variable, you'd have to first access that specific object within the array, and then you can get the num value:
this.data[0].num should do the trick.
P.S. your code snippet is missing one closing bracket (})
Within Alpine, you don't need to refer to the object, since you should be setting it as x-data somewhere, from which you can then do whatever you wish with the data.
<div x-data="dataInit()">
<!-- Loop the array of data -->
<template x-for="singleData in data">
<div>
<span x-text="singleData.num"></span>
</div>
</template>
</div>
Does anyone know how to build a 'dropup' in TailwindCSS /AlpineJS? I know how to build a dropdown but can't manage to make a dropup.
My dropdown:
<link href="https://unpkg.com/tailwindcss#^1.0/dist/tailwind.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine#v2.x.x/dist/alpine.min.js" defer></script>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta charset="utf-8" />
<div class="flex-shrink-0 flex border-t border-gray-200 p-4">
<a href="#" class="flex-shrink-0 group block">
<div class="flex items-center">
<div #click.away="open = false" class="relative" x-data="{ open: false }">
<div>
<button #click="open = !open" class="max-w-xs flex items-center text-sm rounded-full text-white focus:outline-none focus:shadow-solid transition ease-in-out duration-150">
<img class="inline-block h-8 w-8 rounded-full" src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="">
<div class="ml-3">
<p class="text-sm leading-5 font-medium text-gray-700 group-hover:text-gray-900">
John Doe
</p>
<p class="text-xs text-left leading-4 font-medium text-gray-500 group-hover:text-gray-700 transition ease-in-out duration-150">
View profile
</p>
</div>
</button>
</div>
<div x-show="open" x-transition:enter="transition ease-out duration-100" x-transition:enter-start="transform opacity-0 scale-95" x-transition:enter-end="transform opacity-100 scale-100" x-transition:leave="transition ease-in duration-75" x-transition:leave-start="transform opacity-100 scale-100" x-transition:leave-end="transform opacity-0 scale-95" class="origin-top-right absolute left-0 mt-2 -mr-1 w-48 rounded-md shadow-lg">
<div class="py-1 rounded-md bg-white shadow-xs relative">
Your Profile
Settings
Sign out
</div>
</div>
</div>
</div>
</a>
</div>
https://jsfiddle.net/s4m6vea7/
Thanks!
With minimal changes
I do know how to build this. Building the drop up is easy. Spacing it out evenly is more difficult. In this code pen, I took your example and made it work without changing very much. I added the bottom-0 class to the dropdown. I also added an mb-12 class to it would sit above the button. This isn't a super dynamic fix though, you'll have to add margins to the bottom of every element you use it on.
A solid solution
I changed this code pen to have a wrapper around the dropup. By wrapping the dropup I was able to get the bottom-0 class to start at the top of the dropdown. Any margin added will be consistent regardless of the dropup height.