Browse Source

Implement the "overflow" feature.

Naotoshi Fujita 2 years ago
parent
commit
7b29da3420

File diff suppressed because it is too large
+ 0 - 0
dist/js/splide-renderer.min.js.map


+ 26 - 11
dist/js/splide.cjs.js

@@ -433,6 +433,7 @@ var EVENT_DRAGGING = "dragging";
 var EVENT_DRAGGED = "dragged";
 var EVENT_SCROLL = "scroll";
 var EVENT_SCROLLED = "scrolled";
+var EVENT_OVERFLOW = "overflow";
 var EVENT_DESTROY = "destroy";
 var EVENT_ARROWS_MOUNTED = ARROWS + "mounted";
 var EVENT_ARROWS_UPDATED = ARROWS + "updated";
@@ -632,11 +633,11 @@ function Media(Splide2, Components2, options) {
     }
   }
 
-  function set(opts, user) {
+  function set(opts, base, notify) {
     merge(options, opts);
-    user && merge(Object.getPrototypeOf(options), opts);
+    base && merge(Object.getPrototypeOf(options), opts);
 
-    if (!state.is(CREATED)) {
+    if (notify || !state.is(CREATED)) {
       Splide2.emit(EVENT_UPDATED, options);
     }
   }
@@ -1201,6 +1202,7 @@ function Layout(Splide2, Components2, options) {
       styleSlides = Slides.style;
   var vertical;
   var rootRect;
+  var overflow;
 
   function mount() {
     init();
@@ -1228,6 +1230,10 @@ function Layout(Splide2, Components2, options) {
       styleSlides("height", cssSlideHeight(), true);
       rootRect = newRect;
       emit(EVENT_RESIZED);
+
+      if (overflow !== (overflow = isOverflow())) {
+        emit(EVENT_OVERFLOW, overflow);
+      }
     }
   }
 
@@ -1288,7 +1294,7 @@ function Layout(Splide2, Components2, options) {
   }
 
   function sliderSize() {
-    return totalSize(Splide2.length - 1, true) - totalSize(-1, true);
+    return totalSize(Splide2.length - 1) - totalSize(0) + slideSize(0, true);
   }
 
   function getGap() {
@@ -1300,6 +1306,10 @@ function Layout(Splide2, Components2, options) {
     return parseFloat(style(track, resolve("padding" + (right ? "Right" : "Left")))) || 0;
   }
 
+  function isOverflow() {
+    return Splide2.is(FADE) || sliderSize() > listSize();
+  }
+
   return {
     mount: mount,
     listSize: listSize,
@@ -1343,8 +1353,12 @@ function Clones(Splide2, Components2, options) {
   }
 
   function observe() {
-    if (cloneCount < computeCloneCount()) {
-      emit(EVENT_REFRESH);
+    var count = computeCloneCount();
+
+    if (cloneCount !== count) {
+      if (cloneCount < count || !count) {
+        emit(EVENT_REFRESH);
+      }
     }
   }
 
@@ -1379,7 +1393,7 @@ function Clones(Splide2, Components2, options) {
 
     if (!Splide2.is(LOOP)) {
       clones2 = 0;
-    } else if (!clones2) {
+    } else if (isUndefined(clones2)) {
       var fixedSize = options[resolve("fixedWidth")] && Components2.Layout.slideSize(0);
       var fixedCount = fixedSize && ceil(rect(Elements.track)[resolve("width")] / fixedSize);
       clones2 = fixedCount || options[resolve("autoWidth")] && Splide2.length || options.perPage * MULTIPLIER;
@@ -1711,7 +1725,7 @@ function Controller(Splide2, Components2, options) {
   function getEnd() {
     var end = slideCount - (hasFocus() || isLoop && perMove ? 1 : perPage);
 
-    while (compact && --end > 0) {
+    while (compact && end-- > 0) {
       if (toPosition(slideCount - 1, true) !== toPosition(end, true)) {
         end++;
         break;
@@ -2674,9 +2688,9 @@ function Sync(Splide2, Components2, options) {
   var events = [];
 
   function setup() {
-    Splide2.options = {
+    Components2.Media.set({
       slideFocus: isUndefined(slideFocus) ? isNavigation : slideFocus
-    };
+    }, true);
   }
 
   function mount() {
@@ -3141,7 +3155,7 @@ var _Splide = /*#__PURE__*/function () {
       return this._o;
     },
     set: function set(options) {
-      this._C.Media.set(options, true);
+      this._C.Media.set(options, true, true);
     }
   }, {
     key: "length",
@@ -3729,6 +3743,7 @@ exports.EVENT_MOUNTED = EVENT_MOUNTED;
 exports.EVENT_MOVE = EVENT_MOVE;
 exports.EVENT_MOVED = EVENT_MOVED;
 exports.EVENT_NAVIGATION_MOUNTED = EVENT_NAVIGATION_MOUNTED;
+exports.EVENT_OVERFLOW = EVENT_OVERFLOW;
 exports.EVENT_PAGINATION_MOUNTED = EVENT_PAGINATION_MOUNTED;
 exports.EVENT_PAGINATION_UPDATED = EVENT_PAGINATION_UPDATED;
 exports.EVENT_READY = EVENT_READY;

+ 26 - 12
dist/js/splide.esm.js

@@ -428,6 +428,7 @@ var EVENT_DRAGGING = "dragging";
 var EVENT_DRAGGED = "dragged";
 var EVENT_SCROLL = "scroll";
 var EVENT_SCROLLED = "scrolled";
+var EVENT_OVERFLOW = "overflow";
 var EVENT_DESTROY = "destroy";
 var EVENT_ARROWS_MOUNTED = ARROWS + "mounted";
 var EVENT_ARROWS_UPDATED = ARROWS + "updated";
@@ -627,11 +628,11 @@ function Media(Splide2, Components2, options) {
     }
   }
 
-  function set(opts, user) {
+  function set(opts, base, notify) {
     merge(options, opts);
-    user && merge(Object.getPrototypeOf(options), opts);
+    base && merge(Object.getPrototypeOf(options), opts);
 
-    if (!state.is(CREATED)) {
+    if (notify || !state.is(CREATED)) {
       Splide2.emit(EVENT_UPDATED, options);
     }
   }
@@ -1196,6 +1197,7 @@ function Layout(Splide2, Components2, options) {
       styleSlides = Slides.style;
   var vertical;
   var rootRect;
+  var overflow;
 
   function mount() {
     init();
@@ -1223,6 +1225,10 @@ function Layout(Splide2, Components2, options) {
       styleSlides("height", cssSlideHeight(), true);
       rootRect = newRect;
       emit(EVENT_RESIZED);
+
+      if (overflow !== (overflow = isOverflow())) {
+        emit(EVENT_OVERFLOW, overflow);
+      }
     }
   }
 
@@ -1283,7 +1289,7 @@ function Layout(Splide2, Components2, options) {
   }
 
   function sliderSize() {
-    return totalSize(Splide2.length - 1, true) - totalSize(-1, true);
+    return totalSize(Splide2.length - 1) - totalSize(0) + slideSize(0, true);
   }
 
   function getGap() {
@@ -1295,6 +1301,10 @@ function Layout(Splide2, Components2, options) {
     return parseFloat(style(track, resolve("padding" + (right ? "Right" : "Left")))) || 0;
   }
 
+  function isOverflow() {
+    return Splide2.is(FADE) || sliderSize() > listSize();
+  }
+
   return {
     mount: mount,
     listSize: listSize,
@@ -1338,8 +1348,12 @@ function Clones(Splide2, Components2, options) {
   }
 
   function observe() {
-    if (cloneCount < computeCloneCount()) {
-      emit(EVENT_REFRESH);
+    var count = computeCloneCount();
+
+    if (cloneCount !== count) {
+      if (cloneCount < count || !count) {
+        emit(EVENT_REFRESH);
+      }
     }
   }
 
@@ -1374,7 +1388,7 @@ function Clones(Splide2, Components2, options) {
 
     if (!Splide2.is(LOOP)) {
       clones2 = 0;
-    } else if (!clones2) {
+    } else if (isUndefined(clones2)) {
       var fixedSize = options[resolve("fixedWidth")] && Components2.Layout.slideSize(0);
       var fixedCount = fixedSize && ceil(rect(Elements.track)[resolve("width")] / fixedSize);
       clones2 = fixedCount || options[resolve("autoWidth")] && Splide2.length || options.perPage * MULTIPLIER;
@@ -1706,7 +1720,7 @@ function Controller(Splide2, Components2, options) {
   function getEnd() {
     var end = slideCount - (hasFocus() || isLoop && perMove ? 1 : perPage);
 
-    while (compact && --end > 0) {
+    while (compact && end-- > 0) {
       if (toPosition(slideCount - 1, true) !== toPosition(end, true)) {
         end++;
         break;
@@ -2669,9 +2683,9 @@ function Sync(Splide2, Components2, options) {
   var events = [];
 
   function setup() {
-    Splide2.options = {
+    Components2.Media.set({
       slideFocus: isUndefined(slideFocus) ? isNavigation : slideFocus
-    };
+    }, true);
   }
 
   function mount() {
@@ -3136,7 +3150,7 @@ var _Splide = /*#__PURE__*/function () {
       return this._o;
     },
     set: function set(options) {
-      this._C.Media.set(options, true);
+      this._C.Media.set(options, true, true);
     }
   }, {
     key: "length",
@@ -3677,4 +3691,4 @@ var SplideRenderer = /*#__PURE__*/function () {
   return SplideRenderer;
 }();
 
-export { CLASSES, CLASS_ACTIVE, CLASS_ARROW, CLASS_ARROWS, CLASS_ARROW_NEXT, CLASS_ARROW_PREV, CLASS_CLONE, CLASS_CONTAINER, CLASS_FOCUS_IN, CLASS_INITIALIZED, CLASS_LIST, CLASS_LOADING, CLASS_NEXT, CLASS_PAGINATION, CLASS_PAGINATION_PAGE, CLASS_PREV, CLASS_PROGRESS, CLASS_PROGRESS_BAR, CLASS_ROOT, CLASS_SLIDE, CLASS_SPINNER, CLASS_SR, CLASS_TOGGLE, CLASS_TOGGLE_PAUSE, CLASS_TOGGLE_PLAY, CLASS_TRACK, CLASS_VISIBLE, DEFAULTS, EVENT_ACTIVE, EVENT_ARROWS_MOUNTED, EVENT_ARROWS_UPDATED, EVENT_AUTOPLAY_PAUSE, EVENT_AUTOPLAY_PLAY, EVENT_AUTOPLAY_PLAYING, EVENT_CLICK, EVENT_DESTROY, EVENT_DRAG, EVENT_DRAGGED, EVENT_DRAGGING, EVENT_END_INDEX_CHANGED, EVENT_HIDDEN, EVENT_INACTIVE, EVENT_LAZYLOAD_LOADED, EVENT_MOUNTED, EVENT_MOVE, EVENT_MOVED, EVENT_NAVIGATION_MOUNTED, EVENT_PAGINATION_MOUNTED, EVENT_PAGINATION_UPDATED, EVENT_READY, EVENT_REFRESH, EVENT_RESIZE, EVENT_RESIZED, EVENT_SCROLL, EVENT_SCROLLED, EVENT_SHIFTED, EVENT_SLIDE_KEYDOWN, EVENT_UPDATED, EVENT_VISIBLE, EventBinder, EventInterface, FADE, LOOP, LTR, RTL, RequestInterval, SLIDE, STATUS_CLASSES, Splide, SplideRenderer, State, TTB, Throttle, Splide as default };
+export { CLASSES, CLASS_ACTIVE, CLASS_ARROW, CLASS_ARROWS, CLASS_ARROW_NEXT, CLASS_ARROW_PREV, CLASS_CLONE, CLASS_CONTAINER, CLASS_FOCUS_IN, CLASS_INITIALIZED, CLASS_LIST, CLASS_LOADING, CLASS_NEXT, CLASS_PAGINATION, CLASS_PAGINATION_PAGE, CLASS_PREV, CLASS_PROGRESS, CLASS_PROGRESS_BAR, CLASS_ROOT, CLASS_SLIDE, CLASS_SPINNER, CLASS_SR, CLASS_TOGGLE, CLASS_TOGGLE_PAUSE, CLASS_TOGGLE_PLAY, CLASS_TRACK, CLASS_VISIBLE, DEFAULTS, EVENT_ACTIVE, EVENT_ARROWS_MOUNTED, EVENT_ARROWS_UPDATED, EVENT_AUTOPLAY_PAUSE, EVENT_AUTOPLAY_PLAY, EVENT_AUTOPLAY_PLAYING, EVENT_CLICK, EVENT_DESTROY, EVENT_DRAG, EVENT_DRAGGED, EVENT_DRAGGING, EVENT_END_INDEX_CHANGED, EVENT_HIDDEN, EVENT_INACTIVE, EVENT_LAZYLOAD_LOADED, EVENT_MOUNTED, EVENT_MOVE, EVENT_MOVED, EVENT_NAVIGATION_MOUNTED, EVENT_OVERFLOW, EVENT_PAGINATION_MOUNTED, EVENT_PAGINATION_UPDATED, EVENT_READY, EVENT_REFRESH, EVENT_RESIZE, EVENT_RESIZED, EVENT_SCROLL, EVENT_SCROLLED, EVENT_SHIFTED, EVENT_SLIDE_KEYDOWN, EVENT_UPDATED, EVENT_VISIBLE, EventBinder, EventInterface, FADE, LOOP, LTR, RTL, RequestInterval, SLIDE, STATUS_CLASSES, Splide, SplideRenderer, State, TTB, Throttle, Splide as default };

+ 25 - 11
dist/js/splide.js

@@ -429,6 +429,7 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
   var EVENT_DRAGGED = "dragged";
   var EVENT_SCROLL = "scroll";
   var EVENT_SCROLLED = "scrolled";
+  var EVENT_OVERFLOW = "overflow";
   var EVENT_DESTROY = "destroy";
   var EVENT_ARROWS_MOUNTED = ARROWS + "mounted";
   var EVENT_ARROWS_UPDATED = ARROWS + "updated";
@@ -628,11 +629,11 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
       }
     }
 
-    function set(opts, user) {
+    function set(opts, base, notify) {
       merge(options, opts);
-      user && merge(Object.getPrototypeOf(options), opts);
+      base && merge(Object.getPrototypeOf(options), opts);
 
-      if (!state.is(CREATED)) {
+      if (notify || !state.is(CREATED)) {
         Splide2.emit(EVENT_UPDATED, options);
       }
     }
@@ -1194,6 +1195,7 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
         styleSlides = Slides.style;
     var vertical;
     var rootRect;
+    var overflow;
 
     function mount() {
       init();
@@ -1221,6 +1223,10 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
         styleSlides("height", cssSlideHeight(), true);
         rootRect = newRect;
         emit(EVENT_RESIZED);
+
+        if (overflow !== (overflow = isOverflow())) {
+          emit(EVENT_OVERFLOW, overflow);
+        }
       }
     }
 
@@ -1281,7 +1287,7 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
     }
 
     function sliderSize() {
-      return totalSize(Splide2.length - 1, true) - totalSize(-1, true);
+      return totalSize(Splide2.length - 1) - totalSize(0) + slideSize(0, true);
     }
 
     function getGap() {
@@ -1293,6 +1299,10 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
       return parseFloat(style(track, resolve("padding" + (right ? "Right" : "Left")))) || 0;
     }
 
+    function isOverflow() {
+      return Splide2.is(FADE) || sliderSize() > listSize();
+    }
+
     return {
       mount: mount,
       listSize: listSize,
@@ -1336,8 +1346,12 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
     }
 
     function observe() {
-      if (cloneCount < computeCloneCount()) {
-        emit(EVENT_REFRESH);
+      var count = computeCloneCount();
+
+      if (cloneCount !== count) {
+        if (cloneCount < count || !count) {
+          emit(EVENT_REFRESH);
+        }
       }
     }
 
@@ -1372,7 +1386,7 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
 
       if (!Splide2.is(LOOP)) {
         clones2 = 0;
-      } else if (!clones2) {
+      } else if (isUndefined(clones2)) {
         var fixedSize = options[resolve("fixedWidth")] && Components2.Layout.slideSize(0);
         var fixedCount = fixedSize && ceil(rect(Elements.track)[resolve("width")] / fixedSize);
         clones2 = fixedCount || options[resolve("autoWidth")] && Splide2.length || options.perPage * MULTIPLIER;
@@ -1704,7 +1718,7 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
     function getEnd() {
       var end = slideCount - (hasFocus() || isLoop && perMove ? 1 : perPage);
 
-      while (compact && --end > 0) {
+      while (compact && end-- > 0) {
         if (toPosition(slideCount - 1, true) !== toPosition(end, true)) {
           end++;
           break;
@@ -2667,9 +2681,9 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
     var events = [];
 
     function setup() {
-      Splide2.options = {
+      Components2.Media.set({
         slideFocus: isUndefined(slideFocus) ? isNavigation : slideFocus
-      };
+      }, true);
     }
 
     function mount() {
@@ -3134,7 +3148,7 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
         return this._o;
       },
       set: function set(options) {
-        this._C.Media.set(options, true);
+        this._C.Media.set(options, true, true);
       }
     }, {
       key: "length",

File diff suppressed because it is too large
+ 0 - 0
dist/js/splide.min.js


BIN
dist/js/splide.min.js.gz


File diff suppressed because it is too large
+ 0 - 0
dist/js/splide.min.js.map


+ 4 - 2
dist/types/index.d.ts

@@ -6,7 +6,7 @@
 interface MediaComponent extends BaseComponent {
     /** @internal */
     reduce(reduced: boolean): void;
-    set(options: Options, userOptions?: boolean): void;
+    set(options: Options, base?: boolean, notify?: boolean): void;
 }
 
 /**
@@ -725,6 +725,7 @@ interface EventMap {
     'dragged': () => void;
     'scroll': () => void;
     'scrolled': () => void;
+    'overflow': (overflow: boolean) => void;
     'destroy': () => void;
     'arrows:mounted': (prev: HTMLButtonElement, next: HTMLButtonElement) => void;
     'arrows:updated': (prev: HTMLButtonElement, next: HTMLButtonElement, prevIndex: number, nextIndex: number) => void;
@@ -1570,6 +1571,7 @@ declare const EVENT_DRAGGING = "dragging";
 declare const EVENT_DRAGGED = "dragged";
 declare const EVENT_SCROLL = "scroll";
 declare const EVENT_SCROLLED = "scrolled";
+declare const EVENT_OVERFLOW = "overflow";
 declare const EVENT_DESTROY = "destroy";
 declare const EVENT_ARROWS_MOUNTED: string;
 declare const EVENT_ARROWS_UPDATED: string;
@@ -1674,4 +1676,4 @@ declare const LOOP = "loop";
  */
 declare const FADE = "fade";
 
-export { AnyFunction, ArrowsComponent, AutoplayComponent, BaseComponent, CLASSES, CLASS_ACTIVE, CLASS_ARROW, CLASS_ARROWS, CLASS_ARROW_NEXT, CLASS_ARROW_PREV, CLASS_CLONE, CLASS_CONTAINER, CLASS_FOCUS_IN, CLASS_INITIALIZED, CLASS_LIST, CLASS_LOADING, CLASS_NEXT, CLASS_PAGINATION, CLASS_PAGINATION_PAGE, CLASS_PREV, CLASS_PROGRESS, CLASS_PROGRESS_BAR, CLASS_ROOT, CLASS_SLIDE, CLASS_SPINNER, CLASS_SR, CLASS_TOGGLE, CLASS_TOGGLE_PAUSE, CLASS_TOGGLE_PLAY, CLASS_TRACK, CLASS_VISIBLE, Cast, ClonesComponent, ComponentConstructor, Components, ControllerComponent, CoverComponent, DEFAULTS, DirectionComponent, DragComponent, EVENT_ACTIVE, EVENT_ARROWS_MOUNTED, EVENT_ARROWS_UPDATED, EVENT_AUTOPLAY_PAUSE, EVENT_AUTOPLAY_PLAY, EVENT_AUTOPLAY_PLAYING, EVENT_CLICK, EVENT_DESTROY, EVENT_DRAG, EVENT_DRAGGED, EVENT_DRAGGING, EVENT_END_INDEX_CHANGED, EVENT_HIDDEN, EVENT_INACTIVE, EVENT_LAZYLOAD_LOADED, EVENT_MOUNTED, EVENT_MOVE, EVENT_MOVED, EVENT_NAVIGATION_MOUNTED, EVENT_PAGINATION_MOUNTED, EVENT_PAGINATION_UPDATED, EVENT_READY, EVENT_REFRESH, EVENT_RESIZE, EVENT_RESIZED, EVENT_SCROLL, EVENT_SCROLLED, EVENT_SHIFTED, EVENT_SLIDE_KEYDOWN, EVENT_UPDATED, EVENT_VISIBLE, ElementsComponent, EventBinder, EventBinderObject, EventInterface, EventInterfaceObject, EventMap, FADE, Head, KeyboardComponent, LOOP, LTR, LayoutComponent, LazyLoadComponent, LiveComponent, MediaComponent, MoveComponent, Options, PaginationComponent, PaginationData, PaginationItem, Push, RTL, RequestInterval, RequestIntervalInterface, Resolve, ResponsiveOptions, SLIDE, STATUS_CLASSES, ScrollComponent, Shift, ShiftN, SlideComponent, SlidesComponent, Splide, SplideRenderer, State, StateObject, SyncComponent, SyncTarget, TTB, Throttle, ThrottleInstance, TransitionComponent, WheelComponent, Splide as default };
+export { AnyFunction, ArrowsComponent, AutoplayComponent, BaseComponent, CLASSES, CLASS_ACTIVE, CLASS_ARROW, CLASS_ARROWS, CLASS_ARROW_NEXT, CLASS_ARROW_PREV, CLASS_CLONE, CLASS_CONTAINER, CLASS_FOCUS_IN, CLASS_INITIALIZED, CLASS_LIST, CLASS_LOADING, CLASS_NEXT, CLASS_PAGINATION, CLASS_PAGINATION_PAGE, CLASS_PREV, CLASS_PROGRESS, CLASS_PROGRESS_BAR, CLASS_ROOT, CLASS_SLIDE, CLASS_SPINNER, CLASS_SR, CLASS_TOGGLE, CLASS_TOGGLE_PAUSE, CLASS_TOGGLE_PLAY, CLASS_TRACK, CLASS_VISIBLE, Cast, ClonesComponent, ComponentConstructor, Components, ControllerComponent, CoverComponent, DEFAULTS, DirectionComponent, DragComponent, EVENT_ACTIVE, EVENT_ARROWS_MOUNTED, EVENT_ARROWS_UPDATED, EVENT_AUTOPLAY_PAUSE, EVENT_AUTOPLAY_PLAY, EVENT_AUTOPLAY_PLAYING, EVENT_CLICK, EVENT_DESTROY, EVENT_DRAG, EVENT_DRAGGED, EVENT_DRAGGING, EVENT_END_INDEX_CHANGED, EVENT_HIDDEN, EVENT_INACTIVE, EVENT_LAZYLOAD_LOADED, EVENT_MOUNTED, EVENT_MOVE, EVENT_MOVED, EVENT_NAVIGATION_MOUNTED, EVENT_OVERFLOW, EVENT_PAGINATION_MOUNTED, EVENT_PAGINATION_UPDATED, EVENT_READY, EVENT_REFRESH, EVENT_RESIZE, EVENT_RESIZED, EVENT_SCROLL, EVENT_SCROLLED, EVENT_SHIFTED, EVENT_SLIDE_KEYDOWN, EVENT_UPDATED, EVENT_VISIBLE, ElementsComponent, EventBinder, EventBinderObject, EventInterface, EventInterfaceObject, EventMap, FADE, Head, KeyboardComponent, LOOP, LTR, LayoutComponent, LazyLoadComponent, LiveComponent, MediaComponent, MoveComponent, Options, PaginationComponent, PaginationData, PaginationItem, Push, RTL, RequestInterval, RequestIntervalInterface, Resolve, ResponsiveOptions, SLIDE, STATUS_CLASSES, ScrollComponent, Shift, ShiftN, SlideComponent, SlidesComponent, Splide, SplideRenderer, State, StateObject, SyncComponent, SyncTarget, TTB, Throttle, ThrottleInstance, TransitionComponent, WheelComponent, Splide as default };

+ 8 - 4
src/js/components/Clones/Clones.ts

@@ -3,7 +3,7 @@ import { LOOP } from '../../constants/types';
 import { EventInterface } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { BaseComponent, Components, Options } from '../../types';
-import { addClass, append, before, ceil, empty, pad, push, rect, remove } from '../../utils';
+import { addClass, append, before, ceil, empty, isUndefined, pad, push, rect, remove } from '../../utils';
 
 
 /**
@@ -79,8 +79,12 @@ export function Clones( Splide: Splide, Components: Components, options: Options
    * Observes the required clone count and refreshes the slider if necessary.
    */
   function observe(): void {
-    if ( cloneCount < computeCloneCount() ) {
-      emit( EVENT_REFRESH );
+    const count = computeCloneCount();
+
+    if ( cloneCount !== count ) {
+      if ( cloneCount < count || ! count ) {
+        emit( EVENT_REFRESH );
+      }
     }
   }
 
@@ -134,7 +138,7 @@ export function Clones( Splide: Splide, Components: Components, options: Options
 
     if ( ! Splide.is( LOOP ) ) {
       clones = 0;
-    } else if ( ! clones ) {
+    } else if ( isUndefined( clones ) ) {
       const fixedSize  = options[ resolve( 'fixedWidth' ) ] && Components.Layout.slideSize( 0 );
       const fixedCount = fixedSize && ceil( rect( Elements.track )[ resolve( 'width' ) ] / fixedSize );
       clones = fixedCount || ( options[ resolve( 'autoWidth' ) ] && Splide.length ) || options.perPage * MULTIPLIER;

+ 31 - 4
src/js/components/Layout/Layout.ts

@@ -1,9 +1,17 @@
 import { TTB } from '../../constants/directions';
-import { EVENT_REFRESH, EVENT_RESIZE, EVENT_RESIZED, EVENT_UPDATED } from '../../constants/events';
+import {
+  EVENT_MOUNTED,
+  EVENT_OVERFLOW,
+  EVENT_REFRESH,
+  EVENT_RESIZE,
+  EVENT_RESIZED,
+  EVENT_UPDATED,
+} from '../../constants/events';
 import { EventInterface, Throttle } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { BaseComponent, Components, Options } from '../../types';
-import { abs, apply, assert, isObject, rect, style, unit } from '../../utils';
+import { abs, apply, assert, isObject, nextTick, rect, style, unit } from '../../utils';
+import { FADE, SLIDE } from '../../constants/types';
 
 
 /**
@@ -20,7 +28,7 @@ export interface LayoutComponent extends BaseComponent {
 }
 
 /**
- * The component that layouts slider components and provides methods for dimensions.
+ * The component that adjusts slider styles and provides methods for dimensions.
  *
  * @since 3.0.0
  *
@@ -47,6 +55,11 @@ export function Layout( Splide: Splide, Components: Components, options: Options
    */
   let rootRect: DOMRect;
 
+  /**
+   * Turns into `true` when the carousel is wider than the list.
+   */
+  let overflow: boolean;
+
   /**
    * Called when the component is mounted.
    */
@@ -87,6 +100,10 @@ export function Layout( Splide: Splide, Components: Components, options: Options
 
       rootRect = newRect;
       emit( EVENT_RESIZED );
+
+      if ( overflow !== ( overflow = isOverflow() ) ) {
+        emit( EVENT_OVERFLOW, overflow );
+      }
     }
   }
 
@@ -210,7 +227,7 @@ export function Layout( Splide: Splide, Components: Components, options: Options
    * @return The width or height of the slider without clones.
    */
   function sliderSize(): number {
-    return totalSize( Splide.length - 1, true ) - totalSize( -1, true );
+    return totalSize( Splide.length - 1 ) - totalSize( 0 ) + slideSize( 0, true );
   }
 
   /**
@@ -235,6 +252,16 @@ export function Layout( Splide: Splide, Components: Components, options: Options
     return parseFloat( style( track, resolve( `padding${ right ? 'Right' : 'Left' }` ) ) ) || 0;
   }
 
+  /**
+   * Checks if the carousel is wider than the list.
+   * This method always returns `true` for a fade carousel.
+   *
+   * @return `true` if the carousel is wider than the list, or otherwise `false`.
+   */
+  function isOverflow(): boolean {
+    return Splide.is( FADE ) || sliderSize() > listSize();
+  }
+
   return {
     mount,
     listSize,

+ 9 - 7
src/js/components/Media/Media.ts

@@ -15,7 +15,7 @@ import { EVENT_UPDATED } from '../../constants/events';
 export interface MediaComponent extends BaseComponent {
   /** @internal */
   reduce( reduced: boolean ): void;
-  set( options: Options, userOptions?: boolean ): void;
+  set( options: Options, base?: boolean, notify?: boolean ): void;
 }
 
 /**
@@ -118,18 +118,20 @@ export function Media( Splide: Splide, Components: Components, options: Options
   }
 
   /**
-   * Sets options.
+   * Sets current options or base options (prototype).
+   * If changing base options, always emits the `updated` event.
    *
    * @internal
    *
-   * @param opts - New options.
-   * @param user - Optional. Determines whether to also update user options or not.
+   * @param opts   - New options.
+   * @param base   - Optional. Determines whether to also update base options or not.
+   * @param notify - Optional. If `true`, always emits the `update` event.
    */
-  function set( opts: Options, user?: boolean ): void {
+  function set( opts: Options, base?: boolean, notify?: boolean ): void {
     merge( options, opts );
-    user && merge( Object.getPrototypeOf( options ), opts );
+    base && merge( Object.getPrototypeOf( options ), opts );
 
-    if ( ! state.is( CREATED ) ) {
+    if ( notify || ! state.is( CREATED ) ) {
       Splide.emit( EVENT_UPDATED, options );
     }
   }

+ 1 - 1
src/js/components/Sync/Sync.ts

@@ -56,7 +56,7 @@ export function Sync( Splide: Splide, Components: Components, options: Options )
    * Called when the component is constructed.
    */
   function setup(): void {
-    Splide.options = { slideFocus: isUndefined( slideFocus ) ? isNavigation : slideFocus };
+    Components.Media.set( { slideFocus: isUndefined( slideFocus ) ? isNavigation : slideFocus }, true );
   }
 
   /**

+ 1 - 0
src/js/constants/events.ts

@@ -20,6 +20,7 @@ export const EVENT_DRAGGING           = 'dragging';
 export const EVENT_DRAGGED            = 'dragged';
 export const EVENT_SCROLL             = 'scroll';
 export const EVENT_SCROLLED           = 'scrolled';
+export const EVENT_OVERFLOW           = 'overflow';
 export const EVENT_DESTROY            = 'destroy';
 export const EVENT_ARROWS_MOUNTED     = `${ ARROWS }mounted`;
 export const EVENT_ARROWS_UPDATED     = `${ ARROWS }updated`;

+ 1 - 1
src/js/core/Splide/Splide.ts

@@ -379,7 +379,7 @@ export class Splide {
    * @param options - An object with new options.
    */
   set options( options: Options ) {
-    this._C.Media.set( options, true );
+    this._C.Media.set( options, true, true );
   }
 
   /**

+ 75 - 0
src/js/test/php/examples/overflow.php

@@ -0,0 +1,75 @@
+<?php
+require_once '../parts.php';
+require_once '../settings.php';
+
+$settings = get_settings();
+?>
+
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width,initial-scale=1">
+  <title>Overflow</title>
+
+  <link rel="stylesheet" href="../../../../../dist/css/themes/splide-<?php echo $settings['theme'] ?>.min.css">
+  <link rel="stylesheet" href="../../assets/css/styles.css">
+  <script src="../../../../../dist/js/splide.js"></script>
+
+  <script>
+    document.addEventListener( 'DOMContentLoaded', function () {
+      var splide01 = new Splide( '#splide01', {
+        fixedWidth : '6rem',
+        gap        : 10,
+      } );
+
+      splide01.on( 'overflow', overflow => {
+        if ( overflow ) {
+          console.log( 'splide01: overflow' );
+          splide01.root.classList.add( 'is-overflow' );
+          splide01.options = { arrows: true, pagination: true, drag: true };
+        } else {
+          console.log( 'splide01: not overflow' );
+          splide01.root.classList.remove( 'is-overflow' );
+          splide01.options = { arrows: false, pagination: false, drag: false };
+        }
+      } );
+
+      splide01.mount();
+
+      var splide02 = new Splide( '#splide02', {
+        type      : 'loop',
+        // gap       : '1rem',
+        fixedWidth: 100,
+      } );
+
+      splide02.on( 'overflow', overflow => {
+        if ( overflow ) {
+          console.log( 'splide02: overflow' );
+          splide02.root.classList.add( 'is-overflow' );
+          splide02.options = { clones: undefined, arrows: true, pagination: true, drag: true };
+        } else {
+          console.log( 'splide02: not overflow' );
+          splide02.go( 0 );
+          splide02.root.classList.remove( 'is-overflow' );
+          splide02.options = { clones: 0, arrows: false, pagination: false, drag: false };
+        }
+      } );
+
+      splide02.mount();
+    } );
+  </script>
+
+  <style>
+    .splide:not( .is-overflow ) .splide__list {
+      justify-content: center;
+    }
+  </style>
+</head>
+<body>
+
+<?php render( 'splide01' ); ?>
+<?php render( 'splide02' ); ?>
+
+</body>
+</html>

+ 1 - 0
src/js/types/events.ts

@@ -28,6 +28,7 @@ export interface EventMap {
   'dragged': () => void;
   'scroll': () => void;
   'scrolled': () => void;
+  'overflow': ( overflow: boolean ) => void;
   'destroy': () => void;
   'arrows:mounted': ( prev: HTMLButtonElement, next: HTMLButtonElement ) => void;
   'arrows:updated': ( prev: HTMLButtonElement, next: HTMLButtonElement, prevIndex: number, nextIndex: number ) => void;

Some files were not shown because too many files changed in this diff