Clones.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import { EVENT_REFRESH, EVENT_RESIZE, EVENT_UPDATED } from '../../constants/events';
  2. import { LOOP } from '../../constants/types';
  3. import { EventInterface } from '../../constructors';
  4. import { Splide } from '../../core/Splide/Splide';
  5. import { BaseComponent, Components, Options } from '../../types';
  6. import { addClass, append, before, ceil, empty, push, rect, remove } from '../../utils';
  7. import { pad } from '../../utils/string';
  8. /**
  9. * The interface for the Clone component.
  10. *
  11. * @since 3.0.0
  12. */
  13. export interface CloneComponent extends BaseComponent {
  14. }
  15. /**
  16. * The component that generates clones for the loop slider.
  17. *
  18. * @since 3.0.0
  19. *
  20. * @param Splide - A Splide instance.
  21. * @param Components - A collection of components.
  22. * @param options - Options.
  23. *
  24. * @return A Clones component object.
  25. */
  26. export function Clones( Splide: Splide, Components: Components, options: Options ): CloneComponent {
  27. const { on, emit } = EventInterface( Splide );
  28. const { Elements, Slides } = Components;
  29. const { resolve } = Components.Direction;
  30. const clones: HTMLElement[] = [];
  31. /**
  32. * Keeps the current number of clones.
  33. */
  34. let cloneCount: number;
  35. /**
  36. * The index used for generating IDs.
  37. */
  38. let cloneIndex: number;
  39. /**
  40. * Called when the component is mounted.
  41. */
  42. function mount(): void {
  43. init();
  44. on( EVENT_REFRESH, refresh );
  45. on( [ EVENT_UPDATED, EVENT_RESIZE ], observe );
  46. }
  47. /**
  48. * Removes all clones if available, and generates new clones.
  49. */
  50. function init(): void {
  51. if ( ( cloneCount = computeCloneCount() ) ) {
  52. generate( cloneCount );
  53. }
  54. }
  55. /**
  56. * Destroys clones.
  57. */
  58. function destroy(): void {
  59. remove( clones );
  60. empty( clones );
  61. }
  62. /**
  63. * Discards all clones and regenerates them.
  64. * Must do this before the Elements component collects slide elements.
  65. */
  66. function refresh(): void {
  67. destroy();
  68. init();
  69. }
  70. /**
  71. * Observes the required clone count and refreshes the slider if necessary.
  72. */
  73. function observe(): void {
  74. if ( cloneCount !== computeCloneCount() ) {
  75. emit( EVENT_REFRESH );
  76. }
  77. }
  78. /**
  79. * Generates the specified number of clones.
  80. *
  81. * @param count - The number of clones to generate for each side.
  82. */
  83. function generate( count: number ): void {
  84. const slides = Slides.get().slice();
  85. const { length } = slides;
  86. if ( length ) {
  87. cloneIndex = 0;
  88. while ( slides.length < count ) {
  89. push( slides, slides );
  90. }
  91. slides.slice( -count ).concat( slides.slice( 0, count ) ).forEach( ( Slide, index ) => {
  92. const isHead = index < count;
  93. const clone = cloneDeep( Slide.slide );
  94. isHead ? before( clone, slides[ 0 ].slide ) : append( Elements.list, clone );
  95. push( clones, clone );
  96. Slides.register( clone, index - count + ( isHead ? 0 : length ), Slide.index );
  97. } );
  98. }
  99. }
  100. /**
  101. * Deeply clones the provided element with removing the ID attribute.
  102. *
  103. * @param elm - An element to clone.
  104. *
  105. * @return A cloned element.
  106. */
  107. function cloneDeep( elm: HTMLElement ): HTMLElement {
  108. const clone = elm.cloneNode( true ) as HTMLElement;
  109. addClass( clone, options.classes.clone );
  110. clone.id = `${ Splide.root.id }-clone${ pad( ++cloneIndex ) }`;
  111. return clone;
  112. }
  113. /**
  114. * Returns the number of elements to generate.
  115. * This always returns 0 if the slider type is not `'loop'`.
  116. *
  117. * @return The number of clones.
  118. */
  119. function computeCloneCount(): number {
  120. let { clones } = options;
  121. if ( ! Splide.is( LOOP ) ) {
  122. clones = 0;
  123. } else if ( ! clones ) {
  124. const fixedSize = options[ resolve( 'fixedWidth' ) ];
  125. const fixedCount = fixedSize && ceil( rect( Elements.track )[ resolve( 'width' ) ] / fixedSize );
  126. const baseCount = fixedCount || ( options[ resolve( 'autoWidth' ) ] && Splide.length ) || options.perPage;
  127. clones = baseCount * ( options.drag ? ( options.flickMaxPages || 1 ) + 1 : 2 );
  128. }
  129. return clones;
  130. }
  131. return {
  132. mount,
  133. destroy,
  134. };
  135. }