Arrows.ts 4.5 KB

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