Arrows.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. import { ALL_ATTRIBUTES, ARIA_CONTROLS, ARIA_LABEL } from '../../constants/attributes';
  2. import { CLASS_ARROWS } from '../../constants/classes';
  3. import {
  4. EVENT_ARROWS_MOUNTED,
  5. EVENT_ARROWS_UPDATED,
  6. EVENT_MOVED,
  7. EVENT_REFRESH,
  8. EVENT_SCROLLED,
  9. EVENT_UPDATED,
  10. } from '../../constants/events';
  11. import { EventInterface } from '../../constructors';
  12. import { Splide } from '../../core/Splide/Splide';
  13. import { BaseComponent, Components, Options } from '../../types';
  14. import {
  15. addClass,
  16. append,
  17. apply,
  18. assign,
  19. before,
  20. create,
  21. display,
  22. parseHtml,
  23. remove,
  24. removeAttribute,
  25. removeClass,
  26. setAttribute,
  27. } from '../../utils';
  28. import { PATH, SIZE, XML_NAME_SPACE } from './path';
  29. /**
  30. * The interface for the Arrows component.
  31. *
  32. * @since 3.0.0
  33. */
  34. export interface ArrowsComponent extends BaseComponent {
  35. arrows: { prev?: HTMLButtonElement, next?: HTMLButtonElement };
  36. }
  37. /**
  38. * The component for handling previous and next arrows.
  39. *
  40. * @since 3.0.0
  41. *
  42. * @param Splide - A Splide instance.
  43. * @param Components - A collection of components.
  44. * @param options - Options.
  45. *
  46. * @return An Arrows component object.
  47. */
  48. export function Arrows( Splide: Splide, Components: Components, options: Options ): ArrowsComponent {
  49. const { on, bind, emit, destroy: destroyEvents } = EventInterface( Splide );
  50. const { classes, i18n } = options;
  51. const { Elements, Controller } = Components;
  52. const userArrows = Elements.arrows;
  53. /**
  54. * The wrapper element.
  55. */
  56. let wrapper = userArrows;
  57. /**
  58. * The previous arrow element.
  59. */
  60. let prev = Elements.prev;
  61. /**
  62. * The next arrow element.
  63. */
  64. let next = Elements.next;
  65. /**
  66. * Indicates whether the component creates arrows or retrieved from the DOM.
  67. */
  68. let created: boolean;
  69. /**
  70. * Holds modifier classes.
  71. */
  72. let wrapperClasses: string;
  73. /**
  74. * An object with previous and next arrows.
  75. */
  76. const arrows: ArrowsComponent[ 'arrows' ] = {};
  77. /**
  78. * Called when the component is mounted.
  79. */
  80. function mount(): void {
  81. init();
  82. on( EVENT_UPDATED, remount );
  83. }
  84. /**
  85. * Remounts the component.
  86. */
  87. function remount(): void {
  88. destroy();
  89. mount();
  90. }
  91. /**
  92. * Initializes the component.
  93. */
  94. function init(): void {
  95. const enabled = options.arrows;
  96. if ( enabled && ! ( prev && next ) ) {
  97. createArrows();
  98. }
  99. if ( prev && next ) {
  100. assign( arrows, { prev, next } );
  101. display( wrapper, enabled ? '' : 'none' );
  102. addClass( wrapper, ( wrapperClasses = `${ CLASS_ARROWS }--${ options.direction }` ) );
  103. if ( enabled ) {
  104. listen();
  105. update();
  106. setAttribute( [ prev, next ], ARIA_CONTROLS, Elements.list.id );
  107. emit( EVENT_ARROWS_MOUNTED, prev, next );
  108. }
  109. }
  110. }
  111. /**
  112. * Destroys the component.
  113. */
  114. function destroy(): void {
  115. destroyEvents();
  116. removeClass( wrapper, wrapperClasses );
  117. if ( created ) {
  118. remove( userArrows ? [ prev, next ] : wrapper );
  119. prev = next = null;
  120. } else {
  121. removeAttribute( [ prev, next ], ALL_ATTRIBUTES );
  122. }
  123. }
  124. /**
  125. * Listens to some events.
  126. */
  127. function listen(): void {
  128. on( [ EVENT_MOVED, EVENT_REFRESH, EVENT_SCROLLED ], update );
  129. bind( next, 'click', apply( go, '>' ) );
  130. bind( prev, 'click', apply( go, '<' ) );
  131. }
  132. /**
  133. * The wrapper function of Controller#go().
  134. *
  135. * @param control - The control pattern.
  136. */
  137. function go( control: string ): void {
  138. Controller.go( control, true );
  139. }
  140. /**
  141. * Create arrows and append them to the slider.
  142. */
  143. function createArrows(): void {
  144. wrapper = userArrows || create( 'div', classes.arrows );
  145. prev = createArrow( true );
  146. next = createArrow( false );
  147. created = true;
  148. append( wrapper, [ prev, next ] );
  149. ! userArrows && before( wrapper, Elements.track );
  150. }
  151. /**
  152. * Creates an arrow button.
  153. *
  154. * @param prev - Determines whether to create a previous or next arrow.
  155. *
  156. * @return A created button element.
  157. */
  158. function createArrow( prev: boolean ): HTMLButtonElement {
  159. const arrow = `<button class="${ classes.arrow } ${ prev ? classes.prev : classes.next }" type="button">`
  160. + `<svg xmlns="${ XML_NAME_SPACE }" viewBox="0 0 ${ SIZE } ${ SIZE }" width="${ SIZE }" height="${ SIZE }">`
  161. + `<path d="${ options.arrowPath || PATH }" />`;
  162. return parseHtml<HTMLButtonElement>( arrow );
  163. }
  164. /**
  165. * Updates status of arrows, such as `disabled` and `aria-label`.
  166. */
  167. function update(): void {
  168. const index = Splide.index;
  169. const prevIndex = Controller.getPrev();
  170. const nextIndex = Controller.getNext();
  171. const prevLabel = prevIndex > -1 && index < prevIndex ? i18n.last : i18n.prev;
  172. const nextLabel = nextIndex > -1 && index > nextIndex ? i18n.first : i18n.next;
  173. prev.disabled = prevIndex < 0;
  174. next.disabled = nextIndex < 0;
  175. setAttribute( prev, ARIA_LABEL, prevLabel );
  176. setAttribute( next, ARIA_LABEL, nextLabel );
  177. emit( EVENT_ARROWS_UPDATED, prev, next, prevIndex, nextIndex );
  178. }
  179. return {
  180. arrows,
  181. mount,
  182. destroy,
  183. };
  184. }