Arrows.ts 4.4 KB

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