Files
svelte-headlessui/src/routes/docs/latest/disclosure.svx
2023-06-11 14:45:30 -07:00

309 lines
10 KiB
Plaintext

# Disclosure
<script>
import Preview from "./_Preview.svelte";
</script>
<Preview url="examples/disclosure" code="" class="h-[370px] bg-fuchsia-300"/>
## Basic example
Disclosures are built using the `Disclosure`, `DisclosureButton`, and `DisclosurePanel` components.
Clicking the `DisclosureButton` will automatically open/close the `DisclosurePanel`, and all components will receive the appropriate `aria-*` attributes like `aria-expanded` and `aria-controls`.
```svelte
<script>
import {
Disclosure,
DisclosureButton,
DisclosurePanel,
} from "@rgossiaux/svelte-headlessui";
</script>
<Disclosure>
<DisclosureButton>Is team pricing available?</DisclosureButton>
<DisclosurePanel>
Yes! You can purchase a license that you can share with your entire team.
</DisclosurePanel>
</Disclosure>
```
## Styling
[See here](general-concepts#component-styling) for some general notes on styling the components in this library.
### Open panels
`Disclosure` and its related components expose a slot prop containing the `open` state of the panel. You can use this to apply whatever styles you wish.
```svelte
<script>
import {
Disclosure,
DisclosureButton,
DisclosurePanel,
} from "@rgossiaux/svelte-headlessui";
import { ChevronRightIcon } from "@rgossiaux/svelte-heroicons/solid";
</script>
<Disclosure let:open>
<DisclosureButton>
<span>Is team pricing available?</span>
<!-- Use the `open` slot prop to rotate the icon when the panel is open -->
<ChevronRightIcon style={open ? "transform: rotate(90deg);" : ""} />
</DisclosureButton>
<DisclosurePanel>
Yes! You can purchase a license that you can share with your entire team.
</DisclosurePanel>
</Disclosure>
```
## Showing/hiding the panel
By default, your `DisclosurePanel` will be shown/hidden automatically based on the internal open state tracked within the `Disclosure` component itself.
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 `DisclosurePanel` component to tell it to always render, and use the `open` slot prop to show or hide the panel yourself.
```svelte
<script>
import {
Disclosure,
DisclosureButton,
DisclosurePanel,
} from "@rgossiaux/svelte-headlessui";
</script>
<Disclosure let:open>
<DisclosureButton>Is team pricing available?</DisclosureButton>
{#if open}
<div>
<!-- Using `static`, `DisclosurePanel` is always rendered,
and ignores the `open` state -->
<DisclosurePanel static>
Yes! You can purchase a license that you can share with your entire
team.
</DisclosurePanel>
</div>
{/if}
</Disclosure>
```
## Closing disclosures manually
To close a disclosure manually when clicking a child of its panel, render that child as a `DisclosureButton`. You can use the `as` prop to customize which element is being rendered.
```svelte
<script>
import {
Disclosure,
DisclosureButton,
DisclosurePanel,
} from "@rgossiaux/svelte-headlessui";
</script>
<Disclosure>
<DisclosureButton>Open mobile menu</DisclosureButton>
<DisclosurePanel>
<DisclosureButton as="a" href="/home">Home</DisclosureButton>
<!-- ... -->
</DisclosurePanel>
</Disclosure>
```
This is especially useful when using disclosures for things like mobile menus that contain links, where you want the disclosure to close when navigating to the next page.
Alternatively, `Disclosure` and `DisclosurePanel` expose a `close()` slot prop which you can use to imperatively close the panel:
```svelte
<script>
import {
Disclosure,
DisclosureButton,
DisclosurePanel,
} from "@rgossiaux/svelte-headlessui";
</script>
<Disclosure>
<DisclosureButton>Solutions</DisclosureButton>
<DisclosurePanel let:close>
<button
on:click={async () => {
await fetch("/accept-terms", { method: "POST" });
close();
}}
>
Read and accept
</button>
<!-- ... -->
</DisclosurePanel>
</Disclosure>
```
By default the `DisclosureButton` receives focus after calling `close()`, but you can change this by passing an element into `close(el)`.
## Transitions
To animate the opening and closing of the disclosure panel, you can use [this library's Transition component](/docs/latest/transition) or Svelte's built-in transition engine. See that page for a comparison.
### Using the `Transition` component
To use the `Transition` component, all you need to do is wrap the `DisclosurePanel` in a `<Transition>` and the panel will transition automatically.
```svelte
<script>
import {
Disclosure,
DisclosureButton,
DisclosurePanel,
Transition,
} from "@rgossiaux/svelte-headlessui";
</script>
<Disclosure>
<DisclosureButton>Is team pricing available?</DisclosureButton>
<!-- This example uses Tailwind's transition classes -->
<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"
>
<DisclosurePanel>
<!-- ... -->
</DisclosurePanel>
</Transition>
</Disclosure>
```
The components in this library communicate with each other, so the Transition will be managed automatically when the Listbox is opened/closed. If you require more control over this behavior, you may use a more explicit version:
```svelte
<script>
import {
Disclosure,
DisclosureButton,
DisclosurePanel,
Transition,
} from "@rgossiaux/svelte-headlessui";
</script>
<Disclosure let:open>
<DisclosureButton>Is team pricing available?</DisclosureButton>
<!-- This example uses Tailwind's transition classes -->
<Transition
show={open}
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"
>
<!-- When controlling the transition manually, make sure to use `static` -->
<DisclosurePanel static>
<!-- ... -->
</DisclosurePanel>
</Transition>
</Disclosure>
```
### Using Svelte transitions
The last example above also provides a blueprint for using Svelte transitions:
```svelte
<script>
import {
Disclosure,
DisclosureButton,
DisclosurePanel,
} from "@rgossiaux/svelte-headlessui";
import { fade } from "svelte/transition";
</script>
<Disclosure let:open>
<DisclosureButton>Is team pricing available?</DisclosureButton>
{#if open}
<div transition:fade>
<!-- When controlling the transition manually, make sure to use `static` -->
<DisclosurePanel static>
<!-- ... -->
</DisclosurePanel>
</div>
{/if}
</Disclosure>
```
Make sure to use the `static` prop, or else the exit transitions won't work correctly.
## Accessibility notes
### Mouse interaction
Clicking a `DisclosureButton` toggles the disclosure's panel open and closed.
### Keyboard interaction
| Command | Description |
| ---------------------------------------------------------- | ------------- |
| `<Enter>` / `<Space>` when a `DisclosureButton` is focused | Toggles panel |
### Other
All relevant ARIA attributes are automatically managed.
For a full reference on all accessibility features implemented in `Disclosure`, see <a href="https://www.w3.org/TR/wai-aria-practices-1.2/#Disclosure">the ARIA spec on Disclosure</a>.
## Component API
### Disclosure
The main disclosure component.
| Prop | Default | Type | Description |
| ------------- | ------- | --------- | -------------------------------------------------- |
| `as` | `div` | `string` | The element the `Disclosure` should render as |
| `defaultOpen` | `false` | `boolean` | Whether the `Disclosure` should be open by default |
| Slot prop | Type | Description |
| --------- | ---------------------------- | ----------------------------------------------------------------------------------- |
| `open` | `boolean` | Whether the disclosure is open |
| `close` | `(el?: HTMLElement) => void` | Closes the disclosure and focuses `el`, if passed, or the `DisclosureButton` if not |
### DisclosureButton
This is the trigger button to toggle a disclosure.
You can also use this `DisclosureButton` component inside a `DisclosurePanel`. If you do, it will behave as a close button and have the appropriate `aria-*` attributes.
| Prop | Default | Type | Description |
| ---- | -------- | -------- | --------------------------------------------------- |
| `as` | `button` | `string` | The element the `DisclosureButton` should render as |
| Slot prop | Type | Description |
| --------- | --------- | ------------------------------------- |
| `open` | `boolean` | Whether or not the disclosure is open |
### DisclosurePanel
This component contains the contents of your disclosure.
| Prop | Default | Type | Description |
| --------- | ------- | --------- | ----------------------------------------------------------------------------------------------- |
| `as` | `div` | `string` | The element the `DisclosurePanel` should render as |
| `static` | `false` | `boolean` | Whether the element should ignore the internally managed open/closed state |
| `unmount` | `true` | `boolean` | Whether the element should be unmounted, instead of just hidden, based on the open/closed state |
Note that `static` and `unmount` cannot be used together.
| Slot prop | Type | Description |
| --------- | ---------------------------- | ----------------------------------------------------------------------------------- |
| `open` | `boolean` | Whether or not the disclosure is open |
| `close` | `(el?: HTMLElement) => void` | Closes the disclosure and focuses `el`, if passed, or the `DisclosureButton` if not |