I'm not very good at CSS, in fact, I barely write CSS due to the rise of libraries like MUI
. I have found myself in a weird situation, I'm trying to rewrite this sample
The ProductSlider.tsx
is the EmblaCarousel.js
in the sample project, the ProductSlider.module.css
is the embla.css
My ProductSlider looks like this:
import s from './ProductSlider.module.css'
import React, { useState, useEffect, useCallback, memo } from 'react'
import useEmblaCarousel from 'embla-carousel-react'
import Thumbnail from './Thumbnail'
import Image from 'next/image'
import { Box } from '@mui/material'
type ProductSliderProps = {
items: {
url: string
alt?: string
}[]
}
const ProductSlider = ({ items }: ProductSliderProps) => {
const [selectedIndex, setSelectedIndex] = useState(0)
const [mainViewportRef, embla] = useEmblaCarousel({ skipSnaps: false })
const [thumbViewportRef, emblaThumbs] = useEmblaCarousel({
containScroll: 'keepSnaps',
dragFree: true,
})
const onThumbClick = useCallback(
(index) => {
if (!embla || !emblaThumbs) return
if (emblaThumbs.clickAllowed()) embla.scrollTo(index)
},
[embla, emblaThumbs]
)
const onSelect = useCallback(() => {
if (!embla || !emblaThumbs) return
setSelectedIndex(embla.selectedScrollSnap())
emblaThumbs.scrollTo(embla.selectedScrollSnap())
}, [embla, emblaThumbs, setSelectedIndex])
useEffect(() => {
if (!embla) return
onSelect()
embla.on('select', onSelect)
}, [embla, onSelect])
return (
<>
<Box className={s.embla}>
<Box className={s.embla__viewport} ref={mainViewportRef}>
<Box className={s.embla__container}>
{items.map(({ url, alt }, idx) => (
<Box className={s.embla__slide} key={idx}>
<Box className={s.embla__slide__inner}>
<Image
src={url}
alt={alt}
layout="fill"
objectFit="contain"
quality="85"
/>
</Box>
</Box>
))}
</Box>
</Box>
</Box>
<Box className="embla embla--thumb">
<Box className={s.embla__viewport} ref={thumbViewportRef}>
<Box className="embla__container embla__container--thumb">
{items.map(({ url, alt }, idx) => (
<Thumbnail
onClick={() => onThumbClick(idx)}
selected={idx === selectedIndex}
imgSrc={url}
key={idx}
alt={alt}
s={s}
/>
))}
</Box>
</Box>
</Box>
</>
)
}
export default memo(ProductSlider)
As you can see I have managed to get the straightforward CSS working, the challenge is, how do I call something like this embla__container embla__container--thumb
using the named import?
or this
<div
className={`embla__slide embla__slide--thumb ${
selected ? 'is-selected' : ''
}`}
I understand in cases where I have two classes like this embla__container embla__container
I can use clsx
like this cn(s.embla__container, s.embla__container)
but in a case where I have something like this --thumb
, some_name--thumb
, some_name-is-selected
or this is-selected
I'm not sure what to do.
CodePudding user response:
Try to do this
import s from './ProductSlider.module.css'
import React, { useState, useEffect, useCallback, memo } from 'react'
import useEmblaCarousel from 'embla-carousel-react'
import Thumbnail from './Thumbnail'
import Image from 'next/image'
import { Box } from '@mui/material'
type ProductSliderProps = {
items: {
url: string
alt?: string
}[]
}
const ProductSlider = ({ items }: ProductSliderProps) => {
const [selectedIndex, setSelectedIndex] = useState(0)
const [mainViewportRef, embla] = useEmblaCarousel({ skipSnaps: false })
const [thumbViewportRef, emblaThumbs] = useEmblaCarousel({
containScroll: 'keepSnaps',
dragFree: true,
})
const onThumbClick = useCallback(
(index) => {
if (!embla || !emblaThumbs) return
if (emblaThumbs.clickAllowed()) embla.scrollTo(index)
},
[embla, emblaThumbs]
)
const onSelect = useCallback(() => {
if (!embla || !emblaThumbs) return
setSelectedIndex(embla.selectedScrollSnap())
emblaThumbs.scrollTo(embla.selectedScrollSnap())
}, [embla, emblaThumbs, setSelectedIndex])
useEffect(() => {
if (!embla) return
onSelect()
embla.on('select', onSelect)
}, [embla, onSelect])
return (
<>
<Box className={s.embla}>
<Box className={s.embla__viewport} ref={mainViewportRef}>
<Box className={s.embla__container}>
{items.map(({ url, alt }, idx) => (
<Box className={s.embla__slide} key={idx}>
<Box className={s.embla__slide__inner}>
<Image
src={url}
alt={alt}
layout="fill"
objectFit="contain"
quality="85"
/>
</Box>
</Box>
))}
</Box>
</Box>
</Box>
<Box className="embla embla--thumb">
<Box className={s.embla__viewport} ref={thumbViewportRef}>
<Box className="embla__container embla__container--thumb">
{items.map(({ url, alt }, idx) => (
<Thumbnail
onClick={() => onThumbClick(idx)}
selected={idx === selectedIndex}
imgSrc={url}
key={idx}
alt={alt}
s={s}
/>
))}
</Box>
</Box>
</Box>
<style>
{
`
// Your Css Here
`
}
</style>
</>
)
}
export default memo(ProductSlider)
CodePudding user response:
Based on this answer I ended up doing this and it worked:
import s from './ProductSlider.module.css'
import React, { useState, useEffect, useCallback, memo } from 'react'
import useEmblaCarousel from 'embla-carousel-react'
import Thumbnail from './Thumbnail'
import Image from 'next/image'
import { Box } from '@mui/material'
type ProductSliderProps = {
items: {
url: string
alt?: string
}[]
}
const ProductSlider = ({ items }: ProductSliderProps) => {
const [selectedIndex, setSelectedIndex] = useState(0)
const [mainViewportRef, embla] = useEmblaCarousel({ skipSnaps: false })
const [thumbViewportRef, emblaThumbs] = useEmblaCarousel({
containScroll: 'keepSnaps',
dragFree: true,
})
const onThumbClick = useCallback(
(index) => {
if (!embla || !emblaThumbs) return
if (emblaThumbs.clickAllowed()) embla.scrollTo(index)
},
[embla, emblaThumbs]
)
const onSelect = useCallback(() => {
if (!embla || !emblaThumbs) return
setSelectedIndex(embla.selectedScrollSnap())
emblaThumbs.scrollTo(embla.selectedScrollSnap())
}, [embla, emblaThumbs, setSelectedIndex])
useEffect(() => {
if (!embla) return
onSelect()
embla.on('select', onSelect)
}, [embla, onSelect])
return (
<>
<Box className={s.embla}>
<Box className={s.embla__viewport} ref={mainViewportRef}>
<Box className={s.embla__container}>
{items.map(({ url, alt }, idx) => (
<Box className={s.embla__slide} key={idx}>
<Box className={s.embla__slide__inner}>
<Image
src={url}
alt={alt}
layout="fill"
objectFit="contain"
quality="85"
/>
</Box>
</Box>
))}
</Box>
</Box>
</Box>
<Box className={`${s.embla} ${s['embla--thumb']}`}>
<Box className={s.embla__viewport} ref={thumbViewportRef}>
<Box
className={`${s.embla__container} ${s['embla__container--thumb']}`}
>
{items.map(({ url, alt }, idx) => (
<Thumbnail
onClick={() => onThumbClick(idx)}
selected={idx === selectedIndex}
imgSrc={url}
key={idx}
alt={alt}
s={s}
/>
))}
</Box>
</Box>
</Box>
</>
)
}
export default memo(ProductSlider)