Skip to content

Commit

Permalink
Merge pull request #95 from vtexdocs/feat/faq-page-modules-chip
Browse files Browse the repository at this point in the history
Feat/faq page modules chip
  • Loading branch information
PedroAntunesCosta authored Oct 17, 2024
2 parents d00bee3 + a05aaaa commit 2cedf4e
Show file tree
Hide file tree
Showing 10 changed files with 403 additions and 67 deletions.
183 changes: 183 additions & 0 deletions src/components/chip-filter/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import styles from './styles'

import { Box, Flex, Button, Text } from '@vtex/brand-ui'

import { useIntl } from 'react-intl'

import { useRef, useState } from 'react'

interface ChipFilterProps {
filters: string[]
categories: string[]
applyCategory: (option: string) => void
resetFilters: () => void
removeCategory: (option: string) => void
getCategoryAmount: (category: string) => number
}

export default function ChipFilter({
filters,
categories,
applyCategory,
resetFilters,
removeCategory,
getCategoryAmount,
}: ChipFilterProps) {
const intl = useIntl()

const containerRef = useRef<HTMLDivElement | null>(null)

const [shouldDisplayArrows, setShouldDisplayArrows] = useState<{
left: boolean
right: boolean
}>({ left: false, right: true })

function handleLeftArrowClick() {
if (containerRef.current) {
containerRef.current.scrollLeft -= 180
}
}

function handleRightArrowClick() {
if (containerRef.current) {
containerRef.current.scrollLeft += 180
}
}

function isCategoryActive(category: string) {
return filters.includes(category)
}

function handleContainerScroll() {
if (containerRef.current) {
const offsetWidth = 20

const isLeftmostScroll: boolean = containerRef.current.scrollLeft === 0
const isRightmostScroll: boolean =
containerRef.current.scrollLeft +
containerRef.current.clientWidth +
offsetWidth >=
containerRef.current.scrollWidth

if (isLeftmostScroll) {
return setShouldDisplayArrows({ ...shouldDisplayArrows, left: false })
}
if (isRightmostScroll) {
return setShouldDisplayArrows({ ...shouldDisplayArrows, right: false })
}

return setShouldDisplayArrows({ right: true, left: true })
}
}

return (
<Flex style={styles.chipButtonWrapper}>
<Box style={styles.leftArrowContainer}>
{shouldDisplayArrows.left && (
<Button
variant="tertiary"
size="small"
sx={styles.arrowButton}
onClick={handleLeftArrowClick}
>
{`<`}
</Button>
)}
<Box style={styles.leftArrowBlur}></Box>
</Box>
<Box
style={styles.chipsContainer}
ref={containerRef}
onScroll={handleContainerScroll}
>
<Box style={styles.optionsContainer}>
<MainChip
value={intl.formatMessage({ id: 'chip.all_results' })}
isActive={!filters.length}
applyCategory={() => resetFilters()}
/>
{categories.map((filter: string) => (
<Chip
removeCategory={() => removeCategory(filter)}
value={filter}
categoryAmount={getCategoryAmount(filter)}
applyCategory={() => applyCategory(filter)}
isActive={isCategoryActive(filter)}
/>
))}
</Box>
</Box>
<Box style={styles.rightArrowContainer}>
{shouldDisplayArrows.right && (
<>
<Button
variant="tertiary"
size="small"
sx={styles.arrowButton}
onClick={handleRightArrowClick}
>{`>`}</Button>{' '}
<Box style={styles.rightArrowBlur}></Box>
</>
)}
</Box>
</Flex>
)
}

interface ChipProps {
value: string
isActive: boolean
applyCategory: () => void
categoryAmount: number
removeCategory: () => void
}

function Chip({
value,
isActive,
applyCategory,
categoryAmount,
removeCategory,
}: ChipProps) {
function handleChipClick(active: boolean) {
if (active) {
return removeCategory()
}
applyCategory()
}

return (
<Button
variant="tertiary"
size="small"
type="button"
sx={isActive ? styles.activeChip : styles.inactiveChip}
onClick={() => handleChipClick(isActive)}
>
{value}
{isActive && categoryAmount !== undefined && (
<Text style={styles.articlesAmount}>{categoryAmount}</Text>
)}
</Button>
)
}

interface MainChipProps {
value: string
isActive: boolean
applyCategory: () => void
}

function MainChip({ value, isActive, applyCategory }: MainChipProps) {
return (
<Button
variant="tertiary"
size="small"
type="button"
sx={isActive ? styles.activeChip : styles.inactiveChip}
onClick={() => applyCategory()}
>
{value}
</Button>
)
}
130 changes: 130 additions & 0 deletions src/components/chip-filter/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { SxStyleProp } from '@vtex/brand-ui'

const chipButtonWrapper: SxStyleProp = {
display: 'flex',
userSelect: 'none',
maxWidth: '800px',
width: '70dvw',
minWidth: '350px',
}

const chipsContainer: SxStyleProp = {
margin: '0 4px',
padding: '0 16px',
scrollbarWidth: 'none',
'-ms-overflow-style': 'none',
overflow: 'scroll',
scrollBehavior: 'smooth',
'& ::-webkit-scrollbar': {
display: 'none',
},
display: 'flex',
}

const optionsContainer: SxStyleProp = {
display: 'flex',
gap: '8px',
}

const chip: SxStyleProp = {
fontSize: 'clamp(0.8rem, 2.5vw, 1rem)',
textWrap: 'nowrap',
padding: '4px 8px',
borderRadius: '16px',
textTransform: 'none',
textDecoration: 'none',
transition: 'all .3s ease-out',
}

const activeChip: SxStyleProp = {
...chip,
backgroundColor: '#E7E9EE',
border: '2px solid #E7E9EE',
color: '#4A596B',
':hover': {
backgroundColor: '#CCCED8',
border: '2px solid #CCCED8',
textDecoration: 'none',
},
}

const inactiveChip: SxStyleProp = {
...chip,
backgroundColor: 'transparent',
border: '2px solid #5E6E84',
color: '#5E6E84',
':hover': {
border: '2px solid #A1A8B3',
color: '#A1A8B3',
textDecoration: 'none',
},
}

const arrowButton: SxStyleProp = {
backgroundColor: 'transparent',
border: 'none',
borderRadius: '50%',
fontWeight: '600',
fontSize: '16px',
padding: '0 8px',
textDecoration: 'none',
':hover': {
textDecoration: 'none',
backgroundColor: 'transparent',
},
}

const rightArrowBlur: SxStyleProp = {
maxWidth: '40px',
minWidth: '20px',
width: '5vw',
background:
'linear-gradient(to left, rgba(255,255,255,80%) 30%, rgba(255, 255, 255, 1%))',
}

const leftArrowBlur: SxStyleProp = {
maxWidth: '40px',
minWidth: '20px',
width: '5vw',
background:
'linear-gradient(to right, rgba(255,255,255,80%) 30%, rgba(255, 255, 255, 1%))',
}

const leftArrowContainer: SxStyleProp = {
position: 'relative',
display: 'flex',
flexShrink: '0',
left: 'clamp(20px, 2.5vw, 30px)',
zIndex: '2000',
}

const rightArrowContainer: SxStyleProp = {
position: 'relative',
display: 'flex',
flexDirection: 'row-reverse',
flexShrink: '0',
right: 'clamp(20px, 2.5vw, 30px)',
zIndex: '2000',
}

const articlesAmount: SxStyleProp = {
backgroundColor: '#fff',
padding: '2px 8px',
margin: '0 2px 0 8px',
borderRadius: '8px',
fontSize: '0.8rem',
}

export default {
leftArrowBlur,
rightArrowBlur,
inactiveChip,
activeChip,
arrowButton,
chipsContainer,
optionsContainer,
chipButtonWrapper,
leftArrowContainer,
rightArrowContainer,
articlesAmount,
}
Loading

0 comments on commit 2cedf4e

Please sign in to comment.