nkbt/react-collapse

Starting mix-height

angelod1as opened this issue · 7 comments

I'm trying to achieve the following:

image

In order to do that, I should have a min-height property, and collapse should work from that. Eg: it will set height as 500px when collapsed, and full when no collapsed.

When clicking Veja mais, it should toggle isOpened (I already set that up using useState)

Is setting this "half-collapsed" style possible?

nkbt commented

Make your own container 500px tall, then on click - height auto.
Wrap it with Collapse (always opened + css transitions as standard).
It will work! Collapse would just follow content height with animation.

Following what you said:

import { ReactNode, useCallback, useState } from 'react'
import { StyledMosaic, SeeMore, Wrapper } from './styles'
import { Collapse } from 'react-collapse'

interface MosaicProps {
  children: ReactNode
  limit: boolean
}

export default function Mosaic({ children, limit }: MosaicProps) {
  const [isOpened, setIsOpened] = useState(false) // Handles Collapse
  const [height, setHeight] = useState('500px') // Handles height changes

  const handleClick = useCallback(() => {
    setHeight('auto')
    setIsOpened(!isOpened)
  }, [isOpened])

  return (
    <Wrapper>
      <Collapse isOpened={isOpened}> // Wraps div with 500px height initially
        <StyledMosaic height={height}>{children}</StyledMosaic> // changes height from 500px to auto on click
      </Collapse>
      {limit ? (
        <SeeMore onClick={handleClick}>
          {isOpened ? 'Veja menos' : 'Veja mais'}
        </SeeMore>
      ) : (
        ''
      )}
    </Wrapper>
  )
}

I use Styled Components to do CSS:

import styled from 'styled-components'

export const Wrapper = styled.div`
  position: relative;

  .ReactCollapse--collapse {
    transition: height 0.7s;
  }
`

export const StyledMosaic = styled.div<{ height: string }>`
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  align-items: center;
  justify-content: center;
  grid-gap: 20px;
  transition: max-height 0.3s;
  height: ${p => p.height};
`

export const SeeMore = styled.div`
  [ommited]
`

The result isn't what you described. It's like this collapsed:

image

And, when clicking the Veja mais button, it opens properly, without issue.

nkbt commented

Sure, should've tried with less variables.

Sadly, it still doesn't work or I'm still getting things wrong.


Below you can see the simplified code. No Styled Components or grid.

It's a simple component. The Collapse is opened by watching the state (it starts closed). It's height is initalized as 500, so it should work as described.

Above the Collapse component, a simple button handles click, that in time changes height to auto and toggles isOpened.

I even disabled animation to see if it would work, and it doesn't.

export default function Mosaic({ children }) {
  const [isOpened, setIsOpened] = useState(false)
  const [height, setHeight] = useState<number | string>(500)

  const handleClick = useCallback(() => {
    setHeight('auto')
    setIsOpened(!isOpened)
  }, [isOpened])

  return (
    <div>
      <button onClick={handleClick}>
        {isOpened ? 'Veja menos' : 'Veja mais'}
      </button>
      <Collapse isOpened={isOpened}>
        <div style={{ height: height }}>{children}</div>
      </Collapse>
    </div>
  )
}

Collapsed:

image

Opened (image cropped for clarity):

image

I also tried without {children}, with simple divs with text inside, and it still doesn't work.

nkbt commented

you need to make sure collapse is always opened

      <Collapse isOpened={true}>
        <div style={{ height: height }}>{children}</div>
      </Collapse>

GOT IT! Styled Components were messing with the updating height.

For reference, the working code is like this:

export default function Mosaic({ children }) {
  const [height, setHeight] = useState<number | string>(500)

  const handleClick = useCallback(() => {
    setHeight(height === 500 ? 'auto' : 500)
  }, [height])

  return (
    <Wrapper>
      <Collapse isOpened={true}>
        <div
          style={{ height: height, overflow: 'hidden', position: 'relative' }}
        >
          <StyledMosaic>{children}</StyledMosaic>
        </div>
      </Collapse>
      <SeeMore onClick={handleClick}>
        {height === 500 ? 'Veja mais' : 'Veja menos'}
      </SeeMore>
    </Wrapper>
  )
}

this div that holds the StyledMosaic is what does the magic.

OOF, @nkbt , thanks a lot for the help. I'm happily closing this issue <3

image

nkbt commented

Awesome! Glad it worked out.