6

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

dev
  • 814
  • 10
  • 27
  • No you cannot transition to auto as is covered in many SO questions. It sounds like you need to switch off the overflow until needed. – Paulie_D Jun 05 '21 at 07:33
  • So is there any other way to achieve this ? – dev Jun 05 '21 at 16:36
  • @Paulie_D if i used onTransitionEnd event then on open of the accordion i will be able to show without scroll and a smooth transition event, but when closing it won't work is there any way to achieve this – dev Jun 05 '21 at 17:54
  • You can do a smooth transition but for that you will need to make the height based on scrollHeight. So that's how you can get a dynamic height and write the transition on height. – moshfiqrony Jun 07 '21 at 09:09
  • Are you open to installing an npm package to solve the problem? I've used this on several projects: https://www.npmjs.com/package/react-animate-height – Aaron Sarnat Jun 12 '21 at 09:29

4 Answers4

1

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

  • @ Evgeny I had one doubt regarding on this question https://stackoverflow.com/questions/70063149/event-timeline-with-animation If you can help me on this it will be really helpful, many thanks – dev Nov 23 '21 at 10:57
1

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.

Amir2mi
  • 896
  • 9
  • 13
  • @ Amir2mi I had one doubt regarding on this question https://stackoverflow.com/questions/70063149/event-timeline-with-animation If you can help me on this it will be really helpful, many thanks – dev Nov 23 '21 at 10:58
0

Use viewport units -> 100vh. https://css-tricks.com/fun-viewport-units/

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

Also, to hide the temporary scrollbar, I applied overflow: hidden; to export const CollapseContent = styled.div

Your codepen modified: https://codesandbox.io/s/strange-swirles-pebp1

Kinglish
  • 23,358
  • 3
  • 22
  • 43
  • But is it the right solution if my content gets more nesting with this 100vh will be a right solution, because i was trying to calculate the actual height of the nested children so that it will be accurate. Since we are applying overflow hidden will the content can't be shown ? On the codesandbox you have mentioned when opening the collapse its not properly animating but when closing its showing properly what could be the reason ? – dev Jun 09 '21 at 10:20
  • @dev - can you add an example of what you mean in the sandbox? Thanks – Kinglish Jun 12 '21 at 08:53
  • @ Kiglish, https://stackoverflow.com/questions/67961600/custom-label-on-select-as-floating any idea on this – dev Jun 14 '21 at 07:11
  • @ Kinglish sorry, https://stackoverflow.com/questions/67961600/custom-label-on-select-as-floating – dev Jun 14 '21 at 09:06
  • @ Kinglish I had one doubt regarding on this question https://stackoverflow.com/questions/70063149/event-timeline-with-animation If you can help me on this it will be really helpful, many thanks – dev Nov 23 '21 at 10:57
0

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).

Anthony Cregan
  • 960
  • 11
  • 25