Skip to content

Commit 7ed264f

Browse files
Collapsible beta!
1 parent c6c62d9 commit 7ed264f

File tree

16 files changed

+196
-31
lines changed

16 files changed

+196
-31
lines changed

apps/website/src/_state/component-statuses.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export const statusByComponent: ComponentKitsStatuses = {
3535
headless: {
3636
Accordion: ComponentStatus.Beta,
3737
Carousel: ComponentStatus.Draft,
38-
Collapsible: ComponentStatus.Draft,
38+
Collapsible: ComponentStatus.Beta,
3939
Combobox: ComponentStatus.Beta,
4040
Modal: ComponentStatus.Beta,
4141
Pagination: ComponentStatus.Draft,

apps/website/src/routes/docs/headless/collapsible/examples/animation.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { component$, useStyles$ } from '@builder.io/qwik';
22
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '@qwik-ui/headless';
3-
import styles from './collapsible.css?inline';
3+
import styles from '../snippets/collapsible.css?inline';
44
import SVG from './svg';
55

66
export default component$(() => {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { component$, useStyles$ } from '@builder.io/qwik';
2+
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '@qwik-ui/headless';
3+
import styles from '../snippets/collapsible.css?inline';
4+
import SVG from './svg';
5+
6+
export default component$(() => {
7+
useStyles$(styles);
8+
9+
return (
10+
<Collapsible class="collapsible" disabled>
11+
<CollapsibleTrigger class="collapsible-trigger">
12+
<span>Trigger</span>
13+
<SVG />
14+
</CollapsibleTrigger>
15+
<CollapsibleContent class="collapsible-content collapsible-content-outline ">
16+
Content
17+
</CollapsibleContent>
18+
</Collapsible>
19+
);
20+
});

apps/website/src/routes/docs/headless/collapsible/examples/hero.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { component$, useStyles$ } from '@builder.io/qwik';
22
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '@qwik-ui/headless';
3-
import styles from './collapsible.css?inline';
3+
import styles from '../snippets/collapsible.css?inline';
44
import SVG from './svg';
55

66
export default component$(() => {

apps/website/src/routes/docs/headless/collapsible/examples/open-change.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { component$, useStyles$, useSignal, $ } from '@builder.io/qwik';
22
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '@qwik-ui/headless';
3-
import styles from './collapsible.css?inline';
3+
import styles from '../snippets/collapsible.css?inline';
44
import SVG from './svg';
55

66
export default component$(() => {

apps/website/src/routes/docs/headless/collapsible/examples/open.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { component$, useStyles$ } from '@builder.io/qwik';
22
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '@qwik-ui/headless';
3-
import styles from './collapsible.css?inline';
3+
import styles from '../snippets/collapsible.css?inline';
44
import SVG from './svg';
55

66
export default component$(() => {

apps/website/src/routes/docs/headless/collapsible/examples/programmatic.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { component$, useSignal, useStyles$ } from '@builder.io/qwik';
22
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '@qwik-ui/headless';
3-
import styles from './collapsible.css?inline';
3+
import styles from '../snippets/collapsible.css?inline';
44

55
export default component$(() => {
66
useStyles$(styles);

apps/website/src/routes/docs/headless/collapsible/index.mdx

Lines changed: 102 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ An interactive component which expands/collapses a panel.
1919
'Full keyboard navigation',
2020
'Controlled or uncontrolled',
2121
'Initial open state does not wake up the component',
22-
'Automatic animation detection',
22+
'Automatic entry/exit animation detection',
2323
'Executes on interaction or programmatically',
2424
]}
2525
/>
@@ -66,28 +66,121 @@ As much as we love the native elements, they come with a couple of problems:
6666

6767
> To read more about the reasons for implementing an ARIA disclosure widget, check out [Scott O'Hara's article](https://www.scottohara.me/blog/2022/09/12/details-summary.html), as well as the [interactive elements](https://html.spec.whatwg.org/multipage/interactive-elements.html#interactive-elements) section of the HTML spec.
6868
69+
## Component State
70+
71+
### Uncontrolled / Initial value
72+
73+
We can select an initial uncontrolled value by passing the `open` prop to the `<Collapsible />` component.
74+
75+
<Showcase name="open" />
76+
77+
The above example expands the collapsible by default. Something to notice, is there isn't any layout shift when refreshing the page.
78+
79+
This is because the content is rendered on the server. Animations applied to `data-open` take effect after the initial render to prevent layout shift.
80+
81+
> Thanks to Qwik's JavaScript Streaming behavior, we can have open state components without even resuming them!
82+
83+
### Controlled / Reactive value
84+
85+
We can pass reactive state by using the bind:open prop to the `<Collapsible />` component.
86+
87+
<Showcase name="programmatic" />
88+
89+
bind:open is a signal prop, it allows us to programmatically control the expanded state of the collapsible.
90+
91+
### Handling open / close
92+
93+
We may want to handle the open / close of the collapsible. For example, we may want execute some code when the collapsible is opened or closed.
94+
95+
<Showcase name="open-change" />
96+
97+
To do that, we can use the onOpenChange$ prop. A parameter is passed to the handler, which is a boolean indicating whether the collapsible is open or closed.
98+
99+
### Disabled collapsible
100+
101+
<Showcase name="disabled" />
102+
103+
The collapsible can be disabled by adding the `disabled` prop to the `<Collapsible />` component.
104+
69105
## Animating the content
70106

107+
To animate the height of the content, we can use a keyframe animation on the height property.
108+
71109
<Showcase name="animation" />
72110

111+
### Height animation
112+
113+
By default, the `--qwikui-collapsible-content-height` CSS variable will automatically be set to the height of the content.
114+
115+
<CodeSnippet name="animation.css" />
116+
73117
### Why does padding or border break the animation?
74118

75-
Padding or border applied to `CollapsibleContent` breaks height animations. This is because the content height has changed.
119+
Padding or border applied to `CollapsibleContent` breaks our keyframe animation above. This is because the content height has changed.
76120

77121
To fix this, add a child element to the content, and set the padding or border on that element.
78122

123+
<CodeSnippet name="content-child" />
124+
79125
> Rather than dealing with this under the hood, we thought it'd be appropriate to keep style management as simple as possible. Let us know if you have a better solution!
80126
81-
## Component State
127+
## Example CSS
82128

83-
### Uncontrolled / Initial value
129+
<CodeSnippet name="collapsible.css" />
84130

85-
<Showcase name="open" />
131+
Every code example uses the following CSS:
86132

87-
### Controlled / Reactive value
133+
Some CSS variables are specific to the docs, feel free to plug in your own values or variables!
88134

89-
<Showcase name="programmatic" />
135+
## API
90136

91-
### Handling open / close
137+
### Data Attributes
92138

93-
<Showcase name="open-change" />
139+
`Collapsible`, `CollapsibleTrigger`, and `CollapsibleContent` all have the following data attributes that are used to track state:
140+
141+
<AnatomyTable
142+
firstColumnLabel="Attribute"
143+
propDescriptors={[
144+
{
145+
name: 'data-open',
146+
description: 'If the collapsible is open (Boolean).',
147+
},
148+
{
149+
name: 'data-closed',
150+
description: 'If the collapsible is closed (Boolean).',
151+
},
152+
{
153+
name: 'data-disabled',
154+
description: 'If the collapsible is disabled (Boolean).',
155+
},
156+
]}
157+
/>
158+
159+
### Collapsible (Root)
160+
161+
<APITable
162+
propDescriptors={[
163+
{
164+
name: 'open',
165+
type: 'boolean',
166+
description: 'Uncontrolled initial expanded value.',
167+
},
168+
{
169+
name: 'bind:open',
170+
type: 'signal',
171+
description: 'Controlled expanded value, manages the collapsible content.',
172+
info: 'boolean',
173+
},
174+
{
175+
name: 'onOpenChange$',
176+
type: 'QRL',
177+
description: 'Function called when the collapsible opens or closes.',
178+
info: 'QRL<(open: boolean) => void>',
179+
},
180+
{
181+
name: 'disabled',
182+
type: 'boolean',
183+
description: 'Disables the collapsible when true.',
184+
},
185+
]}
186+
/>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
.collapsible-content {
2+
overflow: hidden;
3+
}
4+
5+
.collapsible-content[data-open] {
6+
animation: 550ms cubic-bezier(0.87, 0, 0.13, 1) 0s 1 normal forwards collapsible-open;
7+
}
8+
.collapsible-content[data-closed] {
9+
animation: 350ms cubic-bezier(0.87, 0, 0.13, 1) 0s 1 normal forwards collapsible-closed;
10+
}
11+
12+
@keyframes collapsible-open {
13+
from {
14+
height: 0;
15+
}
16+
to {
17+
height: var(--qwikui-collapsible-content-height);
18+
}
19+
}
20+
21+
@keyframes collapsible-closed {
22+
from {
23+
height: var(--qwikui-collapsible-content-height);
24+
}
25+
to {
26+
height: 0;
27+
}
28+
}

apps/website/src/routes/docs/headless/collapsible/examples/collapsible.css renamed to apps/website/src/routes/docs/headless/collapsible/snippets/collapsible.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
align-items: center;
1313
}
1414

15+
.collapsible-trigger[data-disabled] {
16+
opacity: 0.3;
17+
}
18+
1519
.collapsible-trigger[data-open] svg {
1620
transform: rotate(180deg);
1721
}

0 commit comments

Comments
 (0)