Fix focus styles showing up when using the mouse (#2347)

* update playground examples to use a shared `Button`

* expose a `ui-focus-visible` variant

* keep track of a `data-headlessui-focus-visible` attribute

* do not set the `tabindex`

The focus was always set, but the ring wasn't showing up. This was also
focusing a ring when the browser decided not the add one.

Let's make the browser decide when to show this or not.

* update changelog
This commit is contained in:
Robin Malfait
2023-03-10 22:00:35 +01:00
committed by GitHub
parent 9a7dcfc76d
commit 0c0601f87a
26 changed files with 258 additions and 216 deletions
+3 -1
View File
@@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
- Nothing yet!
### Fixed
- Fix focus styles showing up when using the mouse ([#2347](https://github.com/tailwindlabs/headlessui/pull/2347))
## [1.7.13] - 2023-03-03
@@ -116,6 +116,47 @@ export function restoreFocusIfNecessary(element: HTMLElement | null) {
})
}
// The method of triggering an action, this is used to determine how we should
// restore focus after an action has been performed.
enum ActivationMethod {
/* If the action was triggered by a keyboard event. */
Keyboard = 0,
/* If the action was triggered by a mouse / pointer / ... event.*/
Mouse = 1,
}
// We want to be able to set and remove the `data-headlessui-mouse` attribute on the `html` element.
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
document.addEventListener(
'keydown',
(event) => {
if (event.metaKey || event.altKey || event.ctrlKey) {
return
}
document.documentElement.dataset.headlessuiFocusVisible = ''
},
true
)
document.addEventListener(
'click',
(event) => {
// Event originated from an actual mouse click
if (event.detail === ActivationMethod.Mouse) {
delete document.documentElement.dataset.headlessuiFocusVisible
}
// Event originated from a keyboard event that triggered the `click` event
else if (event.detail === ActivationMethod.Keyboard) {
document.documentElement.dataset.headlessuiFocusVisible = ''
}
},
true
)
}
export function focusElement(element: HTMLElement | null) {
element?.focus({ preventScroll: true })
}
@@ -232,14 +273,5 @@ export function focusIn(
next.select()
}
// This is a little weird, but let me try and explain: There are a few scenario's
// in chrome for example where a focused `<a>` tag does not get the default focus
// styles and sometimes they do. This highly depends on whether you started by
// clicking or by using your keyboard. When you programmatically add focus `anchor.focus()`
// then the active element (document.activeElement) is this anchor, which is expected.
// However in that case the default focus styles are not applied *unless* you
// also add this tabindex.
if (!next.hasAttribute('tabindex')) next.setAttribute('tabindex', '0')
return FocusResult.Success
}
@@ -37,6 +37,6 @@
},
"devDependencies": {
"esbuild": "^0.11.18",
"tailwindcss": "^3.2.4"
"tailwindcss": "^3.2.7"
}
}
@@ -9,6 +9,7 @@ let css = String.raw
function run(input: string, config: any, plugin = tailwind) {
let { currentTestName } = expect.getState()
// @ts-ignore
return postcss(plugin(config)).process(input, {
from: `${path.resolve(__filename)}?test=${currentTestName}`,
})
@@ -52,6 +53,21 @@ it('should generate the inverse "not" css for an exposed state', async () => {
})
})
it('should generate the ui-focus-visible variant', async () => {
let config = {
content: [{ raw: html`<div class="ui-focus-visible:underline"></div>` }],
plugins: [hui],
}
return run('@tailwind utilities', config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
:where([data-headlessui-focus-visible]) .ui-focus-visible\:underline:focus {
text-decoration-line: underline;
}
`)
})
})
describe('custom prefix', () => {
it('should generate css for an exposed state', async () => {
let config = {
@@ -31,6 +31,8 @@ export default plugin.withOptions<Options>(({ prefix = 'ui' } = {}) => {
`&[data-headlessui-state]:not([data-headlessui-state~="${state}"])`,
`:where([data-headlessui-state]:not([data-headlessui-state~="${state}"])) &:not([data-headlessui-state])`,
])
addVariant(`${prefix}-focus-visible`, ':where([data-headlessui-focus-visible]) &:focus')
}
}
})
+3 -1
View File
@@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
- Nothing yet!
### Fixed
- Fix focus styles showing up when using the mouse ([#2347](https://github.com/tailwindlabs/headlessui/pull/2347))
## [1.7.12] - 2023-03-03
@@ -109,6 +109,47 @@ export function restoreFocusIfNecessary(element: HTMLElement | null) {
})
}
// The method of triggering an action, this is used to determine how we should
// restore focus after an action has been performed.
enum ActivationMethod {
/* If the action was triggered by a keyboard event. */
Keyboard = 0,
/* If the action was triggered by a mouse / pointer / ... event.*/
Mouse = 1,
}
// We want to be able to set and remove the `data-headlessui-mouse` attribute on the `html` element.
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
document.addEventListener(
'keydown',
(event) => {
if (event.metaKey || event.altKey || event.ctrlKey) {
return
}
document.documentElement.dataset.headlessuiFocusVisible = ''
},
true
)
document.addEventListener(
'click',
(event) => {
// Event originated from an actual mouse click
if (event.detail === ActivationMethod.Mouse) {
delete document.documentElement.dataset.headlessuiFocusVisible
}
// Event originated from a keyboard event that triggered the `click` event
else if (event.detail === ActivationMethod.Keyboard) {
document.documentElement.dataset.headlessuiFocusVisible = ''
}
},
true
)
}
export function focusElement(element: HTMLElement | null) {
element?.focus({ preventScroll: true })
}
@@ -226,14 +267,5 @@ export function focusIn(
next.select()
}
// This is a little weird, but let me try and explain: There are a few scenario's
// in chrome for example where a focused `<a>` tag does not get the default focus
// styles and sometimes they do. This highly depends on whether you started by
// clicking or by using your keyboard. When you programmatically add focus `anchor.focus()`
// then the active element (document.activeElement) is this anchor, which is expected.
// However in that case the default focus styles are not applied *unless* you
// also add this tabindex.
if (!next.hasAttribute('tabindex')) next.setAttribute('tabindex', '0')
return FocusResult.Success
}
@@ -0,0 +1,20 @@
import { ComponentProps, forwardRef, ReactNode } from 'react'
function classNames(...classes: (string | false | undefined | null)[]) {
return classes.filter(Boolean).join(' ')
}
export let Button = forwardRef<
HTMLButtonElement,
ComponentProps<'button'> & { children?: ReactNode }
>(({ className, ...props }, ref) => (
<button
ref={ref}
type="button"
className={classNames(
'ui-focus-visible:ring-2 ui-focus-visible:ring-offset-2 flex items-center rounded-md border border-gray-300 bg-white px-2 py-1 ring-gray-500 ring-offset-gray-100 focus:outline-none',
className
)}
{...props}
/>
))
+1 -1
View File
@@ -27,6 +27,6 @@
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-flatpickr": "^3.10.9",
"tailwindcss": "^0.0.0-insiders.83b4811"
"tailwindcss": "^3.2.7"
}
}
@@ -1,6 +1,7 @@
import { useState } from 'react'
import { Switch, RadioGroup, Listbox, Combobox } from '@headlessui/react'
import { classNames } from '../../utils/class-names'
import { Button } from '../../components/button'
function Section({ title, children }) {
return (
@@ -170,26 +171,24 @@ export default function App() {
{({ value }) => (
<>
<div className="relative">
<span className="inline-block w-full rounded-md shadow-sm">
<Listbox.Button className="relative w-full cursor-default rounded-md border border-gray-300 bg-white py-2 pl-3 pr-10 text-left focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 sm:text-sm sm:leading-5">
<span className="block truncate">{value?.name?.first}</span>
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<svg
className="h-5 w-5 text-gray-400"
viewBox="0 0 20 20"
fill="none"
stroke="currentColor"
>
<path
d="M7 7l3-3 3 3m0 6l-3 3-3-3"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</span>
</Listbox.Button>
</span>
<Listbox.Button as={Button} className="w-full">
<span className="block truncate">{value?.name?.first}</span>
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<svg
className="h-5 w-5 text-gray-400"
viewBox="0 0 20 20"
fill="none"
stroke="currentColor"
>
<path
d="M7 7l3-3 3 3m0 6l-3 3-3-3"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</span>
</Listbox.Button>
<div className="absolute z-10 mt-1 w-full rounded-md bg-white shadow-lg">
<Listbox.Options className="shadow-xs max-h-60 overflow-auto rounded-md py-1 text-base leading-6 focus:outline-none sm:text-sm sm:leading-5">
@@ -1,21 +1,22 @@
import { useState } from 'react'
import { Dialog, Tab } from '@headlessui/react'
import { Button } from '../../components/button'
export default function App() {
let [open, setOpen] = useState(false)
return (
<>
<button onClick={() => setOpen(true)}>Open dialog</button>
<div className="p-12">
<Button onClick={() => setOpen(true)}>Open dialog</Button>
<Dialog open={open} onClose={setOpen} className="fixed inset-0 grid place-content-center">
<div className="fixed inset-0 bg-gray-500/70" />
<Dialog.Panel className="inline-block transform overflow-hidden rounded-lg bg-white text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:align-middle">
<div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<Tab.Group>
<Tab.List>
<Tab className="px-3 py-2">Tab 1</Tab>
<Tab className="px-3 py-2">Tab 2</Tab>
<Tab className="px-3 py-2">Tab 3</Tab>
<Tab.List className="flex gap-4 py-4">
<Tab as={Button}>Tab 1</Tab>
<Tab as={Button}>Tab 2</Tab>
<Tab as={Button}>Tab 3</Tab>
</Tab.List>
<Tab.Panels>
<Tab.Panel className="px-3 py-2">Panel 1</Tab.Panel>
@@ -26,6 +27,6 @@ export default function App() {
</div>
</Dialog.Panel>
</Dialog>
</>
</div>
)
}
@@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react'
import { Combobox } from '@headlessui/react'
import { classNames } from '../../utils/class-names'
import { Button } from '../../components/button'
let everybody = [
'Wade Cooper',
@@ -64,7 +65,7 @@ export default function Home() {
onChange={(e) => setQuery(e.target.value)}
className="border-none px-3 py-1 outline-none"
/>
<Combobox.Button className="cursor-default border-l bg-gray-100 px-1 text-indigo-600 focus:outline-none">
<Combobox.Button as={Button}>
<span className="pointer-events-none flex items-center px-2">
<svg
className="h-5 w-5 text-gray-400"
@@ -1,5 +1,6 @@
import { useState } from 'react'
import { Dialog } from '@headlessui/react'
import { Button } from '../../components/button'
function Modal(props) {
return (
@@ -7,9 +8,9 @@ function Modal(props) {
<div className="fixed inset-0 bg-green-500 bg-opacity-90 backdrop-blur backdrop-filter" />
<div className="fixed inset-0 overflow-y-auto">
<div className="flex min-h-full items-center justify-center p-4">
<Dialog.Panel className="relative m-5 w-full max-w-3xl rounded-lg bg-white p-10 shadow">
<button className="m-5 rounded-lg bg-blue-600 py-2 px-5 text-white">One</button>
<button className="m-5 rounded-lg bg-blue-600 py-2 px-5 text-white">Two</button>
<Dialog.Panel className="relative m-5 flex w-full max-w-3xl gap-4 rounded-lg bg-white p-10 shadow">
<Button>One</Button>
<Button>Two</Button>
</Dialog.Panel>
</div>
</div>
@@ -23,12 +24,7 @@ export default function DialogFocusIssue() {
return (
<div className="p-10">
<h1 className="py-2 text-3xl font-semibold">Headless UI Focus Jump</h1>
<button
className="my-5 rounded-lg bg-blue-600 py-2 px-5 text-white"
onClick={() => setIsOpen(true)}
>
Open Dialog
</button>
<Button onClick={() => setIsOpen(true)}>Open Dialog</Button>
<div className="bg-white p-20"></div>
<div className="bg-gray-100 p-20"></div>
<div className="bg-gray-200 p-20"></div>
@@ -1,6 +1,7 @@
import { useEffect, useRef } from 'react'
import { useState } from 'react'
import { Dialog } from '@headlessui/react'
import { Button } from '../../components/button'
if (typeof document !== 'undefined') {
class MyCustomElement extends HTMLElement {
@@ -49,12 +50,7 @@ export default function App() {
return (
<div>
<button
className="m-4 rounded border-0 bg-gray-500 px-3 py-1 font-medium text-white hover:bg-gray-600"
onClick={() => setOpen(true)}
>
open
</button>
<Button onClick={() => setOpen(true)}>open</Button>
<Dialog open={open} onClose={() => setOpen(false)}>
<div className="fixed inset-0 z-50 bg-gray-900/75 backdrop-blur-lg">
<div>
@@ -3,6 +3,7 @@ import Flatpickr from 'react-flatpickr'
import { Dialog, Menu, Portal, Transition } from '@headlessui/react'
import { usePopper } from '../../utils/hooks/use-popper'
import { classNames } from '../../utils/class-names'
import { Button } from '../../components/button'
import 'flatpickr/dist/themes/light.css'
@@ -14,16 +15,6 @@ function resolveClass({ active, disabled }) {
)
}
function Button(props: React.ComponentProps<'button'>) {
return (
<button
type="button"
className="rounded bg-gray-200 px-2 py-1 ring-gray-500 ring-offset-gray-100 focus:outline-none focus:ring-2 focus:ring-offset-2"
{...props}
/>
)
}
function Nested({ onClose, level = 0 }) {
let [showChild, setShowChild] = useState(false)
@@ -38,7 +29,7 @@ function Nested({ onClose, level = 0 }) {
}}
>
<p>Level: {level}</p>
<div className="space-x-4">
<div className="flex gap-4">
<Button onClick={() => setShowChild(true)}>Open (1)</Button>
<Button onClick={() => setShowChild(true)}>Open (2)</Button>
<Button onClick={() => setShowChild(true)}>Open (3)</Button>
@@ -70,11 +61,6 @@ export default function Home() {
</div>
{nested && <Nested onClose={() => setNested(false)} />}
<div
data-preload
className="translate-y-4 translate-y-0 translate-y-0 translate-y-4 scale-95 scale-100 scale-100 scale-95 transform transform transform transform transform transform opacity-0 opacity-75 opacity-75 opacity-0 opacity-75 opacity-0 opacity-100 opacity-100 opacity-0 opacity-0 opacity-100 opacity-100 opacity-0 transition transition duration-1000 duration-300 duration-200 duration-300 duration-200 duration-300 duration-75 ease-out ease-in ease-out ease-in ease-out ease-out sm:translate-y-0 sm:translate-y-0 sm:scale-95 sm:scale-100 sm:scale-100 sm:scale-95"
/>
<Transition
data-debug="Dialog"
show={isOpen}
@@ -161,27 +147,22 @@ export default function Home() {
Are you sure you want to deactivate your account? All of your data will
be permanently removed. This action cannot be undone.
</p>
<div className="relative mt-10 inline-block text-left">
<div className="relative mt-10 inline-flex gap-4 text-left">
<Menu>
<span className="rounded-md shadow-sm">
<Menu.Button
ref={trigger}
className="focus:shadow-outline-blue inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out hover:text-gray-500 focus:border-blue-300 focus:outline-none active:bg-gray-50 active:text-gray-800"
<Menu.Button as={Button} ref={trigger}>
<span>Choose a reason</span>
<svg
className="ml-2 -mr-1 h-5 w-5"
viewBox="0 0 20 20"
fill="currentColor"
>
<span>Choose a reason</span>
<svg
className="ml-2 -mr-1 h-5 w-5"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fillRule="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"
clipRule="evenodd"
/>
</svg>
</Menu.Button>
</span>
<path
fillRule="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"
clipRule="evenodd"
/>
</svg>
</Menu.Button>
<Transition
enter="transition duration-300 ease-out"
@@ -242,21 +223,9 @@ export default function Home() {
</div>
</div>
</div>
<div className="bg-gray-50 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6">
<button
type="button"
onClick={() => setIsOpen(false)}
className="focus:shadow-outline-red inline-flex w-full justify-center rounded-md border border-transparent bg-red-600 px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-red-700 focus:outline-none sm:ml-3 sm:w-auto sm:text-sm"
>
Deactivate
</button>
<button
type="button"
onClick={() => setIsOpen(false)}
className="focus:shadow-outline-indigo mt-3 inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-base font-medium text-gray-700 shadow-sm hover:text-gray-500 focus:outline-none sm:mt-0 sm:w-auto sm:text-sm"
>
Cancel
</button>
<div className="flex bg-gray-50 px-4 py-3 sm:flex-row-reverse sm:gap-2">
<Button onClick={() => setIsOpen(false)}>Deactivate</Button>
<Button onClick={() => setIsOpen(false)}>Cancel</Button>
</div>
</Dialog.Panel>
</Transition.Child>
@@ -1,9 +1,10 @@
import React from 'react'
import React, { forwardRef } from 'react'
import Link from 'next/link'
import { Menu } from '@headlessui/react'
import { Menu, MenuItemProps } from '@headlessui/react'
import { AnimatePresence, motion } from 'framer-motion'
import { classNames } from '../../utils/class-names'
import { Button } from '../../components/button'
export default function Home() {
return (
@@ -13,7 +14,7 @@ export default function Home() {
{({ open }) => (
<>
<span className="rounded-md shadow-sm">
<Menu.Button className="focus:shadow-outline-blue inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out hover:text-gray-500 focus:border-blue-300 focus:outline-none active:bg-gray-50 active:text-gray-800">
<Menu.Button as={Button}>
<span>Options</span>
<svg className="ml-2 -mr-1 h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path
@@ -67,16 +68,18 @@ export default function Home() {
)
}
function NextLink(props: React.ComponentProps<'a'>) {
let NextLink = forwardRef<HTMLAnchorElement>((props: React.ComponentProps<'a'>, ref) => {
let { href, children, ...rest } = props
return (
<Link href={href}>
<a {...rest}>{children}</a>
<a ref={ref} {...rest}>
{children}
</a>
</Link>
)
}
})
function SignOutButton(props) {
let SignOutButton = forwardRef<HTMLButtonElement>((props, ref) => {
return (
<form
method="POST"
@@ -87,16 +90,17 @@ function SignOutButton(props) {
}}
className="w-full"
>
<button type="submit" {...props}>
<button ref={ref} type="submit" {...props}>
Sign out
</button>
</form>
)
}
})
function Item(props) {
let Item = forwardRef<HTMLAnchorElement, MenuItemProps<any>>((props, ref) => {
return (
<Menu.Item
ref={ref}
as="a"
className={({ active, disabled }) =>
classNames(
@@ -108,4 +112,4 @@ function Item(props) {
{...props}
/>
)
}
})
@@ -4,6 +4,7 @@ import { Menu } from '@headlessui/react'
import { usePopper } from '../../utils/hooks/use-popper'
import { classNames } from '../../utils/class-names'
import { Button } from '../../components/button'
export default function Home() {
let [trigger, container] = usePopper({
@@ -25,10 +26,7 @@ export default function Home() {
<div className="mt-64 inline-block text-left">
<Menu>
<span className="inline-flex rounded-md shadow-sm">
<Menu.Button
ref={trigger}
className="focus:shadow-outline-blue inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out hover:text-gray-500 focus:border-blue-300 focus:outline-none active:bg-gray-50 active:text-gray-800"
>
<Menu.Button ref={trigger} as={Button}>
<span>Options</span>
<svg className="ml-2 -mr-1 h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path
@@ -3,6 +3,7 @@ import { Menu, Transition } from '@headlessui/react'
import { usePopper } from '../../utils/hooks/use-popper'
import { classNames } from '../../utils/class-names'
import { Button } from '../../components/button'
export default function Home() {
let [trigger, container] = usePopper({
@@ -24,10 +25,7 @@ export default function Home() {
<div className="mt-64 inline-block text-left">
<Menu>
<span className="rounded-md shadow-sm">
<Menu.Button
ref={trigger}
className="focus:shadow-outline-blue inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out hover:text-gray-500 focus:border-blue-300 focus:outline-none active:bg-gray-50 active:text-gray-800"
>
<Menu.Button ref={trigger} as={Button}>
<span>Options</span>
<svg className="ml-2 -mr-1 h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path
@@ -1,6 +1,7 @@
import React from 'react'
import { Menu, Transition } from '@headlessui/react'
import { classNames } from '../../utils/class-names'
import { Button } from '../../components/button'
export default function Home() {
function resolveClass({ active, disabled }) {
@@ -14,13 +15,9 @@ export default function Home() {
return (
<div className="flex h-full w-screen justify-center bg-gray-50 p-12">
<div className="relative inline-block text-left">
<div
data-preload
className="scale-95 scale-100 scale-100 scale-95 transform transform transform transform opacity-0 opacity-100 opacity-100 opacity-0 transition transition duration-1000 duration-1000 ease-out ease-out"
/>
<Menu>
<span className="rounded-md shadow-sm">
<Menu.Button className="focus:shadow-outline-blue inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out hover:text-gray-500 focus:border-blue-300 focus:outline-none active:bg-gray-50 active:text-gray-800">
<Menu.Button as={Button}>
<span>Options</span>
<svg className="ml-2 -mr-1 h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path
@@ -2,6 +2,7 @@ import React from 'react'
import { Menu } from '@headlessui/react'
import { classNames } from '../../utils/class-names'
import { Button } from '../../components/button'
export default function Home() {
return (
@@ -9,7 +10,7 @@ export default function Home() {
<div className="relative inline-block text-left">
<Menu>
<span className="rounded-md shadow-sm">
<Menu.Button className="focus:shadow-outline-blue inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out hover:text-gray-500 focus:border-blue-300 focus:outline-none active:bg-gray-50 active:text-gray-800">
<Menu.Button as={Button}>
<span>Options</span>
<svg
className="ml-2 -mr-1 h-5 w-5 transition-transform duration-150"
@@ -1,6 +1,7 @@
import React from 'react'
import { Menu } from '@headlessui/react'
import { classNames } from '../../utils/class-names'
import { Button } from '../../components/button'
export default function Home() {
return (
@@ -34,7 +35,7 @@ function Dropdown() {
<div className="relative inline-block text-left">
<Menu>
<span className="inline-flex rounded-md shadow-sm">
<Menu.Button className="focus:shadow-outline-blue inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out hover:text-gray-500 focus:border-blue-300 focus:outline-none active:bg-gray-50 active:text-gray-800">
<Menu.Button as={Button}>
<span>Options</span>
<svg className="ml-2 -mr-1 h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path
@@ -17,10 +17,6 @@ export default function Home() {
return (
<div>
<div
data-preload
className="translate-y-4 translate-y-0 translate-y-0 translate-y-4 opacity-0 opacity-100 opacity-100 opacity-0 opacity-0 opacity-100 opacity-100 opacity-0 duration-300 duration-200 duration-300 duration-200 ease-out ease-in ease-out ease-in sm:translate-y-0 sm:translate-y-0 sm:scale-95 sm:scale-100 sm:scale-100 sm:scale-95"
/>
<div className="flex space-x-4 p-12">
<div className="inline-block p-12">
<span className="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto">
@@ -18,12 +18,6 @@ export default function Home() {
</button>
</span>
<div
hidden
data-preload
className="transform transform transform transform rounded-md rounded-md bg-red-500 bg-blue-500 bg-red-500 bg-green-500 bg-green-500 bg-blue-500 bg-white p-4 p-4 opacity-0 opacity-100 opacity-100 opacity-0 shadow shadow transition-colors transition-colors transition-colors transition transition duration-[5s] duration-1000 duration-1000 duration-1000 duration-1000 ease-out ease-in ease-out ease-in"
/>
<Transition
show={isOpen}
appear={false}
@@ -0,0 +1,9 @@
<template>
<button
type="button"
class="ui-focus-visible:ring-2 ui-focus-visible:ring-offset-2 flex items-center rounded-md border border-gray-300 bg-white px-2 py-1 ring-gray-500 ring-offset-gray-100 focus:outline-none"
v-bind="$props"
>
<slot></slot>
</button>
</template>
@@ -72,25 +72,19 @@
</p>
<div class="relative mt-10 inline-block text-left">
<Menu>
<span class="rounded-md shadow-sm">
<MenuButton
ref="trigger"
class="focus:shadow-outline-blue inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out hover:text-gray-500 focus:border-blue-300 focus:outline-none active:bg-gray-50 active:text-gray-800"
>
<span>Choose a reason</span>
<svg
class="ml-2 -mr-1 h-5 w-5"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fillRule="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"
clipRule="evenodd"
/>
</svg>
</MenuButton>
</span>
<MenuButton
ref="trigger"
class="ui-focus-visible:ring-2 ui-focus-visible:ring-offset-2 flex items-center rounded-md border border-gray-300 bg-white px-2 py-1 ring-gray-500 ring-offset-gray-100 focus:outline-none"
>
<span>Choose a reason</span>
<svg class="ml-2 -mr-1 h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path
fillRule="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"
clipRule="evenodd"
/>
</svg>
</MenuButton>
<TransitionRoot
enter="transition duration-300 ease-out"
@@ -152,20 +146,8 @@
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6">
<button
type="button"
@click="setIsOpen(false)"
class="focus:shadow-outline-red inline-flex w-full justify-center rounded-md border border-transparent bg-red-600 px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-red-700 focus:outline-none sm:ml-3 sm:w-auto sm:text-sm"
>
Deactivate
</button>
<button
type="button"
@click="setIsOpen(false)"
class="focus:shadow-outline-indigo mt-3 inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-base font-medium text-gray-700 shadow-sm hover:text-gray-500 focus:outline-none sm:mt-0 sm:w-auto sm:text-sm"
>
Cancel
</button>
<Button type="button" @click="setIsOpen(false)"> Deactivate </Button>
<Button @click="setIsOpen(false)"> Cancel </Button>
</div>
</DialogPanel>
</TransitionChild>
@@ -192,6 +174,7 @@ import {
} from '@headlessui/vue'
import Flatpickr from 'vue-flatpickr-component'
import { usePopper } from '../../playground-utils/hooks/use-popper'
import Button from '../Button.vue'
import 'flatpickr/dist/themes/light.css'
@@ -207,22 +190,6 @@ function resolveClass({ active, disabled }) {
)
}
let Button = defineComponent({
setup(props, { slots }) {
return () =>
h(
'button',
{
type: 'button',
class:
'rounded bg-gray-200 px-2 py-1 ring-gray-500 ring-offset-gray-100 focus:outline-none focus:ring-2 focus:ring-offset-2',
...props,
},
slots.default?.()
)
},
})
let Nested = defineComponent({
components: { Dialog, DialogOverlay },
emits: ['close'],
@@ -245,7 +212,7 @@ let Nested = defineComponent({
},
[
h('p', `Level: ${level}`),
h('div', { class: 'space-x-4' }, [
h('div', { class: 'flex gap-4' }, [
h(Button, { onClick: () => (showChild.value = true) }, () => `Open ${level + 1} a`),
h(Button, { onClick: () => (showChild.value = true) }, () => `Open ${level + 1} b`),
h(Button, { onClick: () => (showChild.value = true) }, () => `Open ${level + 1} c`),
@@ -290,6 +257,7 @@ export default {
let date = ref(new Date())
return {
Button,
nested,
date,
isOpen,
+23 -15
View File
@@ -4504,6 +4504,14 @@ postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.6:
cssesc "^3.0.0"
util-deprecate "^1.0.2"
postcss-selector-parser@^6.0.11:
version "6.0.11"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz#2e41dc39b7ad74046e1615185185cd0b17d0c8dc"
integrity sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==
dependencies:
cssesc "^3.0.0"
util-deprecate "^1.0.2"
postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
@@ -4518,6 +4526,15 @@ postcss@8.4.5:
picocolors "^1.0.0"
source-map-js "^1.0.1"
postcss@^8.0.9:
version "8.4.21"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4"
integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==
dependencies:
nanoid "^3.3.4"
picocolors "^1.0.0"
source-map-js "^1.0.2"
postcss@^8.1.10:
version "8.4.8"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.8.tgz#dad963a76e82c081a0657d3a2f3602ce10c2e032"
@@ -4545,15 +4562,6 @@ postcss@^8.4.16:
picocolors "^1.0.0"
source-map-js "^1.0.2"
postcss@^8.4.18:
version "8.4.19"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.19.tgz#61178e2add236b17351897c8bcc0b4c8ecab56fc"
integrity sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==
dependencies:
nanoid "^3.3.4"
picocolors "^1.0.0"
source-map-js "^1.0.2"
prelude-ls@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
@@ -5383,10 +5391,10 @@ tailwindcss@^0.0.0-insiders.83b4811:
quick-lru "^5.1.1"
resolve "^1.22.0"
tailwindcss@^3.2.4:
version "3.2.4"
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.2.4.tgz#afe3477e7a19f3ceafb48e4b083e292ce0dc0250"
integrity sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ==
tailwindcss@^3.2.7:
version "3.2.7"
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.2.7.tgz#5936dd08c250b05180f0944500c01dce19188c07"
integrity sha512-B6DLqJzc21x7wntlH/GsZwEXTBttVSl1FtCzC8WP4oBc/NKef7kaax5jeihkkCEWc831/5NDJ9gRNDK6NEioQQ==
dependencies:
arg "^5.0.2"
chokidar "^3.5.3"
@@ -5402,12 +5410,12 @@ tailwindcss@^3.2.4:
normalize-path "^3.0.0"
object-hash "^3.0.0"
picocolors "^1.0.0"
postcss "^8.4.18"
postcss "^8.0.9"
postcss-import "^14.1.0"
postcss-js "^4.0.0"
postcss-load-config "^3.1.4"
postcss-nested "6.0.0"
postcss-selector-parser "^6.0.10"
postcss-selector-parser "^6.0.11"
postcss-value-parser "^4.2.0"
quick-lru "^5.1.1"
resolve "^1.22.1"