import { Options, Splide as SplideCore } from '@splidejs/splide'; import React, { ReactNode } from 'react'; import { EVENTS } from '../../constants/events'; import { SplideProps } from '../../types'; import { classNames, isEqualDeep, isEqualShallow, merge } from '../../utils'; import { SplideTrack } from '../SplideTrack/SplideTrack'; /** * The class for the Splide React node. * * @since 0.5.0 */ export class Splide extends React.Component { /** * The RefObject for the Splide root element. */ readonly splideRef = React.createRef(); /** * Holds the Splide instance. */ splide: SplideCore | undefined; /** * Holds current options to compare with new ones. */ private options: Options | undefined; /** * Holds the latest slides to compare with new ones. */ private slides: HTMLElement[] = []; /** * Called when the component is mounted. */ componentDidMount(): void { const { options, extensions, transition } = this.props; const { current } = this.splideRef; if ( current ) { this.splide = new SplideCore( current, options ); this.bind( this.splide ); this.splide.mount( extensions, transition ); this.options = merge( {}, options || {} ); this.slides = this.getSlides(); } } /** * Destroys the splide instance. */ componentWillUnmount(): void { if ( this.splide ) { this.splide.destroy(); this.splide = undefined; } this.options = undefined; this.slides.length = 0; } /** * Updates and/or refreshes the splide when the component is updated. */ componentDidUpdate(): void { if ( ! this.splide ) { return; } const { options } = this.props; if ( options && ! isEqualDeep( this.options, options ) ) { this.splide.options = options; this.options = merge( {}, options ); } const newSlides = this.getSlides(); if ( ! isEqualShallow( this.slides, newSlides ) ) { this.splide.refresh(); this.slides = newSlides; } } /** * Adds a splide instance to sync with. * * @param splide - A Splide instance. */ sync( splide: SplideCore ): void { this.splide?.sync( splide ); } /** * Moves the slider by the specified control pattern. * * @see Splide#go() * * @param control - A control pattern. */ go( control: number | string ): void { this.splide?.go( control ); } /** * Returns an array with slide elements. * * @return An array with slide elements. */ protected getSlides(): HTMLElement[] { if ( this.splide ) { const children = this.splide.Components.Elements?.list.children; return children && Array.prototype.slice.call( children ) || []; } return []; } /** * Binds event handlers to the splide instance. * * @param splide - A splide instance. */ protected bind( splide: SplideCore ): void { EVENTS.forEach( ( [ event, name ] ) => { const handler = this.props[ name ]; if ( typeof handler === 'function' ) { splide.on( event, ( ...args: any[] ) => { handler( splide, ...args ); } ); } } ); } /** * Omits specified keys from props. * * @param props - An object with props. * @param keys - An array with keys to omit. * * @return An object with props without specified keys. */ omit( props: SplideProps, keys: readonly K[] ): Omit { keys.forEach( key => { if ( Object.prototype.hasOwnProperty.call( props, key ) ) { delete props[ key ]; } } ); return props; } /** * Render the splide carousel elements. * * @return A root node. */ render(): ReactNode { const { className, tag: Root = 'div', hasTrack = true, children, ...props } = this.props; return ( event[ 1 ] ) ] ) } > { hasTrack ? { children } : children } ); } }