Horizontal slide between multiple components using React transition group

131 views Asked by At

I'm trying to implement a carousel-like flow between multiple components just like this: enter image description here

I used CSS transition from React transition group to try and replicate it but the components are only rendered individual, and not as a flex. If I click the tab assigned to each screen, I want it to slide to the requested screen. enter image description here enter image description here

Transition.js

import React, { useRef, useEffect, useContext } from 'react';
import { CSSTransition as ReactCSSTransition } from 'react-transition-group';

const TransitionContext = React.createContext({
  parent: {},
})

function useIsInitialRender() {
  const isInitialRender = useRef(true);
  useEffect(() => {
    isInitialRender.current = false;
  }, [])
  return isInitialRender.current;
}

function CSSTransition({
  show,
  enter = '',
  enterStart = '',
  enterEnd = '',
  leave = '',
  leaveStart = '',
  leaveEnd = '',
  appear,
  unmountOnExit,
  tag = 'div',
  children,
  ...rest
}) {
  const enterClasses = enter.split(' ').filter((s) => s.length);
  const enterStartClasses = enterStart.split(' ').filter((s) => s.length);
  const enterEndClasses = enterEnd.split(' ').filter((s) => s.length);
  const leaveClasses = leave.split(' ').filter((s) => s.length);
  const leaveStartClasses = leaveStart.split(' ').filter((s) => s.length);
  const leaveEndClasses = leaveEnd.split(' ').filter((s) => s.length);
  const removeFromDom = unmountOnExit;

  function addClasses(node, classes) {
    classes.length && node.classList.add(...classes);
  }

  function removeClasses(node, classes) {
    classes.length && node.classList.remove(...classes);
  }

  const nodeRef = React.useRef(null);
  const Component = tag;

  return (
    <ReactCSSTransition
      appear={appear}
      nodeRef={nodeRef}
      unmountOnExit={removeFromDom}
      in={show}
      addEndListener={(done) => {
        nodeRef.current.addEventListener('transitionend', done, false)
      }}
      onEnter={() => {
        if (!removeFromDom) nodeRef.current.style.display = null;
        addClasses(nodeRef.current, [...enterClasses, ...enterStartClasses])
      }}
      onEntering={() => {
        removeClasses(nodeRef.current, enterStartClasses)
        addClasses(nodeRef.current, enterEndClasses)
      }}
      onEntered={() => {
        removeClasses(nodeRef.current, [...enterEndClasses, ...enterClasses])
      }}
      onExit={() => {
        addClasses(nodeRef.current, [...leaveClasses, ...leaveStartClasses])
      }}
      onExiting={() => {
        removeClasses(nodeRef.current, leaveStartClasses)
        addClasses(nodeRef.current, leaveEndClasses)
      }}
      onExited={() => {
        removeClasses(nodeRef.current, [...leaveEndClasses, ...leaveClasses])
        if (!removeFromDom) nodeRef.current.style.display = 'none';
      }}
    >
      <Component ref={nodeRef} {...rest} style={{ display: !removeFromDom ? 'none': null }}>{children}</Component>
    </ReactCSSTransition>
  )
}

function Transition({ show, appear, ...rest }) {
  const { parent } = useContext(TransitionContext);
  const isInitialRender = useIsInitialRender();
  const isChild = show === undefined;

  if (isChild) {
    return (
      <CSSTransition
        appear={parent.appear || !parent.isInitialRender}
        show={parent.show}
        {...rest}
      />
    )
  }

  return (
    <TransitionContext.Provider
      value={{
        parent: {
          show,
          isInitialRender,
          appear,
        },
      }}
    >
      <CSSTransition appear={appear} show={show} {...rest} />
    </TransitionContext.Provider>
  )
}

export default Transition;

WidgetBuilder.jsx

import React, { useState, useRef } from "react";
import { Accordion, AccordionItem,AccordionButton, AccordionPanel, AccordionIcon } from "@chakra-ui/react"

const WidgetBuilder = () => {

const [tab, setTab] = useState(1);

const tabs = useRef(null);

return (
  <Accordion allowToggle>
    <AccordionItem onClick={(e) => {e.preventDefault(); setTab(1) }}>
      <AccordionButton _focus={{ boxShadow: "none" }}>
        Widget name
      </AccordionButton>
    </AccordionItem>

    <AccordionItem onClick={(e) => {e.preventDefault(); setTab(2) }}>
      <AccordionButton _focus={{ boxShadow: "none" }}>
        Connected
      </AccordionButton>
    </AccordionItem>
  </Accordion>

  <Box ml="500px">
    <div ref={tabs}>
      <Transition
       show={tab === 1}
       appear={true}
       className="w-full"
       enter="transition ease out"
       enterStart="opacity-0 translate-y-400"
       enterEnd="translate-y-400"
       leave="transition ease-out duration-2000"
       leaveStart="opacity-0 translate-y-400"
       leaveEnd="opacity-100 -translate-y-400"
       >
         <IntroScreen />
       </Transition>

       <Transition
         show={tab === 2}
         appear={true}
         className="w-full"
         enter="transition ease out"
         enterStart="opacity-0 translate-y-400"
         enterEnd="translate-y-400"
         leave="transition ease-out duration-2000"
         leaveStart="opacity-0 translate-y-400"
         leaveEnd="opacity-100 -translate-y-400"
         >
          <SuccessScreen />
        </Transition>
       </div>
     </Box>
)
}

IntroScreen and SuccessScreen are the 2 screens I want to slide between. I will appreciate any help please.

0

There are 0 answers