Arrows.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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 event = EventInterface( Splide );
  50. const { on, bind, emit } = event;
  51. const { classes, i18n } = options;
  52. const { Elements, Controller } = Components;
  53. const { arrows: placeholder, track } = Elements;
  54. /**
  55. * The wrapper element.
  56. */
  57. let wrapper = placeholder;
  58. /**
  59. * The previous arrow element.
  60. */
  61. let prev = Elements.prev;
  62. /**
  63. * The next arrow element.
  64. */
  65. let next = Elements.next;
  66. /**
  67. * Indicates whether the component creates arrows or retrieved from the DOM.
  68. */
  69. let created: boolean;
  70. /**
  71. * Holds modifier classes.
  72. */
  73. let wrapperClasses: string;
  74. /**
  75. * An object with previous and next arrows.
  76. */
  77. const arrows: ArrowsComponent[ 'arrows' ] = {};
  78. /**
  79. * Called when the component is mounted.
  80. */
  81. function mount(): void {
  82. init();
  83. on( EVENT_UPDATED, remount );
  84. }
  85. /**
  86. * Remounts the component.
  87. */
  88. function remount(): void {
  89. destroy();
  90. mount();
  91. }
  92. /**
  93. * Initializes the component.
  94. */
  95. function init(): void {
  96. const enabled = options.arrows;
  97. if ( enabled && ! ( prev && next ) ) {
  98. createArrows();
  99. }
  100. if ( prev && next ) {
  101. assign( arrows, { prev, next } );
  102. display( wrapper, enabled ? '' : 'none' );
  103. addClass( wrapper, ( wrapperClasses = `${ CLASS_ARROWS }--${ options.direction }` ) );
  104. if ( enabled ) {
  105. listen();
  106. update();
  107. setAttribute( [ prev, next ], ARIA_CONTROLS, track.id );
  108. emit( EVENT_ARROWS_MOUNTED, prev, next );
  109. }
  110. }
  111. }
  112. /**
  113. * Destroys the component.
  114. */
  115. function destroy(): void {
  116. event.destroy();
  117. removeClass( wrapper, wrapperClasses );
  118. if ( created ) {
  119. remove( placeholder ? [ prev, next ] : wrapper );
  120. prev = next = null;
  121. } else {
  122. removeAttribute( [ prev, next ], ALL_ATTRIBUTES );
  123. }
  124. }
  125. /**
  126. * Listens to some events.
  127. */
  128. function listen(): void {
  129. on( [ EVENT_MOVED, EVENT_REFRESH, EVENT_SCROLLED ], update );
  130. bind( next, 'click', apply( go, '>' ) );
  131. bind( prev, 'click', apply( go, '<' ) );
  132. }
  133. /**
  134. * The wrapper function of Controller#go().
  135. *
  136. * @param control - The control pattern.
  137. */
  138. function go( control: string ): void {
  139. Controller.go( control, true );
  140. }
  141. /**
  142. * Create arrows and append them to the slider.
  143. */
  144. function createArrows(): void {
  145. wrapper = placeholder || create( 'div', classes.arrows );
  146. prev = createArrow( true );
  147. next = createArrow( false );
  148. created = true;
  149. append( wrapper, [ prev, next ] );
  150. ! placeholder && before( wrapper, track );
  151. }
  152. /**
  153. * Creates an arrow button.
  154. * In IE, A SVG element is focusable.
  155. *
  156. * @param prev - Determines whether to create a previous or next arrow.
  157. *
  158. * @return A created button element.
  159. */
  160. function createArrow( prev: boolean ): HTMLButtonElement {
  161. const arrow = `<button class="${ classes.arrow } ${ prev ? classes.prev : classes.next }" type="button">`
  162. + `<svg xmlns="${ XML_NAME_SPACE }" viewBox="0 0 ${ SIZE } ${ SIZE }" width="${ SIZE }" height="${ SIZE }" focusable="false">`
  163. + `<path d="${ options.arrowPath || PATH }" />`;
  164. return parseHtml<HTMLButtonElement>( arrow );
  165. }
  166. /**
  167. * Updates status of arrows, such as `disabled` and `aria-label`.
  168. */
  169. function update(): void {
  170. const index = Splide.index;
  171. const prevIndex = Controller.getPrev();
  172. const nextIndex = Controller.getNext();
  173. const prevLabel = prevIndex > -1 && index < prevIndex ? i18n.last : i18n.prev;
  174. const nextLabel = nextIndex > -1 && index > nextIndex ? i18n.first : i18n.next;
  175. prev.disabled = prevIndex < 0;
  176. next.disabled = nextIndex < 0;
  177. setAttribute( prev, ARIA_LABEL, prevLabel );
  178. setAttribute( next, ARIA_LABEL, nextLabel );
  179. emit( EVENT_ARROWS_UPDATED, prev, next, prevIndex, nextIndex );
  180. }
  181. return {
  182. arrows,
  183. mount,
  184. destroy,
  185. };
  186. }