Current File : /home/exataengenharia/public_html/node_modules/@splidejs/splide/src/js/components/Slides/Slide.ts |
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_SCROLLED,
EVENT_SHIFTED,
EVENT_SLIDE_KEYDOWN,
EVENT_VISIBLE,
} from '../../constants/events';
import { MOVING, SCROLLING } from '../../constants/states';
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 subcomponent 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 subcomponent.
*/
export function Slide( Splide: Splide, index: number, slideIndex: number, slide: HTMLElement ): SlideComponent {
const event = EventInterface( Splide );
const { on, emit, bind } = event;
const { Components, root, options } = Splide;
const { isNavigation, updateOnMove, i18n, pagination, slideFocus } = options;
const { resolve } = Components.Direction;
const styles = getAttribute( slide, 'style' );
const label = getAttribute( slide, ARIA_LABEL );
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, i18n.slide );
setAttribute( slide, ARIA_LABEL, 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_MOVED, EVENT_SHIFTED, EVENT_SCROLLED ], update );
on( EVENT_NAVIGATION_MOUNTED, initNavigation );
if ( updateOnMove ) {
on( EVENT_MOVE, onMove );
}
}
/**
* Destroys the component.
*/
function destroy(): void {
destroyed = true;
event.destroy();
removeClass( slide, STATUS_CLASSES );
removeAttribute( slide, ALL_ATTRIBUTES );
setAttribute( slide, 'style', styles );
setAttribute( slide, ARIA_LABEL, label || '' );
}
/**
* 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 );
setAttribute( slide, ROLE, slideFocus ? 'button' : '' );
slideFocus && removeAttribute( slide, ARIA_ROLEDESCRIPTION );
}
/**
* 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.
*/
function update(): void {
if ( ! destroyed ) {
const { index: curr } = Splide;
updateActivity();
updateVisibility();
toggleClass( slide, CLASS_PREV, index === curr - 1 );
toggleClass( slide, CLASS_NEXT, index === curr + 1 );
}
}
/**
* Updates the status related with activity.
*/
function updateActivity(): void {
const active = isActive();
if ( active !== hasClass( slide, CLASS_ACTIVE ) ) {
toggleClass( slide, CLASS_ACTIVE, active );
setAttribute( slide, ARIA_CURRENT, isNavigation && active || '' );
emit( active ? EVENT_ACTIVE : EVENT_INACTIVE, self );
}
}
/**
* Updates classes and attributes related with visibility.
* - Do not update aria-hidden on shifting to avoid Window Narrator from start reading contents.
* - If the slide has focus and gets hidden, moves focus to the active slide.
*/
function updateVisibility(): void {
const visible = isVisible();
const hidden = ! visible && ( ! isActive() || isClone );
if ( ! Splide.state.is( [ MOVING, SCROLLING ] ) ) {
setAttribute( slide, ARIA_HIDDEN, hidden || '' );
}
setAttribute( queryAll( slide, options.focusableNodes || '' ), TAB_INDEX, hidden ? -1 : '' );
if ( slideFocus ) {
setAttribute( slide, TAB_INDEX, hidden ? -1 : 0 );
}
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 );
}
}
/**
* 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', true );
const right = resolve( 'right', true );
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;
}