Arrows.ts 5.2 KB

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