Skip to content

Commit 9b1cb09

Browse files
Merge pull request #780 from thejackshelton/accordion-animation
Accordion animation
2 parents 2fc1226 + ec77bcb commit 9b1cb09

File tree

9 files changed

+26
-22
lines changed

9 files changed

+26
-22
lines changed

.changeset/two-glasses-fix.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ The Accordion has been refactored from the ground up to be more accessible and p
2020

2121
- The default behavior is a single item open at a time.
2222

23+
- The `animated` prop has been removed. Animations are now automatically detected!
24+
2325
- `onSelectIndexChange$` has been deprecated and removed in favor of `onChange$`.
2426

2527
- `onFocusIndexChange$` has been deprecated and removed. Let us know if you have a use case for this.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export default component$(() => {
66
const items = [1, 2, 3];
77

88
return (
9-
<Accordion.Root animated>
9+
<Accordion.Root>
1010
{items.map((item) => (
1111
<Accordion.Item class="collapsible" key={item}>
1212
<Accordion.Header>

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,14 +135,12 @@ The component itself can also be disabled by setting the `disabled` prop to true
135135

136136
### Height Animation
137137

138-
To animate the height of the content, set the `animated` prop to `true`.
138+
To animate the Accordion content, the `--qwikui-collapsible-content-height` CSS variable in your keyframes.
139139

140140
<Showcase name="animation" />
141141

142142
> In the near future, you won't need JavaScript at all to animate the height of content! Here's the [CSS WG Proposal](https://github.com/w3c/csswg-drafts/blob/main/css-values-5/calc-size-explainer.md) for more info.
143143
144-
You can use the `--qwikui-collapsible-content-height` CSS variable in your keyframes.
145-
146144
<CodeSnippet name="animation.css" />
147145

148146
### Why does padding or border break the animation?

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export default component$(() => {
88

99
return (
1010
<div>
11-
<Collapsible.Root class="collapsible" bind:open={isOpen} animated>
11+
<Collapsible.Root class="collapsible" bind:open={isOpen}>
1212
<Collapsible.Trigger class="collapsible-trigger">
1313
<span>Trigger</span>
1414
<LuChevronDown class="collapsible-transition" />

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,12 @@ To animate the height of the content, we can use a keyframe animation on the hei
118118

119119
### Height Animation
120120

121-
To animate the height of the content, set the `animated` prop to `true`.
121+
To animate the Accordion content, the `--qwikui-collapsible-content-height` CSS variable in your keyframes.
122122

123123
<Showcase name="animation" />
124124

125125
> In the near future, you won't need JavaScript at all to animate the height of content! Here's the [CSS WG Proposal](https://github.com/w3c/csswg-drafts/blob/main/css-values-5/calc-size-explainer.md) for more info.
126126
127-
You can use the `--qwikui-collapsible-content-height` CSS variable in your keyframes.
128-
129127
<CodeSnippet name="animation.css" />
130128

131129
### Why does padding or border break the animation?

packages/kit-headless/src/components/accordion/accordion-item.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ export const HAccordionItem = component$(
105105
disabled={context.disabled || props.disabled}
106106
collapsible={context.collapsible}
107107
accordionItem
108-
animated={context.isAnimatedSig.value}
109108
{...props}
110109
>
111110
<Slot />

packages/kit-headless/src/components/collapsible/collapsible-content.tsx

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ import { isServer } from '@builder.io/qwik/build';
1616
export const HCollapsibleContent = component$((props: CollapsibleContentProps) => {
1717
const context = useContext(collapsibleContextId);
1818
const isHiddenSig = useSignal<boolean>(!context.isOpenSig.value);
19-
// check if it's initially "animatable"
20-
const initialRenderSig = useSignal<boolean>(true);
19+
const isAnimatedSig = useSignal<boolean>(false);
2120
const contentId = `${context.itemId}-content`;
2221
const triggerId = `${context.itemId}-trigger`;
2322

@@ -27,20 +26,35 @@ export const HCollapsibleContent = component$((props: CollapsibleContentProps) =
2726
}
2827
});
2928

30-
useTask$(async function animations({ track }) {
29+
// animations are detected automatically
30+
useTask$(async function automaticAnimations({ track }) {
3131
track(() => context.isOpenSig.value);
3232

33-
if (isServer || !context.isAnimatedSig.value) {
33+
if (isServer || !context.contentRef.value) {
3434
return;
3535
}
3636

3737
await context.getContentDimensions$();
3838

3939
if (context.isOpenSig.value) {
40+
context.contentRef.value.removeAttribute('data-closed');
41+
context.contentRef.value.dataset.open = '';
4042
isHiddenSig.value = false;
43+
} else {
44+
context.contentRef.value.dataset.closed = '';
45+
context.contentRef.value.removeAttribute('data-open');
4146
}
4247

43-
initialRenderSig.value = false;
48+
// check if the content element has an animation or transition duration
49+
const { animationDuration, transitionDuration } = getComputedStyle(
50+
context.contentRef.value,
51+
);
52+
53+
if (animationDuration !== '0s' || transitionDuration !== '0s') {
54+
isAnimatedSig.value = true;
55+
} else {
56+
isAnimatedSig.value = false;
57+
}
4458
});
4559

4660
return (
@@ -50,11 +64,9 @@ export const HCollapsibleContent = component$((props: CollapsibleContentProps) =
5064
id={contentId}
5165
data-collapsible-content
5266
data-disabled={context.disabled ? '' : undefined}
53-
data-open={!initialRenderSig.value && context.isOpenSig.value ? '' : undefined}
54-
data-closed={!context.isOpenSig.value ? '' : undefined}
5567
onAnimationEnd$={[hideContent$, props.onAnimationEnd$]}
5668
onTransitionEnd$={[hideContent$, props.onTransitionEnd$]}
57-
hidden={context.isAnimatedSig.value ? isHiddenSig.value : !context.isOpenSig.value}
69+
hidden={isAnimatedSig.value ? isHiddenSig.value : !context.isOpenSig.value}
5870
aria-labelledby={triggerId}
5971
>
6072
<Slot />

packages/kit-headless/src/components/collapsible/collapsible-context.type.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,5 @@ export interface CollapsibleContext {
88
contentHeightSig: Signal<number | null>;
99
getContentDimensions$: QRL<() => void>;
1010
disabled: boolean | undefined;
11-
isAnimatedSig: Signal<boolean>;
1211
collapsible?: boolean;
1312
}

packages/kit-headless/src/components/collapsible/collapsible.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ export type CollapsibleProps = PropsOf<'div'> & {
2626
triggerRef?: Signal<HTMLButtonElement>;
2727
collapsible?: boolean;
2828
accordionItem?: boolean;
29-
animated?: boolean;
3029
};
3130

3231
export const HCollapsible = component$((props: CollapsibleProps) => {
@@ -40,7 +39,6 @@ export const HCollapsible = component$((props: CollapsibleProps) => {
4039
collapsible = true,
4140
open,
4241
accordionItem,
43-
animated,
4442
...rest
4543
} = props;
4644

@@ -50,7 +48,6 @@ export const HCollapsible = component$((props: CollapsibleProps) => {
5048
const defaultTriggerRef = useSignal<HTMLButtonElement>();
5149
const triggerRef = givenTriggerRef ?? defaultTriggerRef;
5250
const contentRef = useSignal<HTMLElement>();
53-
const isAnimatedSig = useSignal<boolean>(animated === true);
5451

5552
const contentHeightSig = useSignal<number | null>(null);
5653

@@ -102,7 +99,6 @@ export const HCollapsible = component$((props: CollapsibleProps) => {
10299
contentHeightSig,
103100
getContentDimensions$,
104101
disabled,
105-
isAnimatedSig,
106102
collapsible,
107103
};
108104

0 commit comments

Comments
 (0)