slide.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /**
  2. * The sub component for handling each slide.
  3. *
  4. * @author Naotoshi Fujita
  5. * @copyright Naotoshi Fujita. All rights reserved.
  6. */
  7. import {
  8. child,
  9. addClass,
  10. removeClass,
  11. hasClass,
  12. getAttribute,
  13. setAttribute,
  14. removeAttribute,
  15. applyStyle,
  16. getRect,
  17. } from '../../utils/dom';
  18. import { FADE, SLIDE } from '../../constants/types';
  19. import { STATUS_CLASSES } from '../../constants/classes';
  20. import { values } from "../../utils/object";
  21. import { pad } from "../../utils/utils";
  22. import { TTB } from "../../constants/directions";
  23. /**
  24. * Events for restoring original styles.
  25. *
  26. * @type {string}
  27. */
  28. const STYLE_RESTORE_EVENTS = 'update.slide';
  29. /**
  30. * The sub component for handling each slide.
  31. *
  32. * @param {Splide} Splide - A Splide instance.
  33. * @param {number} index - An unique slide index.
  34. * @param {number} realIndex - Clones should pass a real slide index.
  35. * @param {Element} slide - A slide element.
  36. *
  37. * @return {Object} - The sub component object.
  38. */
  39. export default ( Splide, index, realIndex, slide ) => {
  40. /**
  41. * Whether to update "is-active" class before or after transition.
  42. *
  43. * @type {boolean}
  44. */
  45. const updateOnMove = Splide.options.updateOnMove;
  46. /**
  47. * Events when the slide status is updated.
  48. * Append a namespace to remove listeners later.
  49. *
  50. * @type {string}
  51. */
  52. const STATUS_UPDATE_EVENTS = 'ready.slide updated.slide resized.slide moved.slide'
  53. + ( updateOnMove ? ' move.slide' : '' );
  54. /**
  55. * Slide sub component object.
  56. *
  57. * @type {Object}
  58. */
  59. const Slide = {
  60. /**
  61. * Slide element.
  62. *
  63. * @type {Element}
  64. */
  65. slide,
  66. /**
  67. * Slide index.
  68. *
  69. * @type {number}
  70. */
  71. index,
  72. /**
  73. * Real index for clones.
  74. *
  75. * @type {number}
  76. */
  77. realIndex,
  78. /**
  79. * Container element if available.
  80. *
  81. * @type {Element|undefined}
  82. */
  83. container: child( slide, Splide.classes.container ),
  84. /**
  85. * Whether this is a cloned slide or not.
  86. *
  87. * @type {boolean}
  88. */
  89. isClone: realIndex > -1,
  90. /**
  91. * Called when the component is mounted.
  92. */
  93. mount() {
  94. if ( ! this.isClone ) {
  95. slide.id = `${ Splide.root.id }-slide${ pad( index + 1 ) }`;
  96. }
  97. Splide
  98. .on( STATUS_UPDATE_EVENTS, () => this.update() )
  99. .on( STYLE_RESTORE_EVENTS, restoreStyles )
  100. .on( 'click', () => Splide.emit( 'click', this ), slide );
  101. /*
  102. * Add "is-active" class to a clone element temporarily
  103. * and it will be removed on "moved" event.
  104. */
  105. if ( updateOnMove ) {
  106. Splide.on( 'move.slide', newIndex => {
  107. if ( newIndex === realIndex ) {
  108. update( true, false );
  109. }
  110. } );
  111. }
  112. // Make sure the slide is shown.
  113. applyStyle( slide, { display: '' } );
  114. // Hold the original styles.
  115. this.styles = getAttribute( slide, 'style' ) || '';
  116. },
  117. /**
  118. * Destroy.
  119. */
  120. destroy() {
  121. Splide.off( STATUS_UPDATE_EVENTS ).off( STYLE_RESTORE_EVENTS ).off( 'click', slide );
  122. removeClass( slide, values( STATUS_CLASSES ) );
  123. restoreStyles();
  124. removeAttribute( this.container, 'style' );
  125. },
  126. /**
  127. * Update active and visible status.
  128. */
  129. update() {
  130. update( this.isActive(), false );
  131. update( this.isVisible(), true );
  132. },
  133. /**
  134. * Check whether this slide is active or not.
  135. *
  136. * @return {boolean} - True if the slide is active or false if not.
  137. */
  138. isActive() {
  139. return Splide.index === index;
  140. },
  141. /**
  142. * Check whether this slide is visible in the viewport or not.
  143. *
  144. * @return {boolean} - True if the slide is visible or false if not.
  145. */
  146. isVisible() {
  147. const active = this.isActive();
  148. if ( Splide.is( FADE ) || active ) {
  149. return active;
  150. }
  151. const { ceil } = Math;
  152. const trackRect = getRect( Splide.Components.Elements.track );
  153. const slideRect = getRect( slide );
  154. if ( Splide.options.direction === TTB ) {
  155. return trackRect.top <= slideRect.top && slideRect.bottom <= ceil( trackRect.bottom );
  156. }
  157. return trackRect.left <= slideRect.left && slideRect.right <= ceil( trackRect.right );
  158. },
  159. /**
  160. * Calculate how far this slide is from another slide and
  161. * return true if the distance is within the given number.
  162. *
  163. * @param {number} from - Index of a target slide.
  164. * @param {number} within - True if the slide is within this number.
  165. *
  166. * @return {boolean} - True if the slide is within the number or false otherwise.
  167. */
  168. isWithin( from, within ) {
  169. let diff = Math.abs( from - index );
  170. if ( ! Splide.is( SLIDE ) && ! this.isClone ) {
  171. diff = Math.min( diff, Splide.length - diff );
  172. }
  173. return diff < within;
  174. },
  175. };
  176. /**
  177. * Update classes for activity or visibility.
  178. *
  179. * @param {boolean} active - Is active/visible or not.
  180. * @param {boolean} forVisibility - Toggle classes for activity or visibility.
  181. */
  182. function update( active, forVisibility ) {
  183. const type = forVisibility ? 'visible' : 'active';
  184. const className = STATUS_CLASSES[ type ];
  185. if ( active ) {
  186. addClass( slide, className );
  187. Splide.emit( `${ type }`, Slide );
  188. } else {
  189. if ( hasClass( slide, className ) ) {
  190. removeClass( slide, className );
  191. Splide.emit( `${ forVisibility ? 'hidden' : 'inactive' }`, Slide );
  192. }
  193. }
  194. }
  195. /**
  196. * Restore the original styles.
  197. */
  198. function restoreStyles() {
  199. setAttribute( slide, 'style', Slide.styles );
  200. }
  201. return Slide;
  202. }