Feature: Add animations to foldable section (#2202)

# Description

Refactored the foldable section. The design is inspired by [Material
Design's expansion
panel](https://m1.material.io/components/expansion-panels.html#). The
existing design has the chevron alternating from pointing down to
pointing right. This is misleading because there is no subordinate
information to the right. Also, this PR animates the arrow and foldable
section, creating a delightful experience.

## Checklist before requesting a review

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented hard-to-understand areas
- [x] New and existing unit tests pass locally with my changes
- [x] Any dependent changes have been merged

## Screenshots (if appropriate):

[expansion.webm](https://github.com/QuivrHQ/quivr/assets/1273463/71ef2c98-c0ed-4374-840e-0bf16bf4da55)
### Update

[expansion2.webm](https://github.com/QuivrHQ/quivr/assets/1273463/0be769d2-93b4-42e3-938d-3bc0a63e0e06)
This commit is contained in:
John Fewell 2024-02-20 18:08:48 -05:00 committed by GitHub
parent 7212e62859
commit ec5679072f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 42 additions and 3 deletions

View File

@ -2,6 +2,7 @@
@use "@/styles/Radius.module.scss"; @use "@/styles/Radius.module.scss";
@use "@/styles/Spacings.module.scss"; @use "@/styles/Spacings.module.scss";
@use "@/styles/Typography.module.scss"; @use "@/styles/Typography.module.scss";
@use "@/styles/Transitions.module.scss";
.foldable_section_wrapper { .foldable_section_wrapper {
display: flex; display: flex;
@ -11,6 +12,15 @@
overflow: hidden; overflow: hidden;
font-size: Typography.$small; font-size: Typography.$small;
.contentWrapper {
overflow: hidden;
transition: max-height 0.3s Transitions.$easeOutBack;
}
.contentCollapsed {
max-height: 0;
}
&.hide_border { &.hide_border {
border-color: transparent; border-color: transparent;
} }
@ -37,4 +47,16 @@
background-color: Colors.$lightest-black; background-color: Colors.$lightest-black;
} }
} }
.iconRotate {
transition: transform 0.3s Transitions.$easeOutBack;
}
.iconRotateDown {
transform: rotate(0deg);
}
.iconRotateRight {
transform: rotate(-90deg);
}
} }

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { iconList } from "@/lib/helpers/iconList"; import { iconList } from "@/lib/helpers/iconList";
@ -16,11 +16,16 @@ interface FoldableSectionProps {
export const FoldableSection = (props: FoldableSectionProps): JSX.Element => { export const FoldableSection = (props: FoldableSectionProps): JSX.Element => {
const [folded, setFolded] = useState<boolean>(false); const [folded, setFolded] = useState<boolean>(false);
const contentRef = useRef<HTMLDivElement>(null);
useEffect(() => { useEffect(() => {
setFolded(props.foldedByDefault ?? false); setFolded(props.foldedByDefault ?? false);
}, [props.foldedByDefault]); }, [props.foldedByDefault]);
const getContentHeight = (): string => {
return folded ? "0" : `${contentRef.current?.scrollHeight}px`;
};
return ( return (
<div <div
className={` className={`
@ -35,12 +40,22 @@ export const FoldableSection = (props: FoldableSectionProps): JSX.Element => {
<p className={styles.header_title}>{props.label}</p> <p className={styles.header_title}>{props.label}</p>
</div> </div>
<Icon <Icon
name={folded ? "chevronDown" : "chevronRight"} name="chevronDown"
size="normal" size="normal"
color="black" color="black"
classname={`${styles.iconRotate} ${
folded ? styles.iconRotateDown : styles.iconRotateRight
}`}
/> />
</div> </div>
<div style={{ height: folded ? "0" : "auto" }}>{props.children}</div> <div
ref={contentRef}
className={`${styles.contentWrapper} ${
folded ? styles.contentCollapsed : styles.contentExpanded
}`}
style={{ maxHeight: getContentHeight() }}
{props.children}
</div>
</div> </div>
); );
}; };

View File

@ -0,0 +1,2 @@
// Transition animations
$easeOutBack: cubic-bezier(0.65, 0.05, 0.36, 1);