Apologies if a similar question has been asked before.
My issue is that when using Embla Carousel, and implementing a color picker (on it's own or wrapped in a div) , dragging the color picker around doesn't work as expected as it drags the carousel as well.
I have implemented a simple code sandbox for your reference.
In my own code, I have tried wrapping the <ChromePicker />
in a div (applied padding on the div). and implementing onPointerDown, onPointerMove and onPointerUp to stopPropagation and preventDefault.
So this works only on the div, where if i try to drag inside the div (on the padded area), it doesn't drag the carousel. But dragging the <ChromePicker />
around still makes the carousel move.
Appreciate the guide @Phil, apologies for not including the code. Below is from the sandbox
index.tsx
- Default Embla Carousel with a slight change to pass in <ChromePicker />
import React, { useState } from 'react'
import ReactDOM from 'react-dom/client'
import EmblaCarousel from './EmblaCarousel'
import { EmblaOptionsType } from 'embla-carousel'
import Header from './Header'
import Footer from './Footer'
import '../css/base.css'
import '../css/sandbox.css'
import '../css/embla.css'
import { ChromePicker } from 'react-color'
const OPTIONS: EmblaOptionsType = {}
const App: React.FC = () => {
const [hex, setHex] = useState<string>('#000000')
const SLIDES = [
// Added ChromePicker here
<ChromePicker color={hex} onChange={(color) => setHex(color.hex)} />,
2,
3,
4,
5
]
return (
<>
<Header />
<EmblaCarousel slides={SLIDES} options={OPTIONS} />
<Footer />
</>
)
}
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<App />
</React.StrictMode>
)
EmblaCarousel.tsx
- Default Embla Carousel with slight change to .map
function to show <ChromePicker/ >
import React from 'react'
import { EmblaOptionsType } from 'embla-carousel'
import useEmblaCarousel from 'embla-carousel-react'
import AutoHeight from 'embla-carousel-auto-height'
import {
NextButton,
PrevButton,
usePrevNextButtons
} from './EmblaCarouselArrowButtons'
import { DotButton, useDotButton } from './EmblaCarouselDotButton'
type PropType = {
// Changed this to any[] for now to allow passing in ChromePicker
slides: any[]
options?: EmblaOptionsType
}
const EmblaCarousel: React.FC<PropType> = (props) => {
const { slides, options } = props
const [emblaRef, emblaApi] = useEmblaCarousel(options, [AutoHeight()])
const { selectedIndex, scrollSnaps, onDotButtonClick } =
useDotButton(emblaApi)
const {
prevBtnDisabled,
nextBtnDisabled,
onPrevButtonClick,
onNextButtonClick
} = usePrevNextButtons(emblaApi)
return (
<div className="embla">
<div className="embla__viewport" ref={emblaRef}>
<div className="embla__container">
{slides.map((slide, index) => (
<div className="embla__slide" key={index}>
{/* Edited to show slide instead of generating index as per default of Embla */}
<div className="embla__slide__number">{slide}</div>
</div>
))}
</div>
</div>
<div className="embla__controls">
<div className="embla__buttons">
<PrevButton onClick={onPrevButtonClick} disabled={prevBtnDisabled} />
<NextButton onClick={onNextButtonClick} disabled={nextBtnDisabled} />
</div>
<div className="embla__dots">
{scrollSnaps.map((_, index) => (
<DotButton
key={index}
onClick={() => onDotButtonClick(index)}
className={'embla__dot'.concat(
index === selectedIndex ? ' embla__dot--selected' : ''
)}
/>
))}
</div>
</div>
</div>
)
}
export default EmblaCarousel
Edited Thank you @Kostas Minaidis for the solution!
Solution added watchDrag
in OPTIONS
- index.tsx
import React, { useState } from 'react'
import ReactDOM from 'react-dom/client'
import EmblaCarousel from './EmblaCarousel'
import { EmblaOptionsType } from 'embla-carousel'
import Header from './Header'
import Footer from './Footer'
import '../css/base.css'
import '../css/sandbox.css'
import '../css/embla.css'
import { ChromePicker } from 'react-color'
const App: React.FC = () => {
const [hex, setHex] = useState<string>('#000000')
const SLIDES = [
// Added ChromePicker here
<ChromePicker color={hex} onChange={(color) => setHex(color.hex)} />,
2,
3,
4,
5
]
// Solution from @Kostas Minaidis works perfectly!
const OPTIONS: EmblaOptionsType = {
watchDrag: (_, event) => {
const target = event.target as HTMLElement
if (
target.classList.contains('chrome-picker') ||
target.closest('.chrome-picker')
) {
return false
}
return true
}
}
return (
<>
<Header />
<EmblaCarousel slides={SLIDES} options={OPTIONS} />
<Footer />
</>
)
}
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<App />
</React.StrictMode>
)
You can utilize the following configuration options, and specifically the watchdrag option as provided by EmblaCarousel
and enable dragging when the dragged target is the container div (return true) or disable dragging otherwise (anywhere inside the color picker).
const OPTIONS: EmblaOptionsType = {
watchDrag: (_, event) => {
if (event.target.classList.contains('embla__slide__number')) {
return true; // <= Will enabled dragging
}
return false; // <= Will disable dragging
}
}