diff --git a/packages/@headlessui-react/README.md b/packages/@headlessui-react/README.md index 3f6c4eeeb1..8b5dbd2515 100644 --- a/packages/@headlessui-react/README.md +++ b/packages/@headlessui-react/README.md @@ -29,6 +29,8 @@ _This project is still in early development. New components will be added regula - [Transition](#transition) - [Menu Button (Dropdown)](#menu-button-dropdown) +- [Listbox](#listbox) +- [Switch (Toggle)](#switch-toggle) ### Roadmap @@ -76,9 +78,7 @@ function MyComponent() { return ( <> - + - + - + - + - {(ref) =>
{/* Your content goes here*/}
} + {ref =>
{/* Your content goes here*/}
}
) @@ -201,9 +195,7 @@ function MyComponent() { return ( <> - + + More + + Option A + Option B + + Option C + + + + ) +} +``` + +### Styling the active and selected option + +This is a headless component so there are no styles included by default. Instead, the components expose useful information via [render props](https://reactjs.org/docs/render-props.html) that you can use to apply the styles you'd like to apply yourself. + +To style the active `Listbox.Option` you can read the `active` render prop argument, which tells you whether or not that listbox option is the option that is currently focused via the mouse or keyboard. + +To style the selected `Listbox.Option` you can read the `selected` render prop argument, which tells you whether or not that listbox option is the option that is currently the `value` passed to the `Listbox`. + +> Note: An option can be both **active** and **selected** at the same time! + +You can use this state to conditionally apply whatever active/focus styles you like, for instance a blue background like is typical in most operating systems. For the selected state, a checkmark is also common. + +```jsx +import { useState, Fragment } from 'react' +import { Listbox } from '@headlessui/react' + +function MyListbox() { + const [option, setOption] = useState('option-a') + return ( + + More + + {/* Use the `active` state to conditionally style the active option. */} + {/* Use the `selected` state to conditionally style the selected option. */} + + {({ active, selected }) => ( +
  • + {selected && } {/* Checkmark svg */} + Option A +
  • + )} +
    + {/* ... */} +
    +
    + ) +} +``` + +### Using a custom label + +By default the `Listbox` will use the button contents as the label for screenreaders. However you can also render a custom `Listbox.Label`. + +```jsx +import { useState, Fragment } from 'react' +import { Listbox } from '@headlessui/react' + +function MyListbox() { + const [country, setCountry] = useState('belgium') + return ( + + Country: + {country} + + Australia + Belgium + Canada + England + {/* ... */} + + + ) +} +``` + +### Showing/hiding the listbox + +By default, your `Listbox.Options` instance will be shown/hidden automatically based on the internal `open` state tracked within the `Listbox` component itself. + +```jsx +import { Listbox } from '@headlessui/react' + +function MyListbox() { + return ( + + More + + {/* By default, this will automatically show/hide when the Listbox.Button is pressed. */} + + {/* ... */} + {/* ... */} + + + ) +} +``` + +If you'd rather handle this yourself (perhaps because you need to add an extra wrapper element for one reason or another), you can add a `static` prop to the `Listbox.Options` instance to tell it to always render, and inspect the `open` slot prop provided by the `Listbox` to control which element is shown/hidden yourself. + +```jsx +import { Listbox } from '@headlessui/react' + +function MyListbox() { + return ( + + {({ open }) => ( + More + {open && ( +
    + {/* Using `static`, `Listbox.Options` is always rendered and ignores the `open` state. */} + + {/* ... */} + {/* ... */} + +
    + )} + )} +
    + ) +} +``` + +### Disabling an option + +Use the `disabled` prop to disable a `Listbox.Option`. This will make it unselectable via keyboard navigation, and it will be skipped when pressing the up/down arrows. + +```jsx +import { Listbox } from '@headlessui/react' + +function MyListbox() { + return ( + + More + + {/* ... */} + + {/* This option will be skipped by keyboard navigation. */} + + Invite a friend (coming soon!) + + + {/* ... */} + + + ) +} +``` + +### Transitions + +To animate the opening/closing of the listbox panel, use the provided `Transition` component. All you need to do is mark your `Listbox.Options` as `static`, wrap it in a ``, and the transition will be applied automatically. + +```jsx +import { Listbox, Transition } from '@headlessui/react' + +function MyListbox() { + return ( + + {({ open }) => ( + <> + More + + {/* Use the Transition + open render prop argument to add transitions. */} + + + {/* ... */} + {/* ... */} + + + + )} + + ) +} +``` + +### Rendering additional content + +The `Listbox` component is not limited to rendering only its related subcomponents. You can render anything you like within a listbox, which gives you complete control over exactly what you are building. + +For example, if you'd like to add a little header section to the listbox with some extra information in it, just render an extra `div` with your content in it. + +```jsx +import { Listbox } from '@headlessui/react' + +function MyListbox() { + return ( + + More + +
    +

    Signed in as

    +

    tom@example.com

    +
    + Option A + {/* ... */} +
    +
    + ) +} +``` + +Note that only `Listbox.Option` instances will be navigable via the keyboard. + +### Rendering a different element for a component + +By default, the `Listbox` and its subcomponents each render a default element that is sensible for that component. + +For example, `Listbox.Label` renders a `label` by default, `Listbox.Button` renders a `button` by default, `Listbox.Options` renders a `ul` and `Listbox.Option` renders a `li` by default. `Listbox` interestingly _does not render an extra element_, and instead renders its children directly by default. + +This is easy to change using the `as` prop, which exists on every component. + +```jsx +import { Listbox } from '@headlessui/react' + +function MyListbox() { + return ( + {/* Render a `div` instead of no wrapper element */} + + More + {/* Render a `div` instead of a `ul` */} + + {/* Render an `span` instead of an `li` */} + + Option A + + + {/* ... */} + + + ) +} +``` + +To tell an element to render its children directly with no wrapper element, use `as={React.Fragment}`. + +```jsx +import { Listbox } from '@headlessui/react' + +function MyDropdown() { + return ( + + {/* Render no wrapper, instead pass in a button manually. */} + + + + + Option A + {/* ... */} + + + ) +} +``` + +### Component API + +#### Listbox + +```jsx + + More + + {/* ... */} + {/* ... */} + + +``` + +##### Props + +| Prop | Type | Default | Description | +| ---------- | ----------------------- | --------------------------------------- | -------------------------------------------------------- | +| `as` | String \| Component | `React.Fragment` _(no wrapper element_) | The element or component the `Listbox` should render as. | +| `value` | `T` _(A generic value)_ | `undefined` | The selected value. | +| `onChange` | `(value: T): void` | `undefined` | The function to call when a new option is selected. | + +##### Render prop object + +| Prop | Type | Description | +| ------ | ------- | ----------------------------------- | +| `open` | Boolean | Whether or not the listbox is open. | + +#### Listbox.Button + +```jsx + + {({ open }) => ( + <> + More options + + + )} + +``` + +##### Props + +| Prop | Type | Default | Description | +| ---- | ------------------- | -------- | --------------------------------------------------------------- | +| `as` | String \| Component | `button` | The element or component the `Listbox.Button` should render as. | + +##### Render prop object + +| Prop | Type | Description | +| ------ | ------- | ----------------------------------- | +| `open` | Boolean | Whether or not the listbox is open. | + +#### Listbox.Label + +```jsx +Enable notifications +``` + +##### Props + +| Prop | Type | Default | Description | +| ---- | ------------------- | ------- | -------------------------------------------------------------- | +| `as` | String \| Component | `label` | The element or component the `Listbox.Label` should render as. | + +#### Listbox.Options + +```jsx + + {/* ... */}> + {/* ... */}> + +``` + +##### Props + +| Prop | Type | Default | Description | +| -------- | ------------------- | ------- | --------------------------------------------------------------------------- | +| `as` | String \| Component | `ul` | The element or component the `Listbox.Options` should render as. | +| `static` | Boolean | `false` | Whether the element should ignore the internally managed open/closed state. | + +##### Render prop object + +| Prop | Type | Description | +| ------ | ------- | ----------------------------------- | +| `open` | Boolean | Whether or not the listbox is open. | + +#### Listbox.Option + +```jsx +Option A +``` + +##### Props + +| Prop | Type | Default | Description | +| ---------- | ----------------------- | ----------- | --------------------------------------------------------------------------------------- | +| `as` | String \| Component | `li` | The element or component the `Listbox.Option` should render as. | +| `value` | `T` _(A generic value)_ | `undefined` | The selected value. | +| `disabled` | Boolean | `false` | Whether or not the option should be disabled for keyboard navigation and ARIA purposes. | + +##### Render prop object + +| Prop | Type | Description | +| ---------- | ------- | ------------------------------------------------------------------------------------ | +| `active` | Boolean | Whether or not the option is the active/focused option in the list. | +| `selected` | Boolean | Whether or not the option is the selected option in the list. | +| `disabled` | Boolean | Whether or not the option is the disabled for keyboard navigation and ARIA purposes. | + +## Switch (Toggle) + +[View live demo on CodeSandbox](https://codesandbox.io/s/headlessuireact-switch-example-y40i1?file=/src/App.js) + +The `Switch` component and related child components are used to quickly build custom switch/toggle components that are fully accessible out of the box, including correct ARIA attribute management and robust keyboard support. + +- [Basic example](#basic-example-3) +- [Using a custom label](#using-a-custom-label) +- [Component API](#component-api-3) + +### Basic example + +Switches are built using the `Switch` component. Optionally you can also use the `Switch.Group` and `Switch.Label` components. + +```jsx +import { useState } from 'react' +import { Switch } from '@headlessui/react' + +function NotificationsToggle() { + const [enabled, setEnabled] = useState(false) + + return ( + + Enable notifications + + + ) +} +``` + +### Using a custom label + +By default the `Switch` will use the contents as the label for screenreaders. If you need more control, you can render a `Switch.Label` outside of the `Switch`, as long as both the switch and label are within a parent `Switch.Group`. + +Clicking the label will toggle the switch state, like you'd expect from a native checkbox. + +```jsx +import { useState } from 'react' +import { Switch } from '@headlessui/react' + +function NotificationsToggle() { + const [enabled, setEnabled] = useState(false) + + return ( + + Enable notifications + + + + + ) +} +``` + +### Component API + +#### Switch + +```jsx + + Enable notifications + {/* ... */} + +``` + +##### Props + +| Prop | Type | Default | Description | +| ---------- | ----------------------- | --------------------------------------- | -------------------------------------------------------- | +| `as` | String \| Component | `button` | The element or component the `Switch` should render as. | +| `checked` | Boolean | | Whether or not the switch is checked. | +| `onChange` | `(value: boolean): void` | | The function to call when the switch is toggled. | + +##### Render prop object + +| Prop | Type | Description | +| ------ | ------- | ----------------------------------- | +| `checked` | Boolean | Whether or not the switch is checked. | + + +#### Switch.Label + +```jsx + + Enable notifications + + {/* ... */} + + +``` + +##### Props + +| Prop | Type | Default | Description | +| ---------- | ----------------------- | --------------------------------------- | -------------------------------------------------------- | +| `as` | String \| Component | `label` | The element or component the `Switch.Label` should render as. | + +#### Switch.Group + +```jsx + + Enable notifications + + {/* ... */} + + +``` + +##### Props + +| Prop | Type | Default | Description | +| ---------- | ----------------------- | --------------------------------------- | -------------------------------------------------------- | +| `as` | String \| Component | `React.Fragment` _(no wrapper element)_| The element or component the `Switch.Group` should render as. | diff --git a/packages/@headlessui-vue/README.md b/packages/@headlessui-vue/README.md index 046dfe9260..ac25039c0c 100644 --- a/packages/@headlessui-vue/README.md +++ b/packages/@headlessui-vue/README.md @@ -30,6 +30,8 @@ yarn add @headlessui/vue _This project is still in early development. New components will be added regularly over the coming months._ - [Menu Button (Dropdown)](#menu-button-dropdown) +- [Listbox](#listbox) +- [Switch (Toggle)](#switch-toggle) ### Roadmap @@ -372,3 +374,797 @@ To tell an element to render its children directly with no wrapper element, use | ---------- | ------- | ---------------------------------------------------------------------------------- | | `active` | Boolean | Whether or not the item is the active/focused item in the list. | | `disabled` | Boolean | Whether or not the item is the disabled for keyboard navigation and ARIA purposes. | + +## Listbox + +[View live demo on CodeSandbox](https://codesandbox.io/s/headlessuivue-menu-example-70br3?file=/src/App.vue) + +The `Listbox` component and related child components are used to quickly build custom listbox components that are fully accessible out of the box, including correct ARIA attribute management and robust keyboard navigation support. + +- [Basic example](#basic-example-2) +- [Styling the active and selected option](#styling-the-active-and-selected-option) +- [Showing/hiding the listbox](#showinghiding-the-listbox) +- [Using a custom label](#using-a-custom-label) +- [Disabling an option](#disabling-an-option) +- [Transitions](#transitions-1) +- [Rendering additional content](#rendering-additional-content-1) +- [Rendering a different element for a component](#rendering-a-different-element-for-a-component-1) +- [Component API](#component-api-2) + +### Basic example + +Listboxes are built using the `Listbox`, `ListboxButton`, `ListboxOptions`, `ListboxOption` and `ListboxLabel` components. + +The `ListboxButton` will automatically open/close the `ListboxOptions` when clicked, and when the menu is open, the list of items receives focus and is automatically navigable via the keyboard. + +```vue + + + +``` + +### Styling the active and selected option + +This is a headless component so there are no styles included by default. Instead, the components expose useful information via [render props](https://reactjs.org/docs/render-props.html) that you can use to apply the styles you'd like to apply yourself. + +To style the active `ListboxOption` you can read the `active` render prop argument, which tells you whether or not that listbox option is the option that is currently focused via the mouse or keyboard. + +To style the selected `ListboxOption` you can read the `selected` render prop argument, which tells you whether or not that listbox option is the option that is currently the `value` passed to the `Listbox`. + +> Note: An option can be both **active** and **selected** at the same time! + +You can use this state to conditionally apply whatever active/focus styles you like, for instance a blue background like is typical in most operating systems. For the selected state, a checkmark is also common. + +```vue + + + +``` + +### Using a custom label + +By default the `Listbox` will use the button contents as the label for screenreaders. However you can also render a custom `ListboxLabel`. + +```vue + + + +``` + +### Showing/hiding the listbox + +By default, your `ListboxOptions` instance will be shown/hidden automatically based on the internal `open` state tracked within the `Listbox` component itself. + +```vue + + + +``` + +If you'd rather handle this yourself (perhaps because you need to add an extra wrapper element for one reason or another), you can add a `static` prop to the `ListboxOptions` instance to tell it to always render, and inspect the `open` slot prop provided by the `Listbox` to control which element is shown/hidden yourself. + +```vue + + + +``` + +### Disabling an option + +Use the `disabled` prop to disable a `ListboxOption`. This will make it unselectable via keyboard navigation, and it will be skipped when pressing the up/down arrows. + +```vue + + + +``` + +### Transitions + +To animate the opening/closing of the listbox panel, use the provided `Transition` component. All you need to do is mark your `ListboxOptions` as `static`, wrap it in a ``, and the transition will be applied automatically. + +```vue + + + +``` + +### Rendering additional content + +The `Listbox` component is not limited to rendering only its related subcomponents. You can render anything you like within a listbox, which gives you complete control over exactly what you are building. + +For example, if you'd like to add a little header section to the listbox with some extra information in it, just render an extra `div` with your content in it. + +```vue + + + +``` + +Note that only `ListboxOption` instances will be navigable via the keyboard. + +### Rendering a different element for a component + +By default, the `Listbox` and its subcomponents each render a default element that is sensible for that component. + +For example, `ListboxLabel` renders a `label` by default, `ListboxButton` renders a `button` by default, `ListboxOptions` renders a `ul` and `ListboxOption` renders a `li` by default. `Listbox` interestingly _does not render an extra element_, and instead renders its children directly by default. + +This is easy to change using the `as` prop, which exists on every component. + +```vue + + + +``` + +To tell an element to render its children directly with no wrapper element, use `as={React.Fragment}`. + +```vue + + + +``` + +### Component API + +#### Listbox + +```vue + + + +``` + +##### Props + +| Prop | Type | Default | Description | +| --------- | ----------------------- | --------------------------------- | -------------------------------------------------------- | +| `as` | String \| Component | `template` _(no wrapper element_) | The element or component the `Listbox` should render as. | +| `v-model` | `T` _(A generic value)_ | `undefined` | The selected value. | + +##### Render prop object + +| Prop | Type | Description | +| ------ | ------- | ----------------------------------- | +| `open` | Boolean | Whether or not the listbox is open. | + +#### ListboxButton + +```vue + + {({ open }) => ( + <> + More options + + + )} + +``` + +##### Props + +| Prop | Type | Default | Description | +| ---- | ------------------- | -------- | -------------------------------------------------------------- | +| `as` | String \| Component | `button` | The element or component the `ListboxButton` should render as. | + +##### Render prop object + +| Prop | Type | Description | +| ------ | ------- | ----------------------------------- | +| `open` | Boolean | Whether or not the listbox is open. | + +#### ListboxLabel + +```vue +Enable notifications +``` + +##### Props + +| Prop | Type | Default | Description | +| ---- | ------------------- | ------- | ------------------------------------------------------------- | +| `as` | String \| Component | `label` | The element or component the `ListboxLabel` should render as. | + +#### ListboxOptions + +```vue + + > + > + +``` + +##### Props + +| Prop | Type | Default | Description | +| -------- | ------------------- | ------- | --------------------------------------------------------------------------- | +| `as` | String \| Component | `ul` | The element or component the `ListboxOptions` should render as. | +| `static` | Boolean | `false` | Whether the element should ignore the internally managed open/closed state. | + +##### Render prop object + +| Prop | Type | Description | +| ------ | ------- | ----------------------------------- | +| `open` | Boolean | Whether or not the listbox is open. | + +#### ListboxOption + +```vue +Option A +``` + +##### Props + +| Prop | Type | Default | Description | +| ---------- | ----------------------- | ----------- | --------------------------------------------------------------------------------------- | +| `as` | String \| Component | `li` | The element or component the `ListboxOption` should render as. | +| `value` | `T` _(A generic value)_ | `undefined` | The selected value. | +| `disabled` | Boolean | `false` | Whether or not the option should be disabled for keyboard navigation and ARIA purposes. | + +##### Render prop object + +| Prop | Type | Description | +| ---------- | ------- | ------------------------------------------------------------------------------------ | +| `active` | Boolean | Whether or not the option is the active/focused option in the list. | +| `selected` | Boolean | Whether or not the option is the selected option in the list. | +| `disabled` | Boolean | Whether or not the option is the disabled for keyboard navigation and ARIA purposes. | + + +## Switch (Toggle) + +[View live demo on CodeSandbox](https://codesandbox.io/s/headlessuivue-switch-example-8ycp6?file=/src/App.vue) + +The `Switch` component and related child components are used to quickly build custom switch/toggle components that are fully accessible out of the box, including correct ARIA attribute management and robust keyboard support. + +- [Basic example](#basic-example-3) +- [Using a custom label](#using-a-custom-label) +- [Component API](#component-api-3) + +### Basic example + +Switches are built using the `Switch` component. Optionally you can also use the `SwitchGroup` and `SwitchLabel` components. + +```vue + + + +``` + +### Using a custom label + +By default the `Switch` will use the contents as the label for screenreaders. If you need more control, you can render a `SwitchLabel` outside of the `Switch`, as long as both the switch and label are within a parent `SwitchGroup`. + +Clicking the label will toggle the switch state, like you'd expect from a native checkbox. + +```vue + + + +``` + +### Component API + +#### Switch + +```jsx + + Enable notifications + + +``` + +##### Props + +| Prop | Type | Default | Description | +| ---------- | ----------------------- | --------------------------------------- | -------------------------------------------------------- | +| `as` | String \| Component | `button` | The element or component the `Switch` should render as. | +| `v-model` | `T` | | The switch value. | + +##### Slot props + +| Prop | Type | Description | +| ------ | ------- | ----------------------------------- | +| `checked` | Boolean | Whether or not the switch is checked. | + + +#### Switch.Label + +```jsx + + Enable notifications + + + + +``` + +##### Props + +| Prop | Type | Default | Description | +| ---------- | ----------------------- | --------------------------------------- | -------------------------------------------------------- | +| `as` | String \| Component | `label` | The element or component the `SwitchLabel` should render as. | + +#### Switch.Group + +```jsx + + Enable notifications + + + + +``` + +##### Props + +| Prop | Type | Default | Description | +| ---------- | ----------------------- | --------------------------------------- | -------------------------------------------------------- | +| `as` | String \| Component | `template` _(no wrapper element)_| The element or component the `SwitchGroup` should render as. |