Files
headlessui/packages/playground-react/pages/menu/multiple-elements.tsx
T
Robin Malfait ea26870480 Add Combobox component (#1047)
* start of combobox

* start with a copy of the Listbox

* WIP

* Add Vue Combobox

* Update Vue version of combobox

* Update tests

* Fix typescript errors in combobox test

* Fix input label

The spec says that the combobox itself is labelled directly by the associated label. The button can however be labelled by the label or itself.

* Add active descendant to combobox/input

* Add listbox role to comobox options

Right now the option list *is* just a listbox. If we were to allow other types in the future this will need to be changable

* Update tests

* move React playground to dedicated package

* add react playground script to root

* ensure we only open/close the combobox when necessary

* ensure export order is correct

* remove leftover pages directory from React package

* Only add aria controls when combobox is open

* add missing next commands

* make typescript happy

* build @headlessui/react before building playground-react

* add empty public folder

This makes vercel happy

* wip

* Add todo

* Update tests

Still more updates to do but some are blocked on implementation

* change default combobox example slightly

* ensure that we sync the input with new state

When the <Combobox value={...} /> changes, then the input should change
as well.

* only sync the value with the input in a single spot

* WIP: object value to string

* WIP

* WIP

* WIP groups

* Add static search filtering to combobox

* Move mouse leave event to combobox

* Fix use in fragments

* Update

* WIP

* make all tests pass for the combobox in React

* remove unnecessary playground item

* remove listbox wip

* only fire change event on inputs

Potentially we also have to do this for all kinds of form inputs. But
this will do for now.

* disable combobox vue tests

* Fix vue typescript errors

* Vue tests WIP

* improve combobox playgrounds a tiny bit

* ensure to lookup the correct value

* make sure that we are using a div instead of a Fragment

* expose `activeItem`

This will be similar to `yourData[activeIndex]`, but in this case the
active option's data. Can probably rename this if necessary!

* Update comments

* Port react tests to Vue

* Vue tests WIP

* WIP

* Rename activeItem to activeOption

* Move display value to input

* Update playgrounds

* Remove static filtering

* Add tests for display value

* WIP Vue Tests

* WIP

* unfocus suite

* Cleanup react accessibility assertions code

* Vue WIP

* Cleanup errors in react interactions test utils

* Update vue implementation

closer :D

* Fix searching

* Update

* Add display value stubs

* Update tests

* move `<Combobox onSearch={} />` to `<Combobox.Input onChange={} />`

* use `useLatestValue` hook

* make `onChange` explicitly required

* remove unused variables

* move `<Combobox @search="" />` to `<ComboboxInput @change="" />`

* use correct event

* use `let` for consistency

* remove unnecessary hidden check

* implement displayValue for Vue

* update playground to reflect changes

* make sure that the activeOptionIndex stays correct

* update changelog

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2022-01-27 16:42:47 +01:00

84 lines
2.9 KiB
TypeScript

import React from 'react'
import { Menu } from '@headlessui/react'
import { classNames } from '../../utils/class-names'
export default function Home() {
return (
<div className="flex justify-center w-screen h-full p-12 space-x-4 bg-gray-50">
<Dropdown />
<div>
<div className="relative rounded-md shadow-sm">
<input
className="block w-full form-input sm:text-sm sm:leading-5"
placeholder="you@example.com"
/>
</div>
</div>
<Dropdown />
</div>
)
}
function Dropdown() {
function resolveClass({ active, disabled }) {
return classNames(
'block w-full text-left px-4 py-2 text-sm leading-5 text-gray-700',
active && 'bg-gray-100 text-gray-900',
disabled && 'cursor-not-allowed opacity-50'
)
}
return (
<div className="relative inline-block text-left">
<Menu>
<span className="inline-flex rounded-md shadow-sm">
<Menu.Button className="inline-flex justify-center w-full px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800">
<span>Options</span>
<svg className="w-5 h-5 ml-2 -mr-1" 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>
<Menu.Items className="absolute right-0 w-56 mt-2 origin-top-right bg-white border border-gray-200 divide-y divide-gray-100 rounded-md shadow-lg outline-none">
<div className="px-4 py-3">
<p className="text-sm leading-5">Signed in as</p>
<p className="text-sm font-medium leading-5 text-gray-900 truncate">tom@example.com</p>
</div>
<div className="py-1">
<Menu.Item as="a" href="#account-settings" className={resolveClass}>
Account settings
</Menu.Item>
<Menu.Item>
{data => (
<a href="#support" className={resolveClass(data)}>
Support
</a>
)}
</Menu.Item>
<Menu.Item as="a" disabled href="#new-feature" className={resolveClass}>
New feature (soon)
</Menu.Item>
<Menu.Item as="a" href="#license" className={resolveClass}>
License
</Menu.Item>
</div>
<div className="py-1">
<Menu.Item as="a" href="#sign-out" className={resolveClass}>
Sign out
</Menu.Item>
</div>
</Menu.Items>
</Menu>
</div>
)
}