react-icons/bs#BsArrowDownShort JavaScript Examples

The following examples show how to use react-icons/bs#BsArrowDownShort. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: LineageFilter.jsx    From covince with MIT License 4 votes vote down vote up
LineageFilter = (props) => {
  const {
    allSelected,
    className,
    emptyMessage,
    fixedLayout,
    heading = defaultHeading,
    isMobile,
    sortedLineages,
    toggleAll,
    toggleLineage
  } = props

  const isScrolling = useMemo(() => {
    return fixedLayout || sortedLineages.length > (isMobile ? 9 : 10)
  }, [fixedLayout, sortedLineages, isMobile])

  const sections = useMemo(() => {
    if (!isScrolling) {
      return [sortedLineages]
    }
    const sectionSize = isMobile ? 9 : 8
    const _sections = []
    const numSections = Math.ceil(sortedLineages.length / sectionSize) || 1
    for (let i = 0; i < numSections; i++) {
      const start = i * sectionSize
      _sections.push(sortedLineages.slice(start, start + sectionSize))
    }
    return _sections
  }, [sortedLineages, isMobile])

  const { nomenclature } = useNomenclature()
  const isLarge = useScreen('lg')

  const gridStyle = useMemo(() => {
    const style = {
      scrollSnapAlign: 'start',
      scrollSnapStop: 'always'
    }
    if (isMobile) {
      return style
    }
    const numLineages = sortedLineages.length
    const maxColumns = isScrolling ? 4 : 5
    const numColumns =
      isLarge && fixedLayout
        ? maxColumns
        : Math.max(2, Math.min(Math.ceil(numLineages / 2), maxColumns))
    return {
      ...style,
      gridTemplateColumns: `repeat(${numColumns}, minmax(0, 1fr))`
    }
  }, [sortedLineages, fixedLayout, isScrolling, isMobile, isLarge])

  const scrollContainer = useRef()
  const userScrolled = useRef(false)

  const doScroll = useCallback((direction) => {
    const { height } = scrollContainer.current.getBoundingClientRect()
    scrollContainer.current.scrollBy({
      top: height * direction,
      behavior: 'smooth'
    })
    userScrolled.current = true
  }, [])

  const sectionRefs = useRef([])
  const [currentSection, setCurrentSection] = useState(null)
  useEffect(() => {
    const callback = entries => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          const index = (sectionRefs.current).indexOf(entry.target)
          setCurrentSection(index)
        }
      })
    }

    const observer = new IntersectionObserver(callback, {
      root: scrollContainer.current,
      threshold: 0.6
    })
    sectionRefs.current.filter(s => s).forEach(section => {
      observer.observe(section)
    })

    return function cleanup () {
      observer.disconnect()
    }
  }, [sections])

  const scrollUpBtnRef = useRef(null)
  const scrollDownBtnRef = useRef(null)

  useEffect(() => {
    if (isMobile || userScrolled.current === false) {
      return
    }
    if (currentSection === 0) {
      scrollDownBtnRef.current.focus()
    } else if (currentSection === sections.length - 1) {
      scrollUpBtnRef.current.focus()
    }
  }, [isMobile, currentSection])

  return (
    <div className={className}>
      <header className='flex justify-between space-x-6'>
        {heading}
        <div className='flex items-center'>
          <label
            htmlFor='lineage_toggle_all'
            className='pr-2 text-primary text-xs uppercase tracking-wide font-bold leading-5'
          >
            toggle all
          </label>
          <Checkbox
            className='text-xs text-primary mx-auto flex-row-reverse'
            id='lineage_toggle_all'
            checked={allSelected}
            onChange={toggleAll}
          />
        </div>
      </header>
      <div className='md:flex md:mt-0.5'>
        <form
          ref={scrollContainer}
          className={classNames(
            'overflow-auto hide-scrollbars flex-grow -mx-4 md:-mx-2 flex md:flex-col md:h-16',
            { 'md:-mx-1': fixedLayout }
          )}
          style={{ scrollSnapType: isMobile ? 'x mandatory' : 'y mandatory' }}
        >
          {sections.map((lineages, i) => (
            <section
              key={`lineages-${i}`}
              ref={el => { sectionRefs.current[i] = el }}
              className={classNames(
                'w-full h-full flex-shrink-0 flex flex-wrap content-start px-4 md:px-0 md:grid md:gap-0.5 relative',
                { 'md:px-1': fixedLayout }
              )}
              style={gridStyle}
            >
              { lineages.length > 0
                ? lineages.map(({ lineage, active, colour, title = lineage, primaryText, secondaryText = lineage }) => (
                  <Checkbox
                    key={lineage}
                    className={classNames(
                      'w-1/3 my-1 h-7 md:my-0',
                      fixedLayout ? 'md:w-24 md:mx-0.5' : 'md:w-auto md:mx-2',
                      {
                        'md:mb-1': isScrolling && nomenclature.length === 0
                      }
                    )}
                    title={title}
                    style={{ color: colour }}
                    id={`lineage_filter_${lineage}`}
                    checked={active}
                    onChange={() => toggleLineage(lineage)}
                  >
                    {primaryText ? <span className={classNames('block text-gray-700 dark:text-gray-100')}>{primaryText}</span> : null}
                    <span className={classNames({ 'text-xs tracking-wide leading-none text-gray-500 dark:text-gray-300': primaryText })}>{secondaryText}</span>
                  </Checkbox>
                ))
                : <>
                    <div className={classNames({ 'lg:w-24 lg:mx-0.5': fixedLayout })} />
                    <div className='absolute inset-0 flex items-center justify-center'>
                      {emptyMessage}
                    </div>
                  </> }
            </section>
          ))}
        </form>
        {(sections.length > 1 || (!isMobile && fixedLayout)) && (
          isMobile
            ? <ol className='list-none p-1 flex justify-center space-x-2'>
              { sections.map((_, i) =>
                <li
                  key={`section-indicator-${i}`}
                  className={classNames(
                    'rounded-full bg-gray-500 dark:bg-gray-300 w-2 h-2 transition-opacity',
                    { 'opacity-50': i !== currentSection || 0 }
                  )}
                />
              )}
            </ol>
            : <form onSubmit={e => e.preventDefault()}
              className={classNames(
                'flex flex-col justify-center relative left-1 pb-1 space-y-0.5 md:ml-2',
                { 'lg:ml-0': fixedLayout }
              )}
            >
            <Button
              ref={scrollUpBtnRef}
              title='Previous lineages'
              className='w-6 h-6 !p-0 flex items-center text-gray-700 dark:text-gray-200 transition-opacity disabled:opacity-50'
              onClick={() => doScroll(-1)}
              disabled={currentSection === 0 || null}
            >
              <BsArrowUpShort className='fill-current w-6 h-6'/>
            </Button>
            <Button
              ref={scrollDownBtnRef}
              title='Next lineages'
              className='w-6 h-6 !p-0 flex items-center text-gray-700 dark:text-gray-200 transition-opacity disabled:opacity-50'
              onClick={() => doScroll(1)}
              disabled={currentSection === sections.length - 1}
            >
              <BsArrowDownShort className='fill-current w-6 h-6'/>
            </Button>
          </form>
        )}
      </div>
    </div>
  )
}