Browse Source

Reposition the slider for autoWidth.

NaotoshiFujita 3 years ago
parent
commit
996e52ac35

+ 34 - 12
dist/js/splide.js

@@ -306,6 +306,10 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
     return requestAnimationFrame(func);
   }
 
+  function approximatelyEqual(x, y, epsilon) {
+    return Math.abs(x - y) < epsilon;
+  }
+
   function between(number, minOrMax, maxOrMin, exclusive) {
     var min = Math.min(minOrMax, maxOrMin);
     var max = Math.max(minOrMax, maxOrMin);
@@ -1419,19 +1423,17 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
     var shouldSnap = true;
 
     function mount() {
-      on([EVENT_RESIZE, EVENT_RESIZED, EVENT_UPDATED, EVENT_REFRESH], reposition, DEFAULT_EVENT_PRIORITY - 1);
+      on([EVENT_RESIZED, EVENT_UPDATED, EVENT_REFRESH], reposition, DEFAULT_EVENT_PRIORITY - 1);
     }
 
     function reposition() {
-      if (shouldSnap || (shouldSnap = canSnap(getPosition()))) {
+      if (exceededLimit(true)) {
+        translate(getLimit(true));
+      } else if (shouldSnap || (shouldSnap = canSnap())) {
         jump(Splide2.index);
       }
     }
 
-    function canSnap(position) {
-      return abs(position - toPosition(toIndex(position), true)) < SNAP_THRESHOLD;
-    }
-
     function move(dest, index, prev) {
       if (!isBusy()) {
         var position = getPosition();
@@ -1468,7 +1470,7 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
 
     function translate(position) {
       currPosition = loop(position);
-      shouldSnap = canSnap(position);
+      shouldSnap = canSnap(currPosition);
       Components2.Style.ruleBy(list, "transform", "translate" + resolve("X") + "(" + 100 * currPosition / listSize() + "%)");
     }
 
@@ -1498,7 +1500,7 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
 
       for (var i = 0; i < Slides.length; i++) {
         var slideIndex = Slides[i].index;
-        var distance = abs(toPosition(slideIndex) - position);
+        var distance = abs(toPosition(slideIndex, true) - position);
 
         if (distance < minDistance) {
           minDistance = distance;
@@ -1544,6 +1546,11 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
       return max ? toPosition(Components2.Controller.getEnd(), trimming) : toPosition(0, trimming);
     }
 
+    function canSnap(position) {
+      position = isUndefined(position) ? getPosition() : position;
+      return abs(position - toPosition(toIndex(position), true)) < SNAP_THRESHOLD;
+    }
+
     function isBusy() {
       return !!(looping || waiting);
     }
@@ -1656,6 +1663,21 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
     function getAdjacent(prev, destination) {
       var number = perMove || hasFocus() ? 1 : perPage;
       var dest = computeDestIndex(currIndex + number * (prev ? -1 : 1), currIndex);
+
+      if (dest === -1 && Splide2.is(SLIDE)) {
+        var position = Move.getPosition();
+
+        if (prev) {
+          if (!approximatelyEqual(position, 0, 1)) {
+            return 0;
+          }
+        } else {
+          if (!approximatelyEqual(position, Move.getLimit(true), 1)) {
+            return getEnd();
+          }
+        }
+      }
+
       return destination ? dest : loop(dest);
     }
 
@@ -1805,12 +1827,12 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
 
     function listen() {
       var go = Controller.go;
-      on([EVENT_MOUNTED, EVENT_MOVE, EVENT_UPDATED, EVENT_REFRESH, EVENT_SCROLLED], update);
+      on([EVENT_MOUNTED, EVENT_MOVE, EVENT_MOVED, EVENT_UPDATED, EVENT_REFRESH, EVENT_SCROLLED], update);
       bind(next, "click", function () {
-        go(">");
+        go(">", true);
       });
       bind(prev, "click", function () {
-        go("<");
+        go("<", true);
       });
     }
 
@@ -2527,7 +2549,7 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
         });
         var text = !hasFocus() && perPage > 1 ? i18n.pageX : i18n.slideX;
         bind(button, "click", function () {
-          go(">" + i);
+          go(">" + i, true);
         });
         setAttribute(button, ARIA_CONTROLS, controls.join(" "));
         setAttribute(button, ARIA_LABEL, format(text, i + 1));

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


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


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


+ 4 - 4
src/js/components/Arrows/Arrows.ts

@@ -3,7 +3,7 @@ import {
   EVENT_ARROWS_MOUNTED,
   EVENT_ARROWS_UPDATED,
   EVENT_MOUNTED,
-  EVENT_MOVE,
+  EVENT_MOVE, EVENT_MOVED,
   EVENT_REFRESH,
   EVENT_SCROLLED,
   EVENT_UPDATED,
@@ -119,9 +119,9 @@ export function Arrows( Splide: Splide, Components: Components, options: Options
    */
   function listen(): void {
     const { go } = Controller;
-    on( [ EVENT_MOUNTED, EVENT_MOVE, EVENT_UPDATED, EVENT_REFRESH, EVENT_SCROLLED ], update );
-    bind( next, 'click', () => { go( '>' ) } );
-    bind( prev, 'click', () => { go( '<' ) } );
+    on( [ EVENT_MOUNTED, EVENT_MOVE, EVENT_MOVED, EVENT_UPDATED, EVENT_REFRESH, EVENT_SCROLLED ], update );
+    bind( next, 'click', () => { go( '>', true ) } );
+    bind( prev, 'click', () => { go( '<', true ) } );
   }
 
   /**

+ 17 - 2
src/js/components/Controller/Controller.ts

@@ -1,9 +1,9 @@
 import { EVENT_REFRESH, EVENT_SCROLLED, EVENT_UPDATED } from '../../constants/events';
-import { LOOP } from '../../constants/types';
+import { LOOP, SLIDE } from '../../constants/types';
 import { EventInterface } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { BaseComponent, Components, Options } from '../../types';
-import { between, clamp, floor, isString, isUndefined, max } from '../../utils';
+import { approximatelyEqual, between, clamp, floor, isString, isUndefined, max } from '../../utils';
 
 
 /**
@@ -176,6 +176,21 @@ export function Controller( Splide: Splide, Components: Components, options: Opt
   function getAdjacent( prev: boolean, destination?: boolean ): number {
     const number = perMove || hasFocus() ? 1 : perPage;
     const dest   = computeDestIndex( currIndex + number * ( prev ? -1 : 1 ), currIndex );
+
+    if ( dest === -1 && Splide.is( SLIDE ) ) {
+      const position = Move.getPosition();
+
+      if ( prev ) {
+        if ( ! approximatelyEqual( position, 0, 1 ) ) {
+          return 0;
+        }
+      } else {
+        if ( ! approximatelyEqual( position, Move.getLimit( true ), 1 ) ) {
+          return getEnd();
+        }
+      }
+    }
+
     return destination ? dest : loop( dest );
   }
 

+ 20 - 24
src/js/components/Move/Move.ts

@@ -1,18 +1,11 @@
-import {
-  EVENT_MOVE,
-  EVENT_MOVED,
-  EVENT_REFRESH,
-  EVENT_RESIZE,
-  EVENT_RESIZED,
-  EVENT_UPDATED,
-} from '../../constants/events';
+import { EVENT_MOVE, EVENT_MOVED, EVENT_REFRESH, EVENT_RESIZED, EVENT_UPDATED } from '../../constants/events';
 import { DEFAULT_EVENT_PRIORITY } from '../../constants/priority';
 import { IDLE, MOVING } from '../../constants/states';
 import { LOOP, SLIDE } from '../../constants/types';
 import { EventInterface } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { BaseComponent, Components, Options } from '../../types';
-import { abs, clamp, rect } from '../../utils';
+import { abs, clamp, isUndefined, rect } from '../../utils';
 import { SNAP_THRESHOLD } from './constants';
 
 
@@ -75,7 +68,7 @@ export function Move( Splide: Splide, Components: Components, options: Options )
    * Called when the component is mounted.
    */
   function mount(): void {
-    on( [ EVENT_RESIZE, EVENT_RESIZED, EVENT_UPDATED, EVENT_REFRESH ], reposition, DEFAULT_EVENT_PRIORITY - 1 );
+    on( [ EVENT_RESIZED, EVENT_UPDATED, EVENT_REFRESH ], reposition, DEFAULT_EVENT_PRIORITY - 1 );
   }
 
   /**
@@ -83,22 +76,13 @@ export function Move( Splide: Splide, Components: Components, options: Options )
    * This must be called before the Slide component checks the visibility.
    */
   function reposition(): void {
-    if ( shouldSnap || ( shouldSnap = canSnap( getPosition() ) ) ) {
+    if ( exceededLimit( true ) ) {
+      translate( getLimit( true ) );
+    } else if ( shouldSnap || ( shouldSnap = canSnap() ) ) {
       jump( Splide.index );
     }
   }
 
-  /**
-   * Checks if the provided position is enough close to some slide to snap or not.
-   *
-   * @param position - A position to test.
-   *
-   * @return `true` if found the slide to snap, or otherwise `false`.
-   */
-  function canSnap( position: number ): boolean {
-    return abs( position - toPosition( toIndex( position ), true ) ) < SNAP_THRESHOLD;
-  }
-
   /**
    * Goes to the slide at the specified index with the Transition component.
    *
@@ -163,7 +147,7 @@ export function Move( Splide: Splide, Components: Components, options: Options )
    */
   function translate( position: number ): void {
     currPosition = loop( position );
-    shouldSnap   = canSnap( position );
+    shouldSnap   = canSnap( currPosition );
 
     Components.Style.ruleBy(
       list,
@@ -214,7 +198,7 @@ export function Move( Splide: Splide, Components: Components, options: Options )
 
     for ( let i = 0; i < Slides.length; i++ ) {
       const slideIndex = Slides[ i ].index;
-      const distance   = abs( toPosition( slideIndex ) - position );
+      const distance   = abs( toPosition( slideIndex, true ) - position );
 
       if ( distance < minDistance ) {
         minDistance = distance;
@@ -292,6 +276,18 @@ export function Move( Splide: Splide, Components: Components, options: Options )
     return max ? toPosition( Components.Controller.getEnd(), trimming ) : toPosition( 0, trimming );
   }
 
+  /**
+   * Checks if the provided position is enough close to some slide to snap or not.
+   *
+   * @param position - A position to test.
+   *
+   * @return `true` if found the slide to snap, or otherwise `false`.
+   */
+  function canSnap( position?: number ): boolean {
+    position = isUndefined( position ) ? getPosition() : position;
+    return abs( position - toPosition( toIndex( position ), true ) ) < SNAP_THRESHOLD;
+  }
+
   /**
    * Checks if the slider can move now or not.
    *

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

@@ -112,7 +112,7 @@ export function Pagination( Splide: Splide, Components: Components, options: Opt
       const controls = Slides.getIn( i ).map( Slide => Slide.slide.id );
       const text     = ! hasFocus() && perPage > 1 ? i18n.pageX : i18n.slideX;
 
-      bind( button, 'click', () => { go( `>${ i }` ) } );
+      bind( button, 'click', () => { go( `>${ i }`, true ) } );
 
       setAttribute( button, ARIA_CONTROLS, controls.join( ' ' ) );
       setAttribute( button, ARIA_LABEL, format( text, i + 1 ) );

+ 12 - 0
src/js/test/assets/css/styles.css

@@ -14,10 +14,22 @@ img {
   margin: 2rem auto;
 }
 
+.splide__slide.is-visible {
+  border: 2px solid darkgray;
+}
+
 .splide__slide.is-active {
   border: 2px solid deepskyblue;
 }
 
+.splide__pagination__page.is-active {
+  background: deepskyblue;
+}
+
+.splide__arrow:disabled {
+  background: darkorchid;
+}
+
 .splide__progress {
   margin: .5rem 0;
 }

+ 2 - 0
src/js/test/php/examples/autoWidth.php

@@ -26,6 +26,8 @@ $settings = get_settings();
 
       splide01.mount();
 
+      return;
+
       var splide02 = new Splide( '#splide02', {
         // width     : 1000,
         autoWidth : true,

+ 1 - 1
src/js/test/utils/utils.ts

@@ -81,7 +81,7 @@ export function init( options: Options = {}, args: InitArgs = {} ): Splide {
   list.getBoundingClientRect = (): DOMRect => {
     return assign( {}, domRect, {
       width: +width,
-      ...parseTransform( list as HTMLElement, +width, +height )
+      ...parseTransform( list as HTMLElement, +width, +height ),
     } );
   };
 

+ 16 - 0
src/js/utils/math/approximatelyEqual/approximatelyEqual.test.ts

@@ -0,0 +1,16 @@
+import { approximatelyEqual } from './approximatelyEqual';
+
+
+describe( 'approximatelyEqual', () => {
+  test( 'can tell if 2 numbers are approximately equal or not.', () => {
+    expect( approximatelyEqual( 1, 1, 1 ) ).toBe( true );
+    expect( approximatelyEqual( 1, 0.9, 1 ) ).toBe( true );
+    expect( approximatelyEqual( 1, 1.9, 1 ) ).toBe( true );
+
+    expect( approximatelyEqual( 1, 2, 1 ) ).toBe( false );
+    expect( approximatelyEqual( 1, 0, 1 ) ).toBe( false );
+
+    expect( approximatelyEqual( 1, 2, 2 ) ).toBe( true );
+    expect( approximatelyEqual( 1, 0, 2 ) ).toBe( true );
+  } );
+} );

+ 12 - 0
src/js/utils/math/approximatelyEqual/approximatelyEqual.ts

@@ -0,0 +1,12 @@
+/**
+ * Checks if the provided 2 numbers are approximately equal or not.
+ *
+ * @param x       - A number.
+ * @param y       - Another number to compare.
+ * @param epsilon - An accuracy that defines the approximation.
+ *
+ * @return `true` if 2 numbers are considered to be equal, or otherwise `false`.
+ */
+export function approximatelyEqual( x: number, y: number, epsilon: number ): boolean {
+  return Math.abs( x - y ) < epsilon;
+}

+ 4 - 3
src/js/utils/math/index.ts

@@ -1,5 +1,6 @@
-export { between } from './between/between';
-export { clamp }   from './clamp/clamp';
-export { sign }    from './sign/sign';
+export { approximatelyEqual } from './approximatelyEqual/approximatelyEqual';
+export { between }            from './between/between';
+export { clamp }              from './clamp/clamp';
+export { sign }               from './sign/sign';
 
 export const { min, max, floor, ceil, abs, round } = Math;

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