From e33242ce41149265f04224380981c34386ec72b4 Mon Sep 17 00:00:00 2001 From: Vlad Date: Wed, 24 Mar 2021 10:42:27 +0200 Subject: [PATCH 1/8] fix: update virtual slides --- playground/angular/src/app/app.component.html | 148 ++++++++++-------- playground/angular/src/app/app.component.ts | 6 + src/angular/src/swiper.component.ts | 5 + 3 files changed, 93 insertions(+), 66 deletions(-) diff --git a/playground/angular/src/app/app.component.html b/playground/angular/src/app/app.component.html index a09ec7389..d7a7294c5 100644 --- a/playground/angular/src/app/app.component.html +++ b/playground/angular/src/app/app.component.html @@ -5,7 +5,7 @@ [slidesPerView]="3" [spaceBetween]="50" [pagination]="{ type: 'fraction' }" - virtual + [virtual]="true" slideActiveClass="swiper-active whyWouldIuseCustomClass" [centeredSlides]="true" navigation @@ -16,6 +16,22 @@ Slide Slide + + + Slide {{ slide }} + + + @@ -91,69 +107,69 @@
- - Slide 1 - Slide 2 - Slide 3 - Slide 4 - Slide 5 - Slide 6 - - - Slide 1 - Slide 2 - Slide 3 - Slide 4 - Slide 5 - Slide 6 - -
-
- - Slide 1 - Slide 2 - Slide 3 - Slide 4 - Slide 5 - Slide 6 - - - Slide 1 - Slide 2 - Slide 3 - Slide 4 - Slide 5 - Slide 6 - -
+ + Slide 1 + Slide 2 + Slide 3 + Slide 4 + Slide 5 + Slide 6 + + + Slide 1 + Slide 2 + Slide 3 + Slide 4 + Slide 5 + Slide 6 + + +
+ + Slide 1 + Slide 2 + Slide 3 + Slide 4 + Slide 5 + Slide 6 + + + Slide 1 + Slide 2 + Slide 3 + Slide 4 + Slide 5 + Slide 6 + +
diff --git a/playground/angular/src/app/app.component.ts b/playground/angular/src/app/app.component.ts index 749f33cea..43e473bda 100644 --- a/playground/angular/src/app/app.component.ts +++ b/playground/angular/src/app/app.component.ts @@ -1,4 +1,5 @@ import { ChangeDetectorRef, Component, ViewChild } from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; import { SwiperComponent } from 'src/angular/src/public-api'; import SwiperCore, { Navigation, @@ -34,9 +35,14 @@ export class AppComponent { show: boolean; thumbs: any; + slides$ = new BehaviorSubject(['']); constructor(private cd: ChangeDetectorRef) {} ngOnInit() {} + getSlides() { + this.slides$.next(Array.from({ length: 600 }).map((el, index) => `Slide ${index + 1}`)); + } + thumbsSwiper: any; setThumbsSwiper(swiper) { this.thumbsSwiper = swiper; diff --git a/src/angular/src/swiper.component.ts b/src/angular/src/swiper.component.ts index 1c666883e..adec92a9e 100644 --- a/src/angular/src/swiper.component.ts +++ b/src/angular/src/swiper.component.ts @@ -471,6 +471,11 @@ export class SwiperComponent implements OnInit { if (!this.virtual) { this.prependSlides = of(this.slides.slice(this.slides.length - this.loopedSlides)); this.appendSlides = of(this.slides.slice(0, this.loopedSlides)); + } else if (this.swiperRef && this.swiperRef.virtual) { + this._ngZone.runOutsideAngular(() => { + this.swiperRef.virtual.slides = this.slides; + this.swiperRef.virtual.update(true); + }); } this._changeDetectorRef.detectChanges(); }; From f96db02f50a8067eff8241817681615b8b26b81d Mon Sep 17 00:00:00 2001 From: Vlad Date: Wed, 24 Mar 2021 10:56:18 +0200 Subject: [PATCH 2/8] feat: custom html element support --- playground/angular/src/app/app.component.html | 138 +++++++++--------- src/angular/src/swiper.component.ts | 15 +- 2 files changed, 82 insertions(+), 71 deletions(-) diff --git a/playground/angular/src/app/app.component.html b/playground/angular/src/app/app.component.html index a09ec7389..fe8e859cf 100644 --- a/playground/angular/src/app/app.component.html +++ b/playground/angular/src/app/app.component.html @@ -73,87 +73,89 @@ {{ exampleConfig | json }} + +
{{ i }} - {{ slide }} - - + +
- - Slide 1 - Slide 2 - Slide 3 - Slide 4 - Slide 5 - Slide 6 - - - Slide 1 - Slide 2 - Slide 3 - Slide 4 - Slide 5 - Slide 6 - -
-
- - Slide 1 - Slide 2 - Slide 3 - Slide 4 - Slide 5 - Slide 6 - - - Slide 1 - Slide 2 - Slide 3 - Slide 4 - Slide 5 - Slide 6 - -
+ + Slide 1 + Slide 2 + Slide 3 + Slide 4 + Slide 5 + Slide 6 + + + Slide 1 + Slide 2 + Slide 3 + Slide 4 + Slide 5 + Slide 6 + + +
+ + Slide 1 + Slide 2 + Slide 3 + Slide 4 + Slide 5 + Slide 6 + + + Slide 1 + Slide 2 + Slide 3 + Slide 4 + Slide 5 + Slide 6 + +
diff --git a/src/angular/src/swiper.component.ts b/src/angular/src/swiper.component.ts index 1c666883e..69e6fafb6 100644 --- a/src/angular/src/swiper.component.ts +++ b/src/angular/src/swiper.component.ts @@ -164,7 +164,10 @@ export class SwiperComponent implements OnInit { }); if ( typeof this._navigation !== 'boolean' && - (typeof this._navigation?.nextEl === 'string' || typeof this._navigation?.prevEl === 'string') + (typeof this._navigation?.nextEl === 'string' || + typeof this._navigation?.prevEl === 'string' || + typeof this._navigation?.nextEl === 'object' || + typeof this._navigation?.prevEl === 'object') ) { this.showNavigation = false; } @@ -181,7 +184,10 @@ export class SwiperComponent implements OnInit { this._pagination = setProperty(val, { el: current || null, }); - if (typeof this._pagination !== 'boolean' && typeof this._pagination?.el === 'string') { + if ( + typeof this._pagination !== 'boolean' && + (typeof this._pagination?.el === 'string' || typeof this._pagination?.el === 'object') + ) { this.showPagination = false; } } @@ -197,7 +203,10 @@ export class SwiperComponent implements OnInit { this._scrollbar = setProperty(val, { el: current || null, }); - if (typeof this._scrollbar !== 'boolean' && typeof this._scrollbar?.el === 'string') { + if ( + typeof this._scrollbar !== 'boolean' && + (typeof this._scrollbar?.el === 'string' || typeof this._scrollbar?.el === 'object') + ) { this.showScrollbar = false; } } From 9fd6e68382b304d3163911cde75d77b07370e62c Mon Sep 17 00:00:00 2001 From: Vlad Date: Wed, 24 Mar 2021 11:42:50 +0200 Subject: [PATCH 3/8] feat: a11y.slideLabelMessage --- cypress/integration/modules/a11y.js | 17 +++++++++++++++++ cypress/support/commands.js | 5 +++++ src/components/a11y/a11y.js | 6 +++++- src/types/components/a11y.d.ts | 7 +++++++ 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/cypress/integration/modules/a11y.js b/cypress/integration/modules/a11y.js index 5db0ea497..0d7bc614c 100644 --- a/cypress/integration/modules/a11y.js +++ b/cypress/integration/modules/a11y.js @@ -14,6 +14,14 @@ context('Core', () => { cy.getSliderWrapper().should('have.attr', 'aria-live', 'polite'); }); + it.only('paginationBulletMessage', () => { + cy.initSwiper({ + pagination: true, + a11y: { paginationBulletMessage: 'Slide to {{index}}' }, + }); + cy.getPaginationBullet(1).should('have.attr', 'Slide to', '2'); + }); + it('should add aria-role-description="slide" to swiper-slide', () => { cy.initSwiper({ a11y: { itemRoleDescriptionMessage: 'test' }, @@ -21,6 +29,15 @@ context('Core', () => { cy.getSlides().should('have.attr', 'aria-role-description', 'test'); }); + it('should add aria-label="1 of 10" to swiper-slide', () => { + cy.initSwiper({ + a11y: { slideLabelMessage: '{{index}} of {{slidesLength}}' }, + }); + cy.getSlide(0).should('have.attr', 'aria-label', '1 of 10'); + cy.getSlide(4).should('have.attr', 'aria-label', '5 of 10'); + cy.getSlide(9).should('have.attr', 'aria-label', '10 of 10'); + }); + it('should add aria-role-description="slide" to swiper-container', () => { cy.initSwiper({ a11y: { containerRoleDescriptionMessage: 'test' }, diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 7ea4d262d..618b23e75 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -30,6 +30,7 @@ Cypress.Commands.add('getSliderContainer', { prevSubject: 'optional' }, () => { Cypress.Commands.add('getSlide', { prevSubject: 'optional' }, (subject, slideIndex) => { return cy.get(`.swiper-slide:nth-child(${slideIndex + 1})`); }); + Cypress.Commands.add('getSlideContains', { prevSubject: 'optional' }, (subject, content) => { cy.get('.swiper-container').contains(content); }); @@ -40,6 +41,10 @@ Cypress.Commands.add('swiperPage', { prevSubject: 'optional' }, () => { return cy.visit('cypress/test.html'); }); +Cypress.Commands.add('getPaginationBullet', { prevSubject: 'optional' }, (subject, bulletIndex) => { + return cy.get(`.swiper-pagination-bullet:nth-child(${bulletIndex + 1})`); +}); + Cypress.Commands.add( 'initSwiper', { prevSubject: 'optional' }, diff --git a/src/components/a11y/a11y.js b/src/components/a11y/a11y.js index 801bdc0ae..8833cf3f3 100644 --- a/src/components/a11y/a11y.js +++ b/src/components/a11y/a11y.js @@ -167,7 +167,10 @@ const A11y = { swiper.a11y.addElRole($(swiper.slides), 'group'); swiper.slides.each((slideEl) => { const $slideEl = $(slideEl); - swiper.a11y.addElLabel($slideEl, `${$slideEl.index() + 1} / ${swiper.slides.length}`); + const ariaLabelMessage = params.slideLabelMessage + .replace(/\{\{index\}\}/, $slideEl.index() + 1) + .replace(/\{\{slidesLength\}\}/, swiper.slides.length); + swiper.a11y.addElLabel($slideEl, ariaLabelMessage); }); // Navigation @@ -259,6 +262,7 @@ export default { firstSlideMessage: 'This is the first slide', lastSlideMessage: 'This is the last slide', paginationBulletMessage: 'Go to slide {{index}}', + slideLabelMessage: '{{index}} / {{slidesLength}}', containerMessage: null, containerRoleDescriptionMessage: null, itemRoleDescriptionMessage: null, diff --git a/src/types/components/a11y.d.ts b/src/types/components/a11y.d.ts index c73cabde3..d85f3dfc4 100644 --- a/src/types/components/a11y.d.ts +++ b/src/types/components/a11y.d.ts @@ -72,4 +72,11 @@ export interface A11yOptions { * @default null */ itemRoleDescriptionMessage?: string | null; + + /** + * Message for screen readers describing the label of slide element + * + * @default '{{index}} / {{slidesLength}}' + */ + slideLabelMessage?: string; } From 259c8c4fce5a8c73c23b9e350e19c3d40ff590e3 Mon Sep 17 00:00:00 2001 From: Vlad Date: Wed, 24 Mar 2021 14:25:49 +0200 Subject: [PATCH 4/8] tests: paginationBulletMessage --- cypress/integration/modules/a11y.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cypress/integration/modules/a11y.js b/cypress/integration/modules/a11y.js index 0d7bc614c..9ecd1df9a 100644 --- a/cypress/integration/modules/a11y.js +++ b/cypress/integration/modules/a11y.js @@ -14,12 +14,14 @@ context('Core', () => { cy.getSliderWrapper().should('have.attr', 'aria-live', 'polite'); }); - it.only('paginationBulletMessage', () => { + it('paginationBulletMessage', () => { cy.initSwiper({ pagination: true, a11y: { paginationBulletMessage: 'Slide to {{index}}' }, }); - cy.getPaginationBullet(1).should('have.attr', 'Slide to', '2'); + cy.getPaginationBullet(1).should('have.attr', 'aria-label', 'Slide to 2'); + cy.getPaginationBullet(4).should('have.attr', 'aria-label', 'Slide to 5'); + cy.getPaginationBullet(9).should('have.attr', 'aria-label', 'Slide to 10'); }); it('should add aria-role-description="slide" to swiper-slide', () => { From 95b5dfe16e7a3b565dd5b22504c6b430f2a660bb Mon Sep 17 00:00:00 2001 From: Vlad Date: Wed, 24 Mar 2021 14:26:41 +0200 Subject: [PATCH 5/8] fix: isObject cross window --- src/angular/src/utils/utils.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/angular/src/utils/utils.ts b/src/angular/src/utils/utils.ts index 7f31f4096..5f2452fd4 100644 --- a/src/angular/src/utils/utils.ts +++ b/src/angular/src/utils/utils.ts @@ -1,5 +1,10 @@ export function isObject(o) { - return typeof o === 'object' && o !== null && o.constructor && o.constructor === Object; + return ( + typeof o === 'object' && + o !== null && + o.constructor && + Object.prototype.toString.call(o).slice(8, -1) === 'Object' + ); } export function extend(target, src) { From 7c36077c5e3442576769adef73daa8aa28997732 Mon Sep 17 00:00:00 2001 From: Vlad Date: Wed, 24 Mar 2021 14:39:03 +0200 Subject: [PATCH 6/8] fix: isObject cross window --- src/components/core/core-class.js | 6 +++++- src/react/utils.js | 7 ++++++- src/svelte/utils.js | 7 ++++++- src/utils/utils.js | 7 ++++++- src/vue/utils.js | 7 ++++++- 5 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/components/core/core-class.js b/src/components/core/core-class.js index 1e5692163..f8f22b80d 100644 --- a/src/components/core/core-class.js +++ b/src/components/core/core-class.js @@ -49,7 +49,11 @@ class Swiper { constructor(...args) { let el; let params; - if (args.length === 1 && args[0].constructor && args[0].constructor === Object) { + if ( + args.length === 1 && + args[0].constructor && + Object.prototype.toString.call(args[0]).slice(8, -1) === 'Object' + ) { params = args[0]; } else { [el, params] = args; diff --git a/src/react/utils.js b/src/react/utils.js index 7993804ff..0378af8df 100644 --- a/src/react/utils.js +++ b/src/react/utils.js @@ -1,5 +1,10 @@ function isObject(o) { - return typeof o === 'object' && o !== null && o.constructor && o.constructor === Object; + return ( + typeof o === 'object' && + o !== null && + o.constructor && + Object.prototype.toString.call(o).slice(8, -1) === 'Object' + ); } function extend(target, src) { diff --git a/src/svelte/utils.js b/src/svelte/utils.js index 7993804ff..0378af8df 100644 --- a/src/svelte/utils.js +++ b/src/svelte/utils.js @@ -1,5 +1,10 @@ function isObject(o) { - return typeof o === 'object' && o !== null && o.constructor && o.constructor === Object; + return ( + typeof o === 'object' && + o !== null && + o.constructor && + Object.prototype.toString.call(o).slice(8, -1) === 'Object' + ); } function extend(target, src) { diff --git a/src/utils/utils.js b/src/utils/utils.js index 4c9348634..c5b8d2814 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -85,7 +85,12 @@ function getTranslate(el, axis = 'x') { return curTransform || 0; } function isObject(o) { - return typeof o === 'object' && o !== null && o.constructor && o.constructor === Object; + return ( + typeof o === 'object' && + o !== null && + o.constructor && + Object.prototype.toString.call(o).slice(8, -1) === 'Object' + ); } function extend(...args) { const to = Object(args[0]); diff --git a/src/vue/utils.js b/src/vue/utils.js index e2e895d0c..a6839f045 100644 --- a/src/vue/utils.js +++ b/src/vue/utils.js @@ -1,5 +1,10 @@ function isObject(o) { - return typeof o === 'object' && o !== null && o.constructor && o.constructor === Object; + return ( + typeof o === 'object' && + o !== null && + o.constructor && + Object.prototype.toString.call(o).slice(8, -1) === 'Object' + ); } function extend(target, src) { From 23d27b36b2e666b4c23a812d6e1f5f46363e2d9b Mon Sep 17 00:00:00 2001 From: Oleg Yamnikov Date: Wed, 24 Mar 2021 18:05:02 +0500 Subject: [PATCH 7/8] Fix getPropertyValue call in updateSlides getPropertyValue only accepts one argument. This typo results in the call always returning "". Because of that slide's size is sometimes calculated incorrectly. --- src/components/core/update/updateSlides.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/core/update/updateSlides.js b/src/components/core/update/updateSlides.js index 18190feff..bf5b35c02 100644 --- a/src/components/core/update/updateSlides.js +++ b/src/components/core/update/updateSlides.js @@ -167,7 +167,7 @@ export default function updateSlides() { const paddingRight = getDirectionPropertyValue(slideStyles, 'padding-right'); const marginLeft = getDirectionPropertyValue(slideStyles, 'margin-left'); const marginRight = getDirectionPropertyValue(slideStyles, 'margin-right'); - const boxSizing = slideStyles.getPropertyValue(slideStyles, 'box-sizing'); + const boxSizing = slideStyles.getPropertyValue('box-sizing'); if (boxSizing && boxSizing === 'border-box') { slideSize = width + marginLeft + marginRight; } else { From 0626be8ccd8e58f222a7a23b07f0ed3f73b2b9b3 Mon Sep 17 00:00:00 2001 From: Dom Stubbs Date: Wed, 24 Mar 2021 20:07:26 +0000 Subject: [PATCH 8/8] Avoid using cached dimensions for keyboard inView calcs --- src/components/keyboard/keyboard.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/keyboard/keyboard.js b/src/components/keyboard/keyboard.js index 94df48a60..9637a6894 100644 --- a/src/components/keyboard/keyboard.js +++ b/src/components/keyboard/keyboard.js @@ -56,15 +56,19 @@ const Keyboard = { ) { return undefined; } + + const $el = swiper.$el; + const swiperWidth = $el[0].clientWidth; + const swiperHeight = $el[0].clientHeight; const windowWidth = window.innerWidth; const windowHeight = window.innerHeight; const swiperOffset = swiper.$el.offset(); if (rtl) swiperOffset.left -= swiper.$el[0].scrollLeft; const swiperCoord = [ [swiperOffset.left, swiperOffset.top], - [swiperOffset.left + swiper.width, swiperOffset.top], - [swiperOffset.left, swiperOffset.top + swiper.height], - [swiperOffset.left + swiper.width, swiperOffset.top + swiper.height], + [swiperOffset.left + swiperWidth, swiperOffset.top], + [swiperOffset.left, swiperOffset.top + swiperHeight], + [swiperOffset.left + swiperWidth, swiperOffset.top + swiperHeight], ]; for (let i = 0; i < swiperCoord.length; i += 1) { const point = swiperCoord[i];