From 575bc841539d8ceb79b88484534c7caf4e94a8fb Mon Sep 17 00:00:00 2001 From: Vladimir Kharlampidi Date: Tue, 11 May 2021 15:46:25 +0300 Subject: [PATCH] feat(core): new parameters and methods to enable/disable Swiper dynamically fixes #4356 fixes #4311 --- .../core/breakpoints/setBreakpoint.js | 122 ++++++++++-------- src/components/core/core-class.js | 27 +++- src/components/core/defaults.js | 1 + src/components/core/events/onClick.js | 1 + src/components/core/events/onScroll.js | 3 +- src/components/core/events/onTouchEnd.js | 3 +- src/components/core/events/onTouchMove.js | 3 +- src/components/core/events/onTouchStart.js | 3 +- src/components/core/slide/slideNext.js | 3 +- src/components/core/slide/slidePrev.js | 3 +- src/components/core/slide/slideTo.js | 8 +- src/components/keyboard/keyboard.js | 2 + src/components/mousewheel/mousewheel.js | 4 + src/components/navigation/navigation.js | 26 +++- src/components/pagination/pagination.js | 16 ++- src/components/scrollbar/scrollbar.js | 18 ++- src/types/swiper-class.d.ts | 12 ++ src/types/swiper-options.d.ts | 7 + 18 files changed, 185 insertions(+), 77 deletions(-) diff --git a/src/components/core/breakpoints/setBreakpoint.js b/src/components/core/breakpoints/setBreakpoint.js index 1506c45ec..fa6d7c0db 100644 --- a/src/components/core/breakpoints/setBreakpoint.js +++ b/src/components/core/breakpoints/setBreakpoint.js @@ -9,72 +9,82 @@ export default function setBreakpoint() { // Get breakpoint for window width and update parameters const breakpoint = swiper.getBreakpoint(breakpoints, swiper.params.breakpointsBase, swiper.el); - if (breakpoint && swiper.currentBreakpoint !== breakpoint) { - const breakpointOnlyParams = breakpoint in breakpoints ? breakpoints[breakpoint] : undefined; - if (breakpointOnlyParams) { - [ - 'slidesPerView', - 'spaceBetween', - 'slidesPerGroup', - 'slidesPerGroupSkip', - 'slidesPerColumn', - ].forEach((param) => { - const paramValue = breakpointOnlyParams[param]; - if (typeof paramValue === 'undefined') return; - if (param === 'slidesPerView' && (paramValue === 'AUTO' || paramValue === 'auto')) { - breakpointOnlyParams[param] = 'auto'; - } else if (param === 'slidesPerView') { - breakpointOnlyParams[param] = parseFloat(paramValue); - } else { - breakpointOnlyParams[param] = parseInt(paramValue, 10); - } - }); - } + if (!breakpoint || swiper.currentBreakpoint === breakpoint) return; - const breakpointParams = breakpointOnlyParams || swiper.originalParams; - const wasMultiRow = params.slidesPerColumn > 1; - const isMultiRow = breakpointParams.slidesPerColumn > 1; - if (wasMultiRow && !isMultiRow) { - $el.removeClass( - `${params.containerModifierClass}multirow ${params.containerModifierClass}multirow-column`, - ); - swiper.emitContainerClasses(); - } else if (!wasMultiRow && isMultiRow) { - $el.addClass(`${params.containerModifierClass}multirow`); - if (breakpointParams.slidesPerColumnFill === 'column') { - $el.addClass(`${params.containerModifierClass}multirow-column`); + const breakpointOnlyParams = breakpoint in breakpoints ? breakpoints[breakpoint] : undefined; + if (breakpointOnlyParams) { + [ + 'slidesPerView', + 'spaceBetween', + 'slidesPerGroup', + 'slidesPerGroupSkip', + 'slidesPerColumn', + ].forEach((param) => { + const paramValue = breakpointOnlyParams[param]; + if (typeof paramValue === 'undefined') return; + if (param === 'slidesPerView' && (paramValue === 'AUTO' || paramValue === 'auto')) { + breakpointOnlyParams[param] = 'auto'; + } else if (param === 'slidesPerView') { + breakpointOnlyParams[param] = parseFloat(paramValue); + } else { + breakpointOnlyParams[param] = parseInt(paramValue, 10); } - swiper.emitContainerClasses(); - } + }); + } - const directionChanged = - breakpointParams.direction && breakpointParams.direction !== params.direction; - const needsReLoop = - params.loop && (breakpointParams.slidesPerView !== params.slidesPerView || directionChanged); + const breakpointParams = breakpointOnlyParams || swiper.originalParams; + const wasMultiRow = params.slidesPerColumn > 1; + const isMultiRow = breakpointParams.slidesPerColumn > 1; - if (directionChanged && initialized) { - swiper.changeDirection(); + const wasEnabled = params.enabled; + const isEnabled = breakpointParams.enabled; + + if (wasMultiRow && !isMultiRow) { + $el.removeClass( + `${params.containerModifierClass}multirow ${params.containerModifierClass}multirow-column`, + ); + swiper.emitContainerClasses(); + } else if (!wasMultiRow && isMultiRow) { + $el.addClass(`${params.containerModifierClass}multirow`); + if (breakpointParams.slidesPerColumnFill === 'column') { + $el.addClass(`${params.containerModifierClass}multirow-column`); } + swiper.emitContainerClasses(); + } - extend(swiper.params, breakpointParams); + const directionChanged = + breakpointParams.direction && breakpointParams.direction !== params.direction; + const needsReLoop = + params.loop && (breakpointParams.slidesPerView !== params.slidesPerView || directionChanged); - extend(swiper, { - allowTouchMove: swiper.params.allowTouchMove, - allowSlideNext: swiper.params.allowSlideNext, - allowSlidePrev: swiper.params.allowSlidePrev, - }); + if (directionChanged && initialized) { + swiper.changeDirection(); + } - swiper.currentBreakpoint = breakpoint; + extend(swiper.params, breakpointParams); - swiper.emit('_beforeBreakpoint', breakpointParams); + extend(swiper, { + allowTouchMove: swiper.params.allowTouchMove, + allowSlideNext: swiper.params.allowSlideNext, + allowSlidePrev: swiper.params.allowSlidePrev, + }); - if (needsReLoop && initialized) { - swiper.loopDestroy(); - swiper.loopCreate(); - swiper.updateSlides(); - swiper.slideTo(activeIndex - loopedSlides + swiper.loopedSlides, 0, false); - } + if (wasEnabled && !isEnabled) { + swiper.disable(); + } else if (!wasEnabled && isEnabled) { + swiper.enable(); + } - swiper.emit('breakpoint', breakpointParams); + swiper.currentBreakpoint = breakpoint; + + swiper.emit('_beforeBreakpoint', breakpointParams); + + if (needsReLoop && initialized) { + swiper.loopDestroy(); + swiper.loopCreate(); + swiper.updateSlides(); + swiper.slideTo(activeIndex - loopedSlides + swiper.loopedSlides, 0, false); } + + swiper.emit('breakpoint', breakpointParams); } diff --git a/src/components/core/core-class.js b/src/components/core/core-class.js index bdd9194aa..93697d0a4 100644 --- a/src/components/core/core-class.js +++ b/src/components/core/core-class.js @@ -129,6 +129,7 @@ class Swiper { // Extend Swiper extend(swiper, { + enabled: swiper.params.enabled, el, // Classes @@ -243,6 +244,26 @@ class Swiper { return swiper; } + enable() { + const swiper = this; + if (swiper.enabled) return; + swiper.enabled = true; + if (swiper.params.grabCursor) { + swiper.setGrabCursor(); + } + swiper.emit('enable'); + } + + disable() { + const swiper = this; + if (!swiper.enabled) return; + swiper.enabled = false; + if (swiper.params.grabCursor) { + swiper.unsetGrabCursor(); + } + swiper.emit('disable'); + } + setProgress(progress, speed) { const swiper = this; progress = Math.min(Math.max(progress, 0), 1); @@ -482,7 +503,7 @@ class Swiper { } // Set Grab Cursor - if (swiper.params.grabCursor) { + if (swiper.params.grabCursor && swiper.enabled) { swiper.setGrabCursor(); } @@ -496,9 +517,11 @@ class Swiper { swiper.params.initialSlide + swiper.loopedSlides, 0, swiper.params.runCallbacksOnInit, + false, + true, ); } else { - swiper.slideTo(swiper.params.initialSlide, 0, swiper.params.runCallbacksOnInit); + swiper.slideTo(swiper.params.initialSlide, 0, swiper.params.runCallbacksOnInit, false, true); } // Attach events diff --git a/src/components/core/defaults.js b/src/components/core/defaults.js index a837148f6..68180e6b5 100644 --- a/src/components/core/defaults.js +++ b/src/components/core/defaults.js @@ -8,6 +8,7 @@ export default { updateOnWindowResize: true, resizeObserver: false, nested: false, + enabled: true, // Overrides width: null, diff --git a/src/components/core/events/onClick.js b/src/components/core/events/onClick.js index d22b94148..b042f4839 100644 --- a/src/components/core/events/onClick.js +++ b/src/components/core/events/onClick.js @@ -1,5 +1,6 @@ export default function onClick(e) { const swiper = this; + if (!swiper.enabled) return; if (!swiper.allowClick) { if (swiper.params.preventClicks) e.preventDefault(); if (swiper.params.preventClicksPropagation && swiper.animating) { diff --git a/src/components/core/events/onScroll.js b/src/components/core/events/onScroll.js index 94070953e..8df18fe6b 100644 --- a/src/components/core/events/onScroll.js +++ b/src/components/core/events/onScroll.js @@ -1,6 +1,7 @@ export default function onScroll() { const swiper = this; - const { wrapperEl, rtlTranslate } = swiper; + const { wrapperEl, rtlTranslate, enabled } = swiper; + if (!enabled) return; swiper.previousTranslate = swiper.translate; if (swiper.isHorizontal()) { if (rtlTranslate) { diff --git a/src/components/core/events/onTouchEnd.js b/src/components/core/events/onTouchEnd.js index dac768168..a1acd545c 100644 --- a/src/components/core/events/onTouchEnd.js +++ b/src/components/core/events/onTouchEnd.js @@ -4,7 +4,8 @@ export default function onTouchEnd(event) { const swiper = this; const data = swiper.touchEventsData; - const { params, touches, rtlTranslate: rtl, $wrapperEl, slidesGrid, snapGrid } = swiper; + const { params, touches, rtlTranslate: rtl, $wrapperEl, slidesGrid, snapGrid, enabled } = swiper; + if (!enabled) return; let e = event; if (e.originalEvent) e = e.originalEvent; if (data.allowTouchCallbacks) { diff --git a/src/components/core/events/onTouchMove.js b/src/components/core/events/onTouchMove.js index 310e4222e..983b06e39 100644 --- a/src/components/core/events/onTouchMove.js +++ b/src/components/core/events/onTouchMove.js @@ -6,7 +6,8 @@ export default function onTouchMove(event) { const document = getDocument(); const swiper = this; const data = swiper.touchEventsData; - const { params, touches, rtlTranslate: rtl } = swiper; + const { params, touches, rtlTranslate: rtl, enabled } = swiper; + if (!enabled) return; let e = event; if (e.originalEvent) e = e.originalEvent; if (!data.isTouched) { diff --git a/src/components/core/events/onTouchStart.js b/src/components/core/events/onTouchStart.js index db7343171..6755cc5ed 100644 --- a/src/components/core/events/onTouchStart.js +++ b/src/components/core/events/onTouchStart.js @@ -8,7 +8,8 @@ export default function onTouchStart(event) { const window = getWindow(); const data = swiper.touchEventsData; - const { params, touches } = swiper; + const { params, touches, enabled } = swiper; + if (!enabled) return; if (swiper.animating && params.preventInteractionOnTransition) { return; diff --git a/src/components/core/slide/slideNext.js b/src/components/core/slide/slideNext.js index 551278b2e..1d5cf3e4a 100644 --- a/src/components/core/slide/slideNext.js +++ b/src/components/core/slide/slideNext.js @@ -1,7 +1,8 @@ /* eslint no-unused-vars: "off" */ export default function slideNext(speed = this.params.speed, runCallbacks = true, internal) { const swiper = this; - const { params, animating } = swiper; + const { params, animating, enabled } = swiper; + if (!enabled) return swiper; const increment = swiper.activeIndex < params.slidesPerGroupSkip ? 1 : params.slidesPerGroup; if (params.loop) { if (animating && params.loopPreventsSlide) return false; diff --git a/src/components/core/slide/slidePrev.js b/src/components/core/slide/slidePrev.js index b77a86d23..b0e0cb5d0 100644 --- a/src/components/core/slide/slidePrev.js +++ b/src/components/core/slide/slidePrev.js @@ -1,7 +1,8 @@ /* eslint no-unused-vars: "off" */ export default function slidePrev(speed = this.params.speed, runCallbacks = true, internal) { const swiper = this; - const { params, animating, snapGrid, slidesGrid, rtlTranslate } = swiper; + const { params, animating, snapGrid, slidesGrid, rtlTranslate, enabled } = swiper; + if (!enabled) return swiper; if (params.loop) { if (animating && params.loopPreventsSlide) return false; diff --git a/src/components/core/slide/slideTo.js b/src/components/core/slide/slideTo.js index f32bc252a..fd87f0128 100644 --- a/src/components/core/slide/slideTo.js +++ b/src/components/core/slide/slideTo.js @@ -3,6 +3,7 @@ export default function slideTo( speed = this.params.speed, runCallbacks = true, internal, + initial, ) { if (typeof index !== 'number' && typeof index !== 'string') { throw new Error( @@ -47,8 +48,13 @@ export default function slideTo( activeIndex, rtlTranslate: rtl, wrapperEl, + enabled, } = swiper; - if (swiper.animating && params.preventInteractionOnTransition) { + + if ( + (swiper.animating && params.preventInteractionOnTransition) || + (!enabled && !internal && !initial) + ) { return false; } diff --git a/src/components/keyboard/keyboard.js b/src/components/keyboard/keyboard.js index 9637a6894..6c58c5793 100644 --- a/src/components/keyboard/keyboard.js +++ b/src/components/keyboard/keyboard.js @@ -1,3 +1,4 @@ +/* eslint-disable consistent-return */ import { getWindow, getDocument } from 'ssr-window'; import $ from '../../utils/dom'; import { bindModuleMethods } from '../../utils/utils'; @@ -5,6 +6,7 @@ import { bindModuleMethods } from '../../utils/utils'; const Keyboard = { handle(event) { const swiper = this; + if (!swiper.enabled) return; const window = getWindow(); const document = getDocument(); const { rtlTranslate: rtl } = swiper; diff --git a/src/components/mousewheel/mousewheel.js b/src/components/mousewheel/mousewheel.js index a638cc2ff..ac6e2c853 100644 --- a/src/components/mousewheel/mousewheel.js +++ b/src/components/mousewheel/mousewheel.js @@ -1,3 +1,4 @@ +/* eslint-disable consistent-return */ import { getWindow, getDocument } from 'ssr-window'; import $ from '../../utils/dom'; import { now, nextTick, bindModuleMethods } from '../../utils/utils'; @@ -112,16 +113,19 @@ const Mousewheel = { }, handleMouseEnter() { const swiper = this; + if (!swiper.enabled) return; swiper.mouseEntered = true; }, handleMouseLeave() { const swiper = this; + if (!swiper.enabled) return; swiper.mouseEntered = false; }, handle(event) { let e = event; let disableParentSwiper = true; const swiper = this; + if (!swiper.enabled) return; const params = swiper.params.mousewheel; if (swiper.params.cssMode) { diff --git a/src/components/navigation/navigation.js b/src/components/navigation/navigation.js index 3800e90a0..bca1e8597 100644 --- a/src/components/navigation/navigation.js +++ b/src/components/navigation/navigation.js @@ -21,9 +21,9 @@ const Navigation = { } else { toggleEl($prevEl, false); } - $prevEl[swiper.params.watchOverflow && swiper.isLocked ? 'addClass' : 'removeClass']( - params.lockClass, - ); + if (swiper.params.watchOverflow && swiper.enabled) { + $prevEl[swiper.isLocked ? 'addClass' : 'removeClass'](params.lockClass); + } } if ($nextEl && $nextEl.length > 0) { if (swiper.isEnd) { @@ -31,9 +31,9 @@ const Navigation = { } else { toggleEl($nextEl, false); } - $nextEl[swiper.params.watchOverflow && swiper.isLocked ? 'addClass' : 'removeClass']( - params.lockClass, - ); + if (swiper.params.watchOverflow && swiper.enabled) { + $nextEl[swiper.isLocked ? 'addClass' : 'removeClass'](params.lockClass); + } } }, onPrevClick(e) { @@ -91,6 +91,11 @@ const Navigation = { $prevEl, prevEl: $prevEl && $prevEl[0], }); + + if (!swiper.enabled) { + if ($nextEl) $nextEl.addClass(params.lockClass); + if ($prevEl) $prevEl.addClass(params.lockClass); + } }, destroy() { const swiper = this; @@ -141,6 +146,15 @@ export default { destroy(swiper) { swiper.navigation.destroy(); }, + 'enable disable': (swiper) => { + const { $nextEl, $prevEl } = swiper.navigation; + if ($nextEl) { + $nextEl[swiper.enabled ? 'removeClass' : 'addClass'](swiper.params.navigation.lockClass); + } + if ($prevEl) { + $prevEl[swiper.enabled ? 'removeClass' : 'addClass'](swiper.params.navigation.lockClass); + } + }, click(swiper, e) { const { $nextEl, $prevEl } = swiper.navigation; const targetEl = e.target; diff --git a/src/components/pagination/pagination.js b/src/components/pagination/pagination.js index b513248ae..fa371e5bb 100644 --- a/src/components/pagination/pagination.js +++ b/src/components/pagination/pagination.js @@ -183,9 +183,9 @@ const Pagination = { } else { swiper.emit('paginationUpdate', $el[0]); } - $el[swiper.params.watchOverflow && swiper.isLocked ? 'addClass' : 'removeClass']( - params.lockClass, - ); + if (swiper.params.watchOverflow && swiper.enabled) { + $el[swiper.isLocked ? 'addClass' : 'removeClass'](params.lockClass); + } }, render() { // Render Container @@ -288,6 +288,10 @@ const Pagination = { $el, el: $el[0], }); + + if (!swiper.enabled) { + $el.addClass(params.lockClass); + } }, destroy() { const swiper = this; @@ -382,6 +386,12 @@ export default { destroy(swiper) { swiper.pagination.destroy(); }, + 'enable disable': (swiper) => { + const { $el } = swiper.pagination; + if ($el) { + $el[swiper.enabled ? 'removeClass' : 'addClass'](swiper.params.pagination.lockClass); + } + }, click(swiper, e) { const targetEl = e.target; if ( diff --git a/src/components/scrollbar/scrollbar.js b/src/components/scrollbar/scrollbar.js index 85f3cf15b..d5b41b17a 100644 --- a/src/components/scrollbar/scrollbar.js +++ b/src/components/scrollbar/scrollbar.js @@ -87,9 +87,11 @@ const Scrollbar = { moveDivider, dragSize, }); - scrollbar.$el[swiper.params.watchOverflow && swiper.isLocked ? 'addClass' : 'removeClass']( - swiper.params.scrollbar.lockClass, - ); + if (swiper.params.watchOverflow && swiper.enabled) { + scrollbar.$el[swiper.isLocked ? 'addClass' : 'removeClass']( + swiper.params.scrollbar.lockClass, + ); + } }, getPointerPosition(e) { const swiper = this; @@ -310,6 +312,10 @@ const Scrollbar = { if (params.draggable) { scrollbar.enableDraggable(); } + + if ($el) { + $el[swiper.enabled ? 'removeClass' : 'addClass'](swiper.params.scrollbar.lockClass); + } }, destroy() { const swiper = this; @@ -362,6 +368,12 @@ export default { setTransition(swiper, duration) { swiper.scrollbar.setTransition(duration); }, + 'enable disable': (swiper) => { + const { $el } = swiper.scrollbar; + if ($el) { + $el[swiper.enabled ? 'removeClass' : 'addClass'](swiper.params.scrollbar.lockClass); + } + }, destroy(swiper) { swiper.scrollbar.destroy(); }, diff --git a/src/types/swiper-class.d.ts b/src/types/swiper-class.d.ts index d8111accf..fc18fb536 100644 --- a/src/types/swiper-class.d.ts +++ b/src/types/swiper-class.d.ts @@ -163,6 +163,18 @@ interface Swiper extends SwiperClass { rtlTranslate: boolean; + /** + * Disable Swiper (if it was enabled). When Swiper is disabled, it will hide all navigation elements and won't respond to any events and interractions + * + */ + disable(): void; + + /** + * Enable Swiper (if it was disabled) + * + */ + enabled(): void; + /** * Set Swiper translate progress (from 0 to 1). Where 0 - its initial position (offset) on first slide, and 1 - its maximum position (offset) on last slide * diff --git a/src/types/swiper-options.d.ts b/src/types/swiper-options.d.ts index 77a589949..24b457340 100644 --- a/src/types/swiper-options.d.ts +++ b/src/types/swiper-options.d.ts @@ -29,6 +29,13 @@ export interface SwiperOptions { */ init?: boolean; + /** + * Whether Swiper initially enabled. When Swiper is disabled, it will hide all navigation elements and won't respond to any events and interractions + * + * @default true + */ + enabled?: boolean; + /** * Swiper will recalculate slides position on window resize (orientationchange) *