Skip to content

Commit

Permalink
Full stack/react experimental components (#1701)
Browse files Browse the repository at this point in the history
* chore(react): add experimental components directory with necessary changes

* chore(react): bump to alpha 1.0.0-alpha.0

* fix(react): allow carousel elements without buttons

* chore(react): bump react to v1.0.0-alpha.1
  • Loading branch information
vanbasten17 committed Jul 28, 2021
1 parent c7d5599 commit 5f43314
Show file tree
Hide file tree
Showing 30 changed files with 3,513 additions and 5 deletions.
61 changes: 58 additions & 3 deletions packages/botonic-react/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions packages/botonic-react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@botonic/react",
"version": "0.18.4",
"version": "1.0.0-alpha.1",
"license": "MIT",
"description": "Build Chatbots using React",
"main": "src/index.js",
Expand Down Expand Up @@ -28,12 +28,14 @@
"README.md"
],
"dependencies": {
"@botonic/core": "~0.18.2",
"@botonic/core": "alpha",
"axios": "^0.21.1",
"emoji-picker-react": "^3.2.3",
"framer-motion": "^3.1.1",
"he": "^1.2.0",
"lodash.merge": "^4.6.2",
"markdown-it": "^12.0.6",
"qrcode.react": "^1.0.1",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"react-frame-component": "^4.1.3",
Expand All @@ -42,6 +44,7 @@
"react-router-dom": "^5.2.0",
"react-textarea-autosize": "^7.1.2",
"react-use-storage": "^0.5.1",
"reconnecting-websocket": "^4.4.0",
"simplebar-react": "^2.3.3",
"styled-components": "^5.3.0",
"ua-parser-js": "^0.7.21",
Expand Down
28 changes: 28 additions & 0 deletions packages/botonic-react/src/experimental/components/audio.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { INPUT, isBrowser } from '@botonic/core'
import React from 'react'

import { ROLES } from '../../constants'
import { Message } from './message'

const serialize = audioProps => {
return { src: audioProps.src }
}

export const Audio = props => (
<Message
role={ROLES.AUDIO_MESSAGE}
json={serialize(props)}
{...props}
type={INPUT.AUDIO}
>
{isBrowser() && (
<audio style={{ maxWidth: '100%' }} id='myAudio' controls>
<source src={props.src} type='audio/mpeg' />
Your browser does not support this audio format.
</audio>
)}
{props.children}
</Message>
)

Audio.serialize = serialize
181 changes: 181 additions & 0 deletions packages/botonic-react/src/experimental/components/carousel.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
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 { ButtonsDisabler } from '../../components/buttons-disabler'
import { COLORS, WEBCHAT } from '../../constants'
import { WebchatContext } from '../../contexts'
import { resolveImage } from '../../util/environment'
import { StyledScrollbar } from '../../webchat/components/styled-scrollbar'
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
Loading

0 comments on commit 5f43314

Please sign in to comment.