]> git.ipfire.org Git - thirdparty/foundation/foundation-sites.git/commitdiff
Force overlap transition for nested off-canvas elements to avoid transform scope...
authorKai Falkowski <kai.falkowski@gmail.com>
Mon, 12 Jun 2017 05:04:57 +0000 (07:04 +0200)
committerKai Falkowski <kai.falkowski@gmail.com>
Mon, 12 Jun 2017 05:04:57 +0000 (07:04 +0200)
Using position fixed within a transform scope doesn't work and fixedPositionEmulation + position calculations don't work reliably yet.
Therefore I'm forcing overlap transition when nesting (at the moment).

docs/pages/off-canvas.md
js/foundation.offcanvas.js
scss/components/_off-canvas.scss
scss/settings/_settings.scss

index 0424ca5a07709bd75404463ae75e3ea84ee62d7d..5d3514445882750ab72a80de0eea228beea3d9e4 100644 (file)
@@ -358,10 +358,10 @@ In v6.4 the off-canvas component has been heavily extended. Apart from the <a hr
 Another improvement is the support of several off-canvas elements that share the same position e.g. two elements with `position-left`.
 
 Advanced off-canvas users may use the new `contentId` option to bind an element to a content. This lets you place the element much more flexibly as it may be a sibling of the content, a child or none of it.<br>
-<strong>Important:</strong> when using the `contentId` on a nested element it's required to also use the new `nested` option and tell the JavaScript if it's nested or not!
+<strong>Important:</strong> when using the `contentId` on a nested element you must also use the new `nested` option and tell the JavaScript it's nested!
 
-<div class="callout">
-  When you are facing a <a href="https://en.wikipedia.org/wiki/Flash_of_unstyled_content" target="_blank">FOUC</a> issue you can try to add the class `is-closed` to the element which is usually added and removed automatically via JavaScript. This will cover the delay in loading.
+<div class="callout warning">
+  Please note that it's currently not possible to use the push transition for a nested off-canvas element.
 </div>
 
 ```html_example
@@ -375,7 +375,10 @@ Advanced off-canvas users may use the new `contentId` option to bind an element
 <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
 
 <div class="off-canvas position-left is-closed" id="offCanvasNestedPush" data-off-canvas>
-  <div class="callout">I'm a nested off-canvas that mustn't be a sibling of the off-canvas content anymore.</div>
+  <div class="callout">
+    <p>I'm a nested off-canvas that mustn't be a sibling of the off-canvas content anymore.</p>
+    <p>Since push transition is currently not possible for nested elements, I'm forced to use overlayp transition.</p>
+  </div>
 </div>
 <div class="off-canvas position-left is-closed" data-transition="overlap" id="offCanvasNestedOverlap" data-off-canvas>
   <div class="callout">I'm a nested off-canvas that uses overlap transition and the same position as the other nested off-canvas.</div>
index 6544c27fb0ee8e4038b31543119328fb95ad23de..77cdc4ec50e28039f0d7f78bbb6656899e7c1f01 100644 (file)
@@ -32,8 +32,6 @@ class OffCanvas extends Plugin {
     this.position = 'left';
     this.$content = $();
     this.nested = !!(this.options.nested);
-    this.$relativeParent;
-    this.relativeScope = false;
 
     //Triggers init is idempotent, just need to make sure it is initialized
     Triggers.init($);
@@ -57,17 +55,6 @@ class OffCanvas extends Plugin {
 
     this.$element.attr('aria-hidden', 'true');
 
-    this.$element.addClass(`is-transition-${this.options.transition} is-closed`);
-
-    // Find triggers that affect this element and add aria-expanded to them
-    this.$triggers = $(document)
-      .find('[data-open="'+id+'"], [data-close="'+id+'"], [data-toggle="'+id+'"]')
-      .attr('aria-expanded', 'false')
-      .attr('aria-controls', id);
-    
-    // Get position by checking for related CSS class
-    this.position = this.$element.is('.position-left, .position-top, .position-right, .position-bottom') ? this.$element.attr('class').match(/position\-(left|top|right|bottom)/)[1] : this.position;
-
     // Find off-canvas content, either by ID (if specified), by siblings or by closest selector (fallback)
     if (this.options.contentId) {
       this.$content = $('#'+this.options.contentId);
@@ -86,15 +73,26 @@ class OffCanvas extends Plugin {
       console.warn('Remember to use the nested option if using the content ID option!');
     }
 
-    // For a nested element, find closest (relative) positioned element
-    if (this.nested) {
-      this.$relativeParent = this.$element.offsetParent();
-      // Initial adjustment of the element according to local scope
-      this._adjustToLocalScope();
+    if (this.nested === true) {
+      // Force transition overlap if nested
+      this.options.transition = 'overlap';
+      // Remove appropriate classes if already assigned in markup
+      this.$element.removeClass('is-transition-push');
+      this.$content.removeClass('has-transition-push');
     }
 
+    this.$element.addClass(`is-transition-${this.options.transition} is-closed`);
     this.$content.addClass(`has-transition-${this.options.transition}`);
 
+    // Find triggers that affect this element and add aria-expanded to them
+    this.$triggers = $(document)
+      .find('[data-open="'+id+'"], [data-close="'+id+'"], [data-toggle="'+id+'"]')
+      .attr('aria-expanded', 'false')
+      .attr('aria-controls', id);
+    
+    // Get position by checking for related CSS class
+    this.position = this.$element.is('.position-left, .position-top, .position-right, .position-bottom') ? this.$element.attr('class').match(/position\-(left|top|right|bottom)/)[1] : this.position;
+
     // Add an overlay over the content if necessary
     if (this.options.contentOverlay === true) {
       var overlay = document.createElement('div');
@@ -120,8 +118,6 @@ class OffCanvas extends Plugin {
     }
 
     this._setContentClasses();
-
-    this._emulateFixedPosition(true);
   }
 
   /**
@@ -130,8 +126,6 @@ class OffCanvas extends Plugin {
    * @private
    */
   _events() {
-    var id = this.$element.attr('id');
-
     this.$element.off('.zf.trigger .zf.offcanvas').on({
       'open.zf.trigger': this.open.bind(this),
       'close.zf.trigger': this.close.bind(this),
@@ -143,18 +137,6 @@ class OffCanvas extends Plugin {
       var $target = this.options.contentOverlay ? this.$overlay : this.$content;
       $target.on({'click.zf.offcanvas': this.close.bind(this)});
     }
-
-    var resizeListener = this.resizeListener = `resize.zf.${id}`;
-    var resizeTimeout;
-
-    $(window).off(resizeListener).on(resizeListener, () => {
-      // Delay as timeout to prevent too many calls on resizing
-      clearTimeout(resizeTimeout);
-      resizeTimeout = setTimeout(() => {
-        this._adjustToLocalScope();
-        this._emulateFixedPosition(true);
-      }, 250);
-    });
   }
 
   /**
@@ -194,71 +176,6 @@ class OffCanvas extends Plugin {
     }
   }
 
-  /**
-   * Adjust element's left position if relative parent is affecting local coords (left).
-   * This is required if using a nested element within a push/pull column.
-   * @private
-   */
-  _adjustToLocalScope() {
-    if (this.nested) {
-      // Check if relative parent is affecting local coords
-      if (parseInt(this.$relativeParent.css('left')) > 0 || this.$relativeParent.offset().top > 0) {
-        this.relativeScope = true;
-        // Adjust element to local scope
-        let xOffset = Math.ceil(parseFloat(this.$relativeParent.css('left'))) * (-1);
-        switch (this.position) {
-          case 'top':
-            this.$element.css('left', xOffset).width($(window).width());
-            break;
-          case 'bottom':
-            this.$element.css('left', xOffset).width($(window).width());
-            break;
-          case 'left':
-            this.$element.css('left', xOffset);
-            break;
-        }
-      } else {
-        this.relativeScope = false;
-        // Reset adjustment
-        this.$element.css({
-          'left': '',
-          'width': ''
-        });
-      }
-    }
-  }
-
-  /**
-   * Emulate fixed position for a nested off-canvas element by using fixed height and current document scrollTop.
-   * This is necessary due to the transform property of the content that creates a new local coordinate system.
-   * @param  {Boolean} resetHeight - true if the element's height should be set as well (mostly on init and window resize)
-   * @private
-   */
-  _emulateFixedPosition(resetHeight = false) {
-    
-    if (this.nested === true) {
-
-      var topOffset = this.relativeScope ? this.$relativeParent.offset().top : 0;
-
-      if (this.position === 'top') {
-        
-        this.$element.css('top', $(document).scrollTop() - topOffset);
-
-      } else if (this.position === 'bottom') {
-
-        this.$element.css('top', $(document).scrollTop() + $(window).height() - this.$element.outerHeight() - topOffset);
-
-      } else { // left|right
-
-        this.$element.css('top', $(document).scrollTop() - topOffset);
-      
-        if (resetHeight === true) {
-          this.$element.height( $(window).height() );
-        }
-      }
-    }
-  }
-
   /**
    * Handles the revealing/hiding the off-canvas at breakpoints, not the same as open.
    * @param {Boolean} isRevealed - true if element should be revealed.
@@ -357,9 +274,6 @@ class OffCanvas extends Plugin {
       this.$element.siblings('[data-off-canvas-content]').css('transition-duration', '');
     }
 
-    this._adjustToLocalScope();
-    this._emulateFixedPosition();
-
     /**
      * Fires when the off-canvas menu opens.
      * @event OffCanvas#opened
@@ -373,8 +287,7 @@ class OffCanvas extends Plugin {
     this.$content.addClass('is-open-' + this.position);
 
     // If `contentScroll` is set to false, add class and disable scrolling on touch devices.
-    // If off-canvas element is nested, force `contentScroll` false.
-    if (this.options.contentScroll === false || this.nested === true) {
+    if (this.options.contentScroll === false) {
       $('body').addClass('is-off-canvas-open').on('touchmove', this._stopScrolling);
       this.$element.on('touchstart', this._recordScrollable);
       this.$element.on('touchmove', this._stopScrollPropagation);
@@ -433,8 +346,7 @@ class OffCanvas extends Plugin {
     this.$content.removeClass('is-open-left is-open-top is-open-right is-open-bottom');
 
     // If `contentScroll` is set to false, remove class and re-enable scrolling on touch devices.
-    // // If off-canvas element is nested, force `contentScroll` false.
-    if (this.options.contentScroll === false || this.nested === true) {
+    if (this.options.contentScroll === false) {
       $('body').removeClass('is-off-canvas-open').off('touchmove', this._stopScrolling);
       this.$element.off('touchstart', this._recordScrollable);
       this.$element.off('touchmove', this._stopScrollPropagation);
@@ -507,7 +419,6 @@ class OffCanvas extends Plugin {
     this.close();
     this.$element.off('.zf.trigger .zf.offcanvas');
     this.$overlay.off('.zf.offcanvas');
-    $(window).off(this.resizeListener);
   }
 }
 
index f5aa2dfc3500fc3aa579f0f2153558d4daf6a284..90498b5fda2159bca7e00ae5e5d90103d26a1851 100644 (file)
@@ -32,19 +32,19 @@ $offcanvas-inner-shadow-color: rgba($black, 0.25) !default;
 
 /// Z-index of an off-canvas content overlay.
 /// @type Number
-$offcanvas-overlay-zindex: 1 !default;
+$offcanvas-overlay-zindex: 11 !default;
 
 /// Z-index of an off-canvas panel with the `push` transition.
 /// @type Number
-$offcanvas-push-zindex: 2 !default;
+$offcanvas-push-zindex: 12 !default;
 
 /// Z-index of an off-canvas panel with the `overlap` transition.
 /// @type Number
-$offcanvas-overlap-zindex: 10 !default;
+$offcanvas-overlap-zindex: 13 !default;
 
 /// Z-index of an off-canvas panel using the `reveal-for-*` classes or mixin.
 /// @type Number
-$offcanvas-reveal-zindex: 2 !default;
+$offcanvas-reveal-zindex: 12 !default;
 
 /// Length of the animation on an off-canvas panel.
 /// @type Number
@@ -70,9 +70,9 @@ $maincontent-class: 'off-canvas-content' !default;
 
   // Checks the z-indexes and increase them due to backwards compatibility.
   // This is necessary because the overlay's z-index is new since v6.4 and may be identical to the user custom settings of the push z-index.
-  @if $offcanvas-push-zindex <= $offcanvas-overlay-zindex { $offcanvas-push-zindex: $offcanvas-push-zindex + 1 !global; }
-  @if $offcanvas-overlap-zindex <= $offcanvas-overlay-zindex { $offcanvas-overlap-zindex: $offcanvas-overlap-zindex + 1 !global; }
-  @if $offcanvas-reveal-zindex <= $offcanvas-overlay-zindex { $offcanvas-reveal-zindex: $offcanvas-reveal-zindex + 1 !global; }
+  @if $offcanvas-push-zindex <= $offcanvas-overlay-zindex { $offcanvas-push-zindex: $offcanvas-overlay-zindex + 1 !global; }
+  @if $offcanvas-overlap-zindex <= $offcanvas-push-zindex { $offcanvas-overlap-zindex: $offcanvas-push-zindex + 1 !global; }
+  @if $offcanvas-reveal-zindex <= $offcanvas-overlay-zindex { $offcanvas-reveal-zindex: $offcanvas-overlay-zindex + 1 !global; }
 
   // Hides overflow on body when an off-canvas panel is open.
   .is-off-canvas-open {
@@ -140,7 +140,7 @@ $maincontent-class: 'off-canvas-content' !default;
 
   // Set the off-canvas z-index.
   // Since the push z-index may be set by custom settings, a condition makes sure it is greater than the overlay's z-index (backwards compatibility).
-  z-index: if($offcanvas-push-zindex > $offcanvas-overlay-zindex, $offcanvas-push-zindex, $offcanvas-overlay-zindex + 1);
+  z-index: $offcanvas-push-zindex;
 
   transition: transform $transition;
   backface-visibility: hidden;
@@ -154,7 +154,7 @@ $maincontent-class: 'off-canvas-content' !default;
 
   // Overlap only styles.
   &.is-transition-overlap {
-    z-index: if($offcanvas-overlap-zindex > $offcanvas-overlay-zindex , $offcanvas-overlap-zindex, $offcanvas-overlay-zindex + 2);
+    z-index: $offcanvas-overlap-zindex;
 
     &.is-open {
       box-shadow: $offcanvas-shadow;
@@ -284,8 +284,8 @@ $maincontent-class: 'off-canvas-content' !default;
   }
 
   // No transform on overlap transition
-  @at-root .#{$maincontent-class}.has-transition-overlap[class*="is-open"] {
-    transform: translate(0, 0);
+  @at-root .#{$maincontent-class}.has-transition-overlap {
+    transform: none;
   }
 }
 
@@ -346,10 +346,10 @@ $content: $maincontent-class
   .off-canvas {
     @include off-canvas-base;
 
-    // Force position absolute for nested off-canvas because fixed doesn't work in this case (and gets emulated by JS anyway).
-    // This is important for the IE which doesn't show an opened push off-canvas element otherwise!
+    // Force position absolute for nested off-canvas because fixed doesn't work for push transition within the transform scope.
     @at-root .#{$maincontent-class} & {
-      position: absolute;
+      // NOTE: since overlap transition is currently forced if nested, there's no need to force position absolute until nested push transition is supported.
+      // position: absolute;
     }
   }
 
index 18f7c3e623f15ba93f4db8763070d200f481be4b..2106e9459104fb78e46196e5e3e66cc902c7b698 100644 (file)
@@ -484,9 +484,9 @@ $offcanvas-background: $light-gray;
 $offcanvas-shadow: 0 0 10px rgba($black, 0.7);
 $offcanvas-inner-shadow-size: 20px;
 $offcanvas-inner-shadow-color: rgba($black, 0.25);
-$offcanvas-push-zindex: 1;
-$offcanvas-overlap-zindex: 10;
-$offcanvas-reveal-zindex: 1;
+$offcanvas-push-zindex: 12;
+$offcanvas-overlap-zindex: 13;
+$offcanvas-reveal-zindex: 12;
 $offcanvas-transition-length: 0.5s;
 $offcanvas-transition-timing: ease;
 $offcanvas-fixed-reveal: true;