index.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. /**
  2. * The component for controlling the track.
  3. *
  4. * @author Naotoshi Fujita
  5. * @copyright Naotoshi Fujita. All rights reserved.
  6. */
  7. import { LOOP } from "../../constants/types";
  8. import { RTL } from '../../constants/directions';
  9. import { between } from '../../utils/utils';
  10. const { floor } = Math;
  11. /**
  12. * The component for controlling the track.
  13. *
  14. * @param {Splide} Splide - A Splide instance.
  15. * @param {Object} Components - An object containing components.
  16. *
  17. * @return {Object} - The component object.
  18. */
  19. export default ( Splide, Components ) => {
  20. /**
  21. * Store current options.
  22. *
  23. * @type {Object}
  24. */
  25. let options;
  26. /**
  27. * True if the slide is LOOP mode.
  28. *
  29. * @type {boolean}
  30. */
  31. let isLoop;
  32. /**
  33. * Controller component object.
  34. *
  35. * @type {Object}
  36. */
  37. const Controller = {
  38. /**
  39. * Called when the component is mounted.
  40. */
  41. mount() {
  42. options = Splide.options;
  43. isLoop = Splide.is( LOOP );
  44. bind();
  45. },
  46. /**
  47. * Make track run by the given control.
  48. * - "+{i}" : Increment the slide index by i.
  49. * - "-{i}" : Decrement the slide index by i.
  50. * - "{i}" : Go to the slide whose index is i.
  51. * - ">" : Go to next page.
  52. * - "<" : Go to prev page.
  53. * - ">{i}" : Go to page i.
  54. *
  55. * @param {string|number} control - A control pattern.
  56. * @param {boolean} silently - Go to the destination without event emission.
  57. */
  58. go( control, silently ) {
  59. const destIndex = this.trim( this.parse( control ) );
  60. Components.Track.go( destIndex, this.rewind( destIndex ), silently );
  61. },
  62. /**
  63. * Parse the given control and return the destination index for the track.
  64. *
  65. * @param {string} control - A control target pattern.
  66. *
  67. * @return {string|number} - A parsed target.
  68. */
  69. parse( control ) {
  70. let index = Splide.index;
  71. const matches = String( control ).match( /([+\-<>]+)(\d+)?/ );
  72. const indicator = matches ? matches[1] : '';
  73. const number = matches ? parseInt( matches[2] ) : 0;
  74. switch ( indicator ) {
  75. case '+':
  76. index += number || 1;
  77. break;
  78. case '-':
  79. index -= number || 1;
  80. break;
  81. case '>':
  82. case '<':
  83. index = parsePage( number, index, indicator === '<' );
  84. break;
  85. default:
  86. index = parseInt( control );
  87. }
  88. return index;
  89. },
  90. /**
  91. * Compute index from the given page number.
  92. *
  93. * @param {number} page - Page number.
  94. *
  95. * @return {number} - A computed page number.
  96. */
  97. toIndex( page ) {
  98. if ( hasFocus() ) {
  99. return page;
  100. }
  101. const length = Splide.length;
  102. const perPage = options.perPage;
  103. let index = page * perPage;
  104. index = index - ( this.pageLength * perPage - length ) * floor( index / length );
  105. // Adjustment for the last page.
  106. if ( length - perPage <= index && index < length ) {
  107. index = length - perPage;
  108. }
  109. return index;
  110. },
  111. /**
  112. * Compute page number from the given slide index.
  113. *
  114. * @param index - Slide index.
  115. *
  116. * @return {number} - A computed page number.
  117. */
  118. toPage( index ) {
  119. if ( hasFocus() ) {
  120. return index;
  121. }
  122. const length = Splide.length;
  123. const perPage = options.perPage;
  124. // Make the last "perPage" number of slides belong to the last page.
  125. if ( length - perPage <= index && index < length ) {
  126. return floor( ( length - 1 ) / perPage );
  127. }
  128. return floor( index / perPage );
  129. },
  130. /**
  131. * Trim the given index according to the current mode.
  132. * Index being returned could be less than 0 or greater than the length in Loop mode.
  133. *
  134. * @param {number} index - An index being trimmed.
  135. *
  136. * @return {number} - A trimmed index.
  137. */
  138. trim( index ) {
  139. if ( ! isLoop ) {
  140. index = options.rewind ? this.rewind( index ) : between( index, 0, this.edgeIndex );
  141. }
  142. return index;
  143. },
  144. /**
  145. * Rewind the given index if it's out of range.
  146. *
  147. * @param {number} index - An index.
  148. *
  149. * @return {number} - A rewound index.
  150. */
  151. rewind( index ) {
  152. const edge = this.edgeIndex;
  153. if ( isLoop ) {
  154. while( index > edge ) {
  155. index -= edge + 1;
  156. }
  157. while( index < 0 ) {
  158. index += edge + 1;
  159. }
  160. } else {
  161. if ( index > edge ) {
  162. index = 0;
  163. } else if ( index < 0 ) {
  164. index = edge;
  165. }
  166. }
  167. return index;
  168. },
  169. /**
  170. * Check if the direction is "rtl" or not.
  171. *
  172. * @return {boolean} - True if "rtl" or false if not.
  173. */
  174. isRtl() {
  175. return options.direction === RTL;
  176. },
  177. /**
  178. * Return the page length.
  179. *
  180. * @return {number} - Max page number.
  181. */
  182. get pageLength() {
  183. const length = Splide.length;
  184. return hasFocus() ? length : Math.ceil( length / options.perPage );
  185. },
  186. /**
  187. * Return the edge index.
  188. *
  189. * @return {number} - Edge index.
  190. */
  191. get edgeIndex() {
  192. const length = Splide.length;
  193. if ( ! length ) {
  194. return 0;
  195. }
  196. if ( hasFocus() || options.isNavigation || isLoop ) {
  197. return length - 1;
  198. }
  199. return length - options.perPage;
  200. },
  201. /**
  202. * Return the index of the previous slide.
  203. *
  204. * @return {number} - The index of the previous slide if available. -1 otherwise.
  205. */
  206. get prevIndex() {
  207. let prev = Splide.index - 1;
  208. if ( isLoop || options.rewind ) {
  209. prev = this.rewind( prev );
  210. }
  211. return prev > -1 ? prev : -1;
  212. },
  213. /**
  214. * Return the index of the next slide.
  215. *
  216. * @return {number} - The index of the next slide if available. -1 otherwise.
  217. */
  218. get nextIndex() {
  219. let next = Splide.index + 1;
  220. if ( isLoop || options.rewind ) {
  221. next = this.rewind( next );
  222. }
  223. return ( Splide.index < next && next <= this.edgeIndex ) || next === 0 ? next : -1;
  224. },
  225. };
  226. /**
  227. * Listen to some events.
  228. */
  229. function bind() {
  230. Splide
  231. .on( 'move', newIndex => { Splide.index = newIndex } )
  232. .on( 'updated refresh', newOptions => {
  233. options = newOptions || options;
  234. Splide.index = between( Splide.index, 0, Controller.edgeIndex );
  235. } );
  236. }
  237. /**
  238. * Verify if the focus option is available or not.
  239. *
  240. * @return {boolean} - True if a slider has the focus option.
  241. */
  242. function hasFocus() {
  243. return options.focus !== false;
  244. }
  245. /**
  246. * Return the next or previous page index computed by the page number and current index.
  247. *
  248. * @param {number} number - Specify the page number.
  249. * @param {number} index - Current index.
  250. * @param {boolean} prev - Prev or next.
  251. *
  252. * @return {number} - Slide index.
  253. */
  254. function parsePage( number, index, prev ) {
  255. if ( number > -1 ) {
  256. return Controller.toIndex( number );
  257. }
  258. const perMove = options.perMove;
  259. const sign = prev ? -1 : 1;
  260. if ( perMove ) {
  261. return index + perMove * sign;
  262. }
  263. return Controller.toIndex( Controller.toPage( index ) + sign );
  264. }
  265. return Controller;
  266. }