@botonic/react
Version:
Build Chatbots using React
182 lines (167 loc) • 5.19 kB
JSX
import { INPUT, isBrowser } from '@botonic/core'
import React, { useContext, useEffect, useRef, useState } from 'react'
import styled from 'styled-components'
import LeftArrow from '../assets/leftArrow.svg'
import RightArrow from '../assets/rightArrow.svg'
import { COLORS, WEBCHAT } from '../constants'
import { WebchatContext } from '../contexts'
import { resolveImage } from '../util/environment'
import { StyledScrollbar } from '../webchat/components/styled-scrollbar'
import { ButtonsDisabler } from './buttons-disabler'
import { Message } from './message'
const StyledCarousel = styled.div`
padding: 10px 0px;
display: flex;
flex-direction: row;
max-width: 100%;
${props => props.carouselArrowsEnabled && 'overflow-x: auto;'}
`
const StyledItems = styled.div`
display: flex;
`
const StyledArrowContainer = styled.div`
position: absolute;
top: calc(50% - 20px);
height: 40px;
width: 25px;
background: ${COLORS.SILVER};
display: flex;
align-items: center;
cursor: pointer;
justify-content: ${props => props.justifyContent};
left: ${props => props.left}px;
right: ${props => props.right}px;
border-top-${props => props.arrow}-radius: 30px;
border-bottom-${props => props.arrow}-radius: 30px;
`
const StyledArrow = styled.img`
width: 20px;
height: 20px;
`
const serialize = carouselProps => {
let carouselChildren = carouselProps.children
if (!Array.isArray(carouselChildren)) carouselChildren = [carouselChildren]
return {
type: INPUT.CAROUSEL,
elements: carouselChildren.map(
e => e && e.type && e.type.serialize && e.type.serialize(e.props)
),
}
}
/**
*
* @param {MessageProps} props
* @returns {JSX.Element}
*/
export const Carousel = props => {
const { getThemeProperty } = useContext(WebchatContext)
let content = props.children
const scrollbarOptions = {
...{ enable: true, autoHide: true },
...getThemeProperty(WEBCHAT.CUSTOM_PROPERTIES.scrollbar),
}
const [hasLeftArrow, setLeftArrow] = useState(false)
const [hasRightArrow, setRightArrow] = useState(true)
const carouselRef = useRef(null)
const CustomCarouselLeftArrow = getThemeProperty(
WEBCHAT.CUSTOM_PROPERTIES.customCarouselLeftArrow,
undefined
)
const CustomCarouselRightArrow = getThemeProperty(
WEBCHAT.CUSTOM_PROPERTIES.customCarouselRightArrow,
undefined
)
const carouselArrowsEnabled = getThemeProperty(
WEBCHAT.CUSTOM_PROPERTIES.enableCarouselArrows,
true
)
const scrollCarouselBy = value => {
carouselRef.current.scrollBy({
left: value,
behavior: 'smooth',
})
}
const setArrowsVisibility = event => {
const carousel = event.currentTarget
const maxRightScroll =
carousel.scrollWidth -
carousel.offsetWidth -
WEBCHAT.DEFAULTS.ELEMENT_MARGIN_RIGHT
setLeftArrow(carousel.scrollLeft !== 0)
setRightArrow(carousel.scrollLeft < maxRightScroll)
}
const getArrows = () => {
const scrollBy =
WEBCHAT.DEFAULTS.ELEMENT_WIDTH + WEBCHAT.DEFAULTS.ELEMENT_MARGIN_RIGHT
return (
<>
{hasLeftArrow &&
(CustomCarouselLeftArrow ? (
<CustomCarouselLeftArrow scrollCarouselBy={scrollCarouselBy} />
) : (
<StyledArrowContainer
left={0}
arrow={'right'}
justifyContent={'flex-start'}
onClick={() => scrollCarouselBy(-scrollBy)}
>
<StyledArrow src={resolveImage(LeftArrow)} />
</StyledArrowContainer>
))}
{hasRightArrow &&
(CustomCarouselRightArrow ? (
<CustomCarouselRightArrow scrollCarouselBy={scrollCarouselBy} />
) : (
<StyledArrowContainer
right={0}
arrow={'left'}
justifyContent={'flex-end'}
onClick={() => scrollCarouselBy(scrollBy)}
>
<StyledArrow src={resolveImage(RightArrow)} />
</StyledArrowContainer>
))}
</>
)
}
useEffect(() => {
const carousel = carouselRef.current
if (carousel && carousel.addEventListener) {
carousel.addEventListener('scroll', setArrowsVisibility, false)
} else if (carousel && carousel.attachEvent) {
carousel.attachEvent('scroll', setArrowsVisibility)
}
}, [carouselRef.current])
const carouselProps = {
...props,
children: ButtonsDisabler.updateChildrenButtons(props.children),
}
if (isBrowser()) {
content = (
<StyledScrollbar
scrollbar={scrollbarOptions}
autoHide={scrollbarOptions.autoHide}
>
<StyledCarousel
ref={carouselRef}
carouselArrowsEnabled={carouselArrowsEnabled}
>
<StyledItems>{carouselProps.children}</StyledItems>
{carouselArrowsEnabled && getArrows()}
</StyledCarousel>
</StyledScrollbar>
)
}
return (
<Message
style={{ width: '85%', padding: 0, backgroundColor: COLORS.TRANSPARENT }}
blob={false}
json={serialize(carouselProps)}
type={INPUT.CAROUSEL}
{...carouselProps}
>
{content}
</Message>
)
}
Carousel.serialize = serialize