Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 19 additions & 18 deletions packages/docs/pages/components/Collapse.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

<section class="odocs-head">

The **Collapse** component is an easy way to toggle the visibility of content with show/hide functionality.
It has two elements: a disclosure button and a section of content whose visibility is controlled by the button.
The component implements the W3C ARIA APG [Disclosure (Show/Hide) Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/) and also supports the W3C ARIA APG [Accordion Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/).
The **Collapse** component (also known as _accordion_) is a disclosure widget which is used to toggle the display of further information.
The component is implemented based on the [HTML \<details\> element](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/details) and consists of two elements: a short disclosure label (trigger) and a section of content whose visibility it controls; the content can be 'collapsed' or 'expanded'.
This component implements the W3C ARIA APG [Disclosure (Show/Hide) Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/) and also supports the W3C ARIA APG [Accordion Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/).

</section>

Expand All @@ -22,31 +22,32 @@ The component implements the W3C ARIA APG [Disclosure (Show/Hide) Pattern](https

## Collapse Component

> An easy way to toggle what you want.
> An easy disclosure widget to toggle content visability.

```html
<o-collapse></o-collapse>
```

### Props

| Prop name | Description | Type | Values | Default |
| --------- | ---------------------------------------------------------------------------- | ----------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| animation | Custom animation (transition name) | string | - | <div><small>From <b>config</b>:</small></div><code style='white-space: nowrap; padding: 0;'>collapse: {<br>&nbsp;&nbsp;animation: "fade"<br>}</code> |
| contentId | Id property of the content container - default is an uuid | string | - | <code style='white-space: nowrap; padding: 0;'>useId()</code> |
| expanded | Expand the trigger to fullwidth | boolean | - | <code style='white-space: nowrap; padding: 0;'>false</code> |
| open | Whether collapse is open or not, use v-model:open to make it two-way binding | boolean | - | <code style='white-space: nowrap; padding: 0;'>true</code> |
| override | Override existing theme classes completely | boolean | - | |
| position | Trigger position | "bottom" \| "top" | `top`, `bottom` | <div><small>From <b>config</b>:</small></div><code style='white-space: nowrap; padding: 0;'>collapse: {<br>&nbsp;&nbsp;position: "top"<br>}</code> |
| triggerId | Id property of the trigger container - default is an uuid | string | - | <code style='white-space: nowrap; padding: 0;'>useId()</code> |
| Prop name | Description | Type | Values | Default |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| contentId | Id property of the content container - default is an uuid | string | - | <code style='white-space: nowrap; padding: 0;'>useId()</code> |
| expanded | Expand the trigger to fullwidth | boolean | - | <code style='white-space: nowrap; padding: 0;'>false</code> |
| label | Some label displayed in the summary element - unnecessary when trigger slot is used | string | - | |
| name | Setting the same name to multiple collapse elements connects them together,<br/>with only one open at a time.<br/>This allows to easily create UI features such as accordions. | string | - | |
| open | Whether collapse is open or not, use v-model:open to make it two-way binding | boolean | - | <code style='white-space: nowrap; padding: 0;'>false</code> |
| override | Override existing theme classes completely | boolean | - | |
| position | Trigger position | "bottom" \| "top" | `top`, `bottom` | <div><small>From <b>config</b>:</small></div><code style='white-space: nowrap; padding: 0;'>collapse: {<br>&nbsp;&nbsp;position: "bottom"<br>}</code> |
| triggerId | Id property of the trigger container - default is an uuid | string | - | <code style='white-space: nowrap; padding: 0;'>useId()</code> |

### Events

| Event name | Properties | Description |
| ----------- | --------------------------------------- | ------------------------- |
| update:open | **value** `boolean` - updated open prop | open prop two-way binding |
| open | | on collapse opened |
| close | | on collapse closed |
| Event name | Properties | Description |
| ----------- | ------------------------------------------------- | ------------------------- |
| update:open | **value** `boolean` - updated open prop | open prop two-way binding |
| open | **event** `ToggleEvent` - the native toggle event | on collapse opened |
| close | **event** `ToggleEvent` - the native toggle event | on collapse closed |

### Slots

Expand Down
81 changes: 46 additions & 35 deletions packages/oruga/src/components/collapse/Collapse.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<script setup lang="ts">
import { computed, useId } from "vue";
import { computed, useId, useTemplateRef } from "vue";

import { getDefault } from "@/utils/config";
import { defineClasses } from "@/composables";

import type { CollapseProps } from "./props";

/**
* An easy way to toggle what you want.
* An easy disclosure widget to toggle content visability.
* @displayName Collapse
* @style _collapse.scss
*/
Expand All @@ -19,10 +19,11 @@ defineOptions({

const props = withDefaults(defineProps<CollapseProps>(), {
override: undefined,
open: true,
open: false,
name: undefined,
label: undefined,
expanded: false,
animation: () => getDefault("collapse.animation", "fade"),
position: () => getDefault("collapse.position", "top"),
position: () => getDefault("collapse.position", "bottom"),
contentId: () => useId(),
triggerId: () => useId(),
});
Expand All @@ -33,10 +34,14 @@ const emits = defineEmits<{
* @param value {boolean} - updated open prop
*/
"update:open": [value: boolean];
/** on collapse opened */
open: [];
/** on collapse closed */
close: [];
/** on collapse opened
* @param event {ToggleEvent} - the native toggle event
*/
open: [event: ToggleEvent];
/** on collapse closed
* @param event {ToggleEvent} - the native toggle event
*/
close: [event: ToggleEvent];
}>();

defineSlots<{
Expand All @@ -49,16 +54,21 @@ defineSlots<{
trigger?(props: { open: boolean }): void;
}>();

const isOpen = defineModel<boolean>("open", { default: true });
const detailsRef = useTemplateRef("detailsElement");

const isOpen = defineModel<boolean>("open", { default: false });

/** detail open state toggle handler */
function onToggle(event: ToggleEvent): void {
if (!detailsRef.value) return;

isOpen.value = detailsRef.value.open;

/** Toggle and emit events */
function toggle(): void {
isOpen.value = !isOpen.value;
if (isOpen.value) emits("open");
else emits("close");
if (detailsRef.value.open) emits("open", event);
else emits("close", event);
}

// --- Computed Component Classes ---
// #region --- Computed Component Classes ---

const rootClasses = defineClasses(
["rootClass", "o-collapse"],
Expand All @@ -81,31 +91,32 @@ const triggerClasses = defineClasses(
);

const contentClasses = defineClasses(["contentClass", "o-collapse__content"]);

// #endregion --- Computed Component Classes ---
</script>

<template>
<div data-oruga="collapse" :class="rootClasses">
<div
<details
ref="detailsElement"
data-oruga="collapse"
:class="rootClasses"
:open="isOpen"
:name="name"
@toggle="onToggle">
<summary
:id="triggerId"
:class="triggerClasses"
role="button"
tabindex="0"
:class="triggerClasses"
:aria-controls="contentId"
:aria-expanded="isOpen"
@click="toggle"
@keydown.enter.prevent="toggle"
@keydown.space.prevent="toggle">
<slot name="trigger" :open="isOpen" />
</div>
:aria-expanded="isOpen">
<slot name="trigger" :open="isOpen">
{{ label }}
</slot>
</summary>

<Transition :name="animation">
<div
v-show="isOpen"
:id="contentId"
:class="contentClasses"
:aria-labelledby="triggerId">
<slot />
</div>
</Transition>
</div>
<div :id="contentId" :class="contentClasses">
<slot />
</div>
</details>
</template>
13 changes: 5 additions & 8 deletions packages/oruga/src/components/collapse/examples/accordion.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
<script setup lang="ts">
import { ref } from "vue";

const isOpen = ref(0);

const collapses = ref([
{
title: "Open to read something intersting written for you!",
text: `Lorem ipsum dolor sit amet, consectetur adipiscing elit. <br />
Nulla accumsan, metus ultrices eleifend gravida, <br />
nulla nunc varius lectus, nec rutrum justo nibh eu lectus. <br />
text: `Lorem ipsum dolor sit amet, consectetur adipiscing elit. <br />
Nulla accumsan, metus ultrices eleifend gravida, <br />
nulla nunc varius lectus, nec rutrum justo nibh eu lectus. <br />
Ut vulputate semper dui. Fusce erat odio, sollicitudin vel erat vel, interdum mattis neque.`,
},
{
Expand All @@ -28,10 +26,9 @@ const collapses = ref([
v-for="(collapse, index) of collapses"
:key="index"
class="card"
animation="slide"
name="my-accordion"
expanded
:open="isOpen == index"
@update:open="isOpen = index">
:open="index === 0">
<template #trigger="{ open }">
<div class="card-header">
<span class="card-header-title">
Expand Down
14 changes: 7 additions & 7 deletions packages/oruga/src/components/collapse/examples/base.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<template>
<section>
<o-collapse :open="false" expanded trigger-class="trigger">
<o-collapse expanded>
<template #trigger="{ open }">
<b>
<h3 class="trigger">
<o-icon :icon="open ? 'chevron-down' : 'chevron-right'" />
{{ open ? "Close" : "Open" }} Collapse!
</b>
</h3>
</template>

<div class="notification">
<h3>Subtitle</h3>
<h4>Subtitle</h4>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<br />
Expand All @@ -24,10 +24,10 @@
</section>
</template>

<style lang="css" scoped>
:deep(.trigger) {
<style scoped>
.trigger {
padding: 1rem;
background-color: var(--vp-c-brand-1);
background-color: var(--vp-c-brand-2);
color: white;
box-shadow:
0 2px 3px hsla(0, 0%, 4%, 0.1),
Expand Down
17 changes: 14 additions & 3 deletions packages/oruga/src/components/collapse/examples/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,33 @@
import Base from "./base.vue";
import BaseCode from "./base.vue?raw";

import Position from "./position.vue";
import PositionCode from "./position.vue?raw";

import Accordion from "./accordion.vue";
import AccordionCode from "./accordion.vue?raw";
</script>

### Base

A custom trigger can be passed in the `trigger` slot.
A custom trigger can be passed in the `trigger` slot oder by the `label` property.
When the controlled content is hidden, the trigger is often styled as a typical push button with a right-pointing arrow or triangle, to indicate that activating the trigger will display the hidden content.
When the content is visible, the arrow or triangle usually points down.

::: info Accessibility Note:
The trigger container is already an interactive element with the `role="button"` attribute. For accessibility reasons, prevent adding other interactive elements such as buttons to avoid [nested-interactive](https://accessibilityinsights.io/info-examples/web/nested-interactive/) accessibility problems.
The trigger container is already an interactive element with the `role="button"` attribute. For accessibility reasons, prevent adding other interactive elements such as buttons in the `trigger` slot to avoid [nested-interactive](https://accessibilityinsights.io/info-examples/web/nested-interactive/) accessibility problems.
:::

<ExampleViewer :component="Base" :code="BaseCode" />

### Position

The collapse can be configured by the `position` property to open to top instead of bottom.

<ExampleViewer :component="Position" :code="PositionCode" />

### Accordion

Combine multiple collapse components to create an accordion behaviour.
Using the [`name`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/details#name) property multiple collapse components can be connected to create an accordion behaviour, with only one content open at a time.

<ExampleViewer :component="Accordion" :code="AccordionCode" />
24 changes: 7 additions & 17 deletions packages/oruga/src/components/collapse/examples/inspector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,25 +36,15 @@ const inspectData: InspectData<CollapseClasses, CollapseProps> = {

<template>
<inspector-wrapper v-slot="props" :inspect-data="inspectData">
<o-collapse
animation="slide"
:open="true"
expanded
class="card"
v-bind="props">
<template #trigger="props">
<div class="card-header" role="button">
<span class="card-header-title"> Collapse Title </span>
<span class="card-header-icon">
<o-icon
:icon="props.open ? 'caret-up' : 'caret-down'" />
</span>
</div>
<o-collapse open expanded v-bind="props">
<template #trigger="{ open }">
<p>
<o-icon :icon="open ? 'chevron-down' : 'chevron-right'" />
{{ open ? "Close" : "Open" }} Collapse!
</p>
</template>

<div class="card-content">
<div class="content">Collapse Content</div>
</div>
<div>Collapse Content</div>
</o-collapse>
</inspector-wrapper>
</template>
36 changes: 36 additions & 0 deletions packages/oruga/src/components/collapse/examples/position.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<template>
<section>
<o-collapse expanded position="top">
<template #trigger="{ open }">
<p class="trigger">
<o-icon :icon="open ? 'chevron-down' : 'chevron-right'" />
{{ open ? "Close" : "Open" }} Collapse!
</p>
</template>

<div class="notification">
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<br />
Nulla accumsan, metus ultrices eleifend gravida, nulla nunc
varius lectus, nec rutrum justo nibh eu lectus.
<br />
Ut vulputate semper dui. Fusce erat odio, sollicitudin vel
erat vel, interdum mattis neque.
</p>
</div>
</o-collapse>
</section>
</template>

<style scoped>
.trigger {
padding: 1rem;
background-color: var(--vp-c-brand-3);
color: white;
box-shadow:
0 2px 3px hsla(0, 0%, 4%, 0.1),
0 0 0 1px hsla(0, 0%, 4%, 0.1);
text-align: center;
}
</style>
Loading
Loading