Skip to content

Commit

Permalink
refactor(esl-carousel): rework a11ty, introduce next/prev markers
Browse files Browse the repository at this point in the history
  • Loading branch information
ala-n committed Apr 8, 2024
1 parent cff2a78 commit 6659889
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 24 deletions.
8 changes: 4 additions & 4 deletions site/views/examples/carousel.njk
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ icon: examples/carousel.svg
</div>
<div class="card-content p-3">
<h5>{{ item.name }}</h5>
{{ lorem.string(12) }}
<p>{{ lorem.string(12) }}</p>
<button type="button" class="btn" onclick="alert('I\'m alive')">Hey cart!</button>
</div>
</div>
</esl-carousel-slide>
Expand All @@ -46,16 +47,15 @@ icon: examples/carousel.svg
</div>
</section>


<a href="#"> Something accessible</a>
<section>
<h2>Carousel with siblings: </h2>
<div esl-carousel-container>
<button class="esl-carousel-arrow inner hide-xs" esl-carousel-nav="group:prev">
<span class="sr-only">Previous Slide</span>
</button>

<esl-carousel class="esl-no-hidden-slides"
style="padding-inline: 60px"
<esl-carousel style="padding-inline: 60px"
esl-carousel-touch
count="2"
loop="true">
Expand Down
15 changes: 9 additions & 6 deletions src/modules/esl-carousel/core/esl-carousel.renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {memoize} from '../../esl-utils/decorators';
import {isEqual} from '../../esl-utils/misc/object';
import {SyntheticEventTarget} from '../../esl-utils/dom';
import {ESLCarouselSlideEvent} from './esl-carousel.events';
import {calcDirection} from './nav/esl-carousel.nav.utils';
import {calcDirection, normalizeIndex} from './nav/esl-carousel.nav.utils';

import type {ESLCarousel, ESLCarouselActionParams} from './esl-carousel';
import type {ESLCarouselConfig, ESLCarouselDirection} from './nav/esl-carousel.nav.types';
Expand Down Expand Up @@ -126,11 +126,14 @@ export abstract class ESLCarouselRenderer implements ESLCarouselConfig {
/** Sets active slides from passed index **/
public setActive(current: number, event?: Partial<ESLCarouselSlideEventInit>): void {
const related = this.$carousel.activeIndex;

this.$carousel.$slides.forEach((el) => el.active = false);
const count = Math.min(this.count, this.size);
for (let i = 0; i < count; i++) {
this.$carousel.slideAt(current + i).active = true;
const nextIndex = normalizeIndex(current + this.count, this.size);
const prevIndex = normalizeIndex(current - 1, this.size);

for (let i = 0; i < this.size; i++) {
this.$slides[i].active = i >= current && i < current + this.count;
if (this.$slides[i].active) continue; // skip next/prev calculation for active slides
this.$slides[i].next = i === nextIndex;
this.$slides[i].prev = i === prevIndex;
}

if (event && typeof event === 'object') {
Expand Down
22 changes: 21 additions & 1 deletion src/modules/esl-carousel/core/esl-carousel.slide.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {ESLBaseElement} from '../../esl-base-element/core';
import {boolAttr, memoize, ready} from '../../esl-utils/decorators';
import {microtask} from '../../esl-utils/async/microtask';
import {boolAttr, decorate, memoize, ready} from '../../esl-utils/decorators';
import {findNext, findPrev, findNextLooped, findPrevLooped} from '../../esl-utils/dom/traversing';

import type {ESLCarousel} from './esl-carousel';
Expand All @@ -13,10 +14,19 @@ import type {ESLCarousel} from './esl-carousel';
export class ESLCarouselSlide extends ESLBaseElement {
public static observedAttributes = ['active'];

/** Carousel marker to omit `inert` attribute on slides */
public static readonly NO_INERT_MARKER = 'no-inert';

/** @returns if the slide is active */
@boolAttr() public active: boolean;
/** @returns if slide is going to be next active */
@boolAttr() public preActive: boolean;

/** Slide is next to active slide */
@boolAttr() public next: boolean;
/** Slide is previous to active slide */
@boolAttr() public prev: boolean;

@memoize()
public get $carousel(): ESLCarousel | undefined {
const carouselTag = this.baseTagName.replace('-slide', '');
Expand Down Expand Up @@ -78,6 +88,16 @@ export class ESLCarouselSlide extends ESLBaseElement {
/** Updates A11y attributes related to active state */
protected updateActiveStateA11y(): void {
this.setAttribute('aria-hidden', String(!this.active));
if (!this.$carousel?.hasAttribute(ESLCarouselSlide.NO_INERT_MARKER)) {
this.toggleAttribute('inert', !this.active);
}
if (!this.active) this.blurIfInactive();
}

@decorate(microtask)
protected blurIfInactive(): void {
if (this.active || !this.contains(document.activeElement)) return;
this.$carousel?.focus({preventScroll: true});
}

/** Creates slide element, use passed content as slide inner */
Expand Down
2 changes: 1 addition & 1 deletion src/modules/esl-carousel/core/esl-carousel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class ESLCarousel extends ESLBaseElement {
@attr({defaultValue: 'all'}) public media: string;
/** Renderer type name (`multi` by default). Supports {@link ESLMediaRuleList} syntax */
@attr({defaultValue: 'default'}) public type: string;
/** Marker to enable loop mode for carousel (`true` by default). Supports {@link ESLMediaRuleList} syntax */
/** Marker to enable loop mode for a carousel (`true` by default). Supports {@link ESLMediaRuleList} syntax */
@attr({defaultValue: 'false'}) public loop: string;
/** Count of slides to show on the screen (`1` by default). Supports {@link ESLMediaRuleList} syntax */
@attr({defaultValue: '1'}) public count: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
.esl-carousel-default-renderer {
esl-carousel-slide:not(:is([active], [visible])) {
visibility: hidden;
}
&.esl-no-hidden-slides esl-carousel-slide {
visibility: visible;
}

[data-slides-area] {
display: flex;
flex-wrap: nowrap;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export class ESLDefaultCarouselRenderer extends ESLCarouselRenderer {
/** Pre-processing animation action. */
public override async onBeforeAnimate(): Promise<void> {
if (this.$carousel.hasAttribute('animating')) throw new Error('[ESL] Carousel: already animating');
this.$slides.forEach((el) => el.toggleAttribute('visible', true));
this.$carousel.toggleAttribute('active', true);
}

/** Processes animation. */
Expand All @@ -90,7 +90,7 @@ export class ESLDefaultCarouselRenderer extends ESLCarouselRenderer {
/** Post-processing animation action. */
public override async onAfterAnimate(): Promise<void> {
this.setTransformOffset(-this.getOffset(this.currentIndex));
this.$slides.forEach((el) => el.removeAttribute('visible'));
this.$carousel.toggleAttribute('active', false);
}

/** Pre-processing the transition animation of one slide. */
Expand All @@ -107,6 +107,7 @@ export class ESLDefaultCarouselRenderer extends ESLCarouselRenderer {
this.setTransformOffset(offsetTo);

if (offsetTo === offsetFrom) return;
// TODO: still not catches cases with no animation (
await promisifyTransition(this.$area, 'transform');
}

Expand Down Expand Up @@ -140,7 +141,7 @@ export class ESLDefaultCarouselRenderer extends ESLCarouselRenderer {

/** Handles the slides transition. */
public onMove(offset: number): void {
this.$slides.forEach((el) => el.toggleAttribute('visible', true));
this.$carousel.toggleAttribute('active', true);

if (!this._checkNonLoop(offset)) return;

Expand Down Expand Up @@ -184,7 +185,7 @@ export class ESLDefaultCarouselRenderer extends ESLCarouselRenderer {
this.reorder(this.currentIndex);
this.setTransformOffset(-this.getOffset(this.currentIndex));
this.setActive(this.currentIndex, {direction: sign > 0 ? 'next' : 'prev'});
this.$slides.forEach((el) => el.toggleAttribute('visible', false));
this.$carousel.toggleAttribute('active', false);
}

/** Sets order style property for slides starting at index */
Expand Down Expand Up @@ -212,7 +213,7 @@ export class ESLDefaultCarouselRenderer extends ESLCarouselRenderer {

this.gap = parseFloat(this.vertical ? areaStyles.rowGap : areaStyles.columnGap);
const areaSize = parseFloat(this.vertical ? areaStyles.height : areaStyles.width);
this.slideSize = (areaSize - this.gap * (this.count - 1)) / this.count;
this.slideSize = Math.floor((areaSize - this.gap * (this.count - 1)) / this.count);
this.$area.style.setProperty(ESLDefaultCarouselRenderer.SIZE_PROP, this.slideSize + 'px');
}
}

0 comments on commit 6659889

Please sign in to comment.