nested dynamic height transition effect

Viewed 872

Trying to create a reusable collapse component, but having a smooth transition on the element getting a problem. So when the collapse item is clicked i want have a smooth transition

Here is the main part of what i have tried so far.

index.js

const Collapse = ({ title, text, child, ...props }) => {
  const [isOpen, setIsOpen] = useState(false);

  const toggleCollapse = () => setIsOpen((isOpen) => !isOpen);
  const closeCollapse = () => setIsOpen(false);

  const content = useRef(null);

  const isParentOpen = props.isParentOpen;

  useEffect(() => {
    if (!isParentOpen) closeCollapse();
  }, [isParentOpen]);

  const height = !isOpen ? "0px" : `auto`; // ${content.current?.scrollHeight}px

  return (
    <CollapseContainer>
      <CollapseButton isOpen={isOpen} onClick={toggleCollapse}>
        <CollapseTitleWrapper>{title}</CollapseTitleWrapper>
      </CollapseButton>
      <CollapseContent ref={content} max_height={height}>
        <CollapseText>
          {text}
          {child?.map((datumn, index) => (
            <Collapse
              {...datumn}
              key={`collapse-child-${index}`}
              isParentOpen={isOpen}
            />
          ))}
        </CollapseText>
      </CollapseContent>
    </CollapseContainer>
  );
};

export default Collapse;

So i am able to calculate the height of the content dynamically using ref, but smooth transition will happen but i will get a scroll inside the child collapse nested that i don't want. Is there way to apply transition on height:auto.

Here is the working codesandbox

4 Answers

it's impossible to use CSS animations with auto keyword. A possible solution is to use height instead of maxHeight and overflow: hidden and set height to the auto when animation is finished. I would recommend using WAAPI(Web Animation API) as it simplifies using animations in js, and do not bubble events like css transitions.

here's an example: https://codesandbox.io/s/determined-curran-hsrm9?file=/src/Collapse/index.js

I think you should change the concept, it is doable simply with CSS animation that is way more optimized and allows you to create custom effects!

First we define two animations (is-open and is-closed) :

.is-open {
  animation: is-open 0.3s ease both;
}

@keyframes is-open {
  from {
    opacity: 0;
    transform: scaleY(0);
  }
  to {
    opacity: 1;
    transform: scaleY(1);
    height: 100%;
  }
}

and

.is-closed {
  animation: is-closed 0.3s ease both;
}

@keyframes is-closed {
  from {
    opacity: 1;
    transform: scaleY(1);
  }
  to {
    opacity: 0;
    transform: scaleY(0);
    height: 0;
  }
}

Now the only thing we need is a conditional class which handle these animations:

<CollapseContent
    ref={content}
    className={isOpen ? "is-open" : "is-closed"}
>

Check the result here.

Get the height of the element with: Ref.current.getBoundingClientRect().height The instant the transition starts add the style overflow:hidden to prevent the scrollbar, then create a function that is called on the transitioned element using onTransitionEnd to reactivate overflow when the transition has completed (if needed).

Related