123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- import {
- ALL_ATTRIBUTES,
- ARIA_CONTROLS,
- ARIA_CURRENT,
- ARIA_HIDDEN,
- ARIA_LABEL,
- ARIA_ROLEDESCRIPTION,
- ROLE,
- TAB_INDEX,
- } from '../../constants/attributes';
- import {
- CLASS_ACTIVE,
- CLASS_CONTAINER,
- CLASS_NEXT,
- CLASS_PREV,
- CLASS_VISIBLE,
- STATUS_CLASSES,
- } from '../../constants/classes';
- import {
- EVENT_ACTIVE,
- EVENT_CLICK,
- EVENT_HIDDEN,
- EVENT_INACTIVE,
- EVENT_MOVE,
- EVENT_MOVED,
- EVENT_NAVIGATION_MOUNTED,
- EVENT_REFRESH,
- EVENT_REPOSITIONED,
- EVENT_SCROLLED,
- EVENT_SHIFTED,
- EVENT_SLIDE_KEYDOWN,
- EVENT_VISIBLE,
- } from '../../constants/events';
- import { FADE, LOOP } from '../../constants/types';
- import { EventInterface } from '../../constructors';
- import { Splide } from '../../core/Splide/Splide';
- import { BaseComponent } from '../../types';
- import {
- abs,
- apply,
- ceil,
- child,
- floor,
- focus,
- format,
- getAttribute,
- hasClass,
- min,
- pad,
- queryAll,
- rect,
- removeAttribute,
- removeClass,
- setAttribute,
- style as _style,
- toggleClass,
- } from '../../utils';
- /**
- * The interface for the Slide sub component.
- *
- * @since 3.0.0
- */
- export interface SlideComponent extends BaseComponent {
- index: number;
- slideIndex: number;
- slide: HTMLElement;
- container: HTMLElement;
- isClone: boolean;
- update(): void;
- style( prop: string, value: string | number, useContainer?: boolean ): void
- isWithin( from: number, distance: number ): boolean;
- }
- /**
- * The sub component for managing each slide.
- *
- * @since 3.0.0
- *
- * @param Splide - A Splide instance.
- * @param index - A slide index.
- * @param slideIndex - A slide index for clones. This must be `-1` if the slide is not a clone.
- * @param slide - A slide element.
- *
- * @return A Slide sub component.
- */
- export function Slide( Splide: Splide, index: number, slideIndex: number, slide: HTMLElement ): SlideComponent {
- const { on, emit, bind, destroy: destroyEvents } = EventInterface( Splide );
- const { Components, root, options } = Splide;
- const { isNavigation, updateOnMove, i18n, pagination } = options;
- const { resolve } = Components.Direction;
- const styles = getAttribute( slide, 'style' );
- const isClone = slideIndex > -1;
- const container = child( slide, `.${ CLASS_CONTAINER }` );
- /**
- * Turns into `true` when the component is destroyed.
- */
- let destroyed: boolean;
- /**
- * Called when the component is mounted.
- */
- function mount( this: SlideComponent ): void {
- if ( ! isClone ) {
- slide.id = `${ root.id }-slide${ pad( index + 1 ) }`;
- setAttribute( slide, ROLE, pagination ? 'tabpanel' : 'group' );
- setAttribute( slide, ARIA_ROLEDESCRIPTION, pagination ? '' : i18n.slide );
- setAttribute( slide, ARIA_LABEL, format( i18n.slideLabel, [ index + 1, Splide.length ] ) );
- }
- listen();
- }
- /**
- * Listens to some events.
- */
- function listen(): void {
- bind( slide, 'click', apply( emit, EVENT_CLICK, self ) );
- bind( slide, 'keydown', apply( emit, EVENT_SLIDE_KEYDOWN, self ) );
- on( [ EVENT_REFRESH, EVENT_REPOSITIONED, EVENT_MOVED, EVENT_SCROLLED ], apply( update, true ) );
- on( EVENT_SHIFTED, apply( update, false ) );
- on( EVENT_NAVIGATION_MOUNTED, initNavigation );
- if ( updateOnMove ) {
- on( EVENT_MOVE, onMove );
- }
- }
- /**
- * Destroys the component.
- */
- function destroy(): void {
- destroyed = true;
- destroyEvents();
- removeClass( slide, STATUS_CLASSES );
- removeAttribute( slide, ALL_ATTRIBUTES );
- setAttribute( slide, 'style', styles );
- }
- /**
- * Initializes slides as navigation.
- */
- function initNavigation(): void {
- const controls = Splide.splides.map( target => {
- const Slide = target.splide.Components.Slides.getAt( index );
- return Slide ? Slide.slide.id : '';
- } ).join( ' ' );
- setAttribute( slide, ARIA_LABEL, format( i18n.slideX, ( isClone ? slideIndex : index ) + 1 ) );
- setAttribute( slide, ARIA_CONTROLS, controls );
- updateAttributes();
- }
- /**
- * If the `updateOnMove` option is `true`, called when the slider starts moving.
- */
- function onMove(): void {
- if ( ! destroyed ) {
- update();
- }
- }
- /**
- * Updates attribute and classes of the slide.
- *
- * @param attributes - Determines whether to update attributes or not.
- */
- function update( attributes?: boolean ): void {
- if ( ! destroyed ) {
- const { index: curr } = Splide;
- updateActivity();
- updateVisibility();
- toggleClass( slide, CLASS_PREV, index === curr - 1 );
- toggleClass( slide, CLASS_NEXT, index === curr + 1 );
- attributes && updateAttributes();
- }
- }
- /**
- * Updates the status related with activity.
- */
- function updateActivity(): void {
- const active = isActive();
- if ( active !== hasClass( slide, CLASS_ACTIVE ) ) {
- toggleClass( slide, CLASS_ACTIVE, active );
- emit( active ? EVENT_ACTIVE : EVENT_INACTIVE, self );
- }
- }
- /**
- * Updates classes and attributes related with visibility.
- * If the slide has focus and gets hidden, moves focus to the active slide.
- */
- function updateVisibility(): void {
- const visible = isVisible();
- if ( visible !== hasClass( slide, CLASS_VISIBLE ) ) {
- toggleClass( slide, CLASS_VISIBLE, visible );
- emit( visible ? EVENT_VISIBLE : EVENT_HIDDEN, self );
- }
- if ( ! visible && document.activeElement === slide ) {
- const Slide = Components.Slides.getAt( Splide.index );
- Slide && focus( Slide.slide );
- }
- }
- /**
- * Updates attributes.
- * Do not call this on "shifted" event to avoid SR from reading clone's contents.
- */
- function updateAttributes(): void {
- const active = isActive();
- const hidden = ! isVisible() && ( ! active || isClone );
- setAttribute( slide, ARIA_CURRENT, isNavigation && active || '' );
- setAttribute( slide, ARIA_HIDDEN, hidden || '' );
- setAttribute( slide, TAB_INDEX, ! hidden && options.slideFocus ? 0 : '' );
- setAttribute( queryAll( slide, options.focusableNodes || '' ), TAB_INDEX, hidden ? -1 : '' );
- }
- /**
- * Adds a CSS rule to the slider or the container.
- *
- * @param prop - A property name.
- * @param value - A CSS value to add.
- * @param useContainer - Optional. Determines whether to apply the rule to the container or not.
- */
- function style( prop: string, value: string | number, useContainer?: boolean ): void {
- _style( ( useContainer && container ) || slide, prop, value );
- }
- /**
- * Checks if the slide is active or not.
- *
- * @return `true` if the slide is active.
- */
- function isActive(): boolean {
- const { index: curr } = Splide;
- return curr === index || ( options.cloneStatus && curr === slideIndex );
- }
- /**
- * Checks if the slide is visible or not.
- */
- function isVisible(): boolean {
- if ( Splide.is( FADE ) ) {
- return isActive();
- }
- const trackRect = rect( Components.Elements.track );
- const slideRect = rect( slide );
- const left = resolve( 'left' );
- const right = resolve( 'right' );
- return floor( trackRect[ left ] ) <= ceil( slideRect[ left ] )
- && floor( slideRect[ right ] ) <= ceil( trackRect[ right ] );
- }
- /**
- * Calculates how far this slide is from another slide and
- * returns `true` if the distance is within the given number.
- *
- * @param from - An index of a base slide.
- * @param distance - `true` if the slide is within this number.
- *
- * @return `true` if the slide is within the `distance` from the base slide, or otherwise `false`.
- */
- function isWithin( from: number, distance: number ): boolean {
- let diff = abs( from - index );
- if ( ! isClone && ( options.rewind || Splide.is( LOOP ) ) ) {
- diff = min( diff, Splide.length - diff );
- }
- return diff <= distance;
- }
- const self = {
- index,
- slideIndex,
- slide,
- container,
- isClone,
- mount,
- destroy,
- update,
- style,
- isWithin,
- };
- return self;
- }
|