UNPKG

@splidejs/splide

Version:

Splide is a lightweight, flexible and accessible slider/carousel. No dependencies, no Lighthouse errors.

151 lines (132 loc) 4.14 kB
import { EVENT_REFRESH, EVENT_RESIZE, EVENT_UPDATED } from '../../constants/events'; import { LOOP } from '../../constants/types'; import { EventInterface } from '../../constructors'; import { Splide } from '../../core/Splide/Splide'; import { BaseComponent, Components, Options } from '../../types'; import { addClass, append, before, ceil, empty, pad, push, rect, remove } from '../../utils'; /** * The interface for the Clone component. * * @since 3.0.0 */ export interface ClonesComponent extends BaseComponent { } /** * The multiplier to determine the number of clones. * * @since 4.0.0 */ export const MULTIPLIER = 2; /** * The component that generates clones for the loop slider. * * @since 3.0.0 * * @param Splide - A Splide instance. * @param Components - A collection of components. * @param options - Options. * * @return A Clones component object. */ export function Clones( Splide: Splide, Components: Components, options: Options ): ClonesComponent { const { on, emit } = EventInterface( Splide ); const { Elements, Slides } = Components; const { resolve } = Components.Direction; /** * Stores all cloned elements. */ const clones: HTMLElement[] = []; /** * Keeps the current number of clones. */ let cloneCount: number; /** * Called when the component is mounted. */ function mount(): void { init(); on( EVENT_REFRESH, destroy ); on( EVENT_REFRESH, init ); on( [ EVENT_UPDATED, EVENT_RESIZE ], observe ); } /** * Removes all clones if available, and generates new clones. */ function init(): void { if ( ( cloneCount = computeCloneCount() ) ) { generate( cloneCount ); emit( EVENT_RESIZE ); } } /** * Destroys clones. */ function destroy(): void { remove( clones ); empty( clones ); } /** * Observes the required clone count and refreshes the slider if necessary. */ function observe(): void { if ( cloneCount < computeCloneCount() ) { emit( EVENT_REFRESH ); } } /** * Generates the specified number of clones. * * @param count - The number of clones to generate for each side. */ function generate( count: number ): void { const slides = Slides.get().slice(); const { length } = slides; if ( length ) { while ( slides.length < count ) { push( slides, slides ); } push( slides.slice( -count ), slides.slice( 0, count ) ).forEach( ( Slide, index ) => { const isHead = index < count; const clone = cloneDeep( Slide.slide, index ); isHead ? before( clone, slides[ 0 ].slide ) : append( Elements.list, clone ); push( clones, clone ); Slides.register( clone, index - count + ( isHead ? 0 : length ), Slide.index ); } ); } } /** * Deeply clones the provided element with removing the ID attribute. * * @param elm - An element to clone. * @param index - An index of the clone. * * @return A cloned element. */ function cloneDeep( elm: HTMLElement, index: number ): HTMLElement { const clone = elm.cloneNode( true ) as HTMLElement; addClass( clone, options.classes.clone ); clone.id = `${ Splide.root.id }-clone${ pad( index + 1 ) }`; return clone; } /** * Returns the number of elements to generate. * This always returns 0 if the slider type is not `'loop'`. * * @return The number of clones. */ function computeCloneCount(): number { let { clones } = options; if ( ! Splide.is( LOOP ) ) { clones = 0; } else if ( ! clones ) { const fixedSize = options[ resolve( 'fixedWidth' ) ] && Components.Layout.slideSize( 0 ); const fixedCount = fixedSize && ceil( rect( Elements.track )[ resolve( 'width' ) ] / fixedSize ); clones = fixedCount || ( options[ resolve( 'autoWidth' ) ] && Splide.length ) || options.perPage * MULTIPLIER; } return clones; } return { mount, destroy, }; }