fix(ion-backdrop): new ion-backdrop can prevent background scrolling

closes #6656
This commit is contained in:
Manu Mtz.-Almeida
2016-05-28 17:08:06 +02:00
parent c42bf97d67
commit a1a582b7a1
21 changed files with 153 additions and 115 deletions

View File

@ -14,6 +14,7 @@
// Core Components
@import
"components/backdrop/backdrop",
"components/grid/grid",
"components/icon/icon",
"components/img/img",

View File

@ -215,7 +215,7 @@ export class ActionSheet extends ViewController {
@Component({
selector: 'ion-action-sheet',
template:
'<div (click)="bdClick()" tappable disable-activated class="backdrop" role="presentation"></div>' +
'<ion-backdrop (click)="bdClick()"></ion-backdrop>' +
'<div class="action-sheet-wrapper">' +
'<div class="action-sheet-container">' +
'<div class="action-sheet-group">' +
@ -384,7 +384,7 @@ class ActionSheetSlideIn extends Transition {
super(opts);
let ele = enteringView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.action-sheet-wrapper'));
backdrop.fromTo('opacity', 0.01, 0.4);
@ -401,7 +401,7 @@ class ActionSheetSlideOut extends Transition {
super(opts);
let ele = leavingView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.action-sheet-wrapper'));
backdrop.fromTo('opacity', 0.4, 0);
@ -418,7 +418,7 @@ class ActionSheetMdSlideIn extends Transition {
super(opts);
let ele = enteringView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.action-sheet-wrapper'));
backdrop.fromTo('opacity', 0.01, 0.26);
@ -435,7 +435,7 @@ class ActionSheetMdSlideOut extends Transition {
super(opts);
let ele = leavingView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.action-sheet-wrapper'));
backdrop.fromTo('opacity', 0.26, 0);
@ -451,7 +451,7 @@ class ActionSheetWpSlideIn extends Transition {
super(opts);
let ele = enteringView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.action-sheet-wrapper'));
backdrop.fromTo('opacity', 0.01, 0.16);
@ -468,7 +468,7 @@ class ActionSheetWpSlideOut extends Transition {
super(opts);
let ele = leavingView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.action-sheet-wrapper'));
backdrop.fromTo('opacity', 0.1, 0);

View File

@ -4,5 +4,5 @@ it('should open action sheet', function() {
});
it('should close with backdrop click', function() {
element(by.css('.backdrop')).click();
element(by.css('ion-backdrop')).click();
});

View File

@ -311,7 +311,7 @@ export class Alert extends ViewController {
@Component({
selector: 'ion-alert',
template:
'<div (click)="bdClick()" tappable disable-activated class="backdrop" role="presentation"></div>' +
'<ion-backdrop (click)="bdClick()"></ion-backdrop>' +
'<div class="alert-wrapper">' +
'<div class="alert-head">' +
'<h2 id="{{hdrId}}" class="alert-title" *ngIf="d.title" [innerHTML]="d.title"></h2>' +
@ -612,7 +612,7 @@ class AlertPopIn extends Transition {
super(opts);
let ele = enteringView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.alert-wrapper'));
wrapper.fromTo('opacity', '0.01', '1').fromTo('scale', '1.1', '1');
@ -633,7 +633,7 @@ class AlertPopOut extends Transition {
super(opts);
let ele = leavingView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.alert-wrapper'));
wrapper.fromTo('opacity', '1', '0').fromTo('scale', '1', '0.9');
@ -654,7 +654,7 @@ class AlertMdPopIn extends Transition {
super(opts);
let ele = enteringView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.alert-wrapper'));
wrapper.fromTo('opacity', '0.01', '1').fromTo('scale', '1.1', '1');
@ -675,7 +675,7 @@ class AlertMdPopOut extends Transition {
super(opts);
let ele = leavingView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.alert-wrapper'));
wrapper.fromTo('opacity', '1', '0').fromTo('scale', '1', '0.9');
@ -697,7 +697,7 @@ class AlertWpPopIn extends Transition {
super(opts);
let ele = enteringView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.alert-wrapper'));
wrapper.fromTo('opacity', '0.01', '1').fromTo('scale', '1.3', '1');
@ -718,7 +718,7 @@ class AlertWpPopOut extends Transition {
super(opts);
let ele = leavingView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.alert-wrapper'));
wrapper.fromTo('opacity', '1', '0').fromTo('scale', '1', '1.3');

View File

@ -100,7 +100,7 @@ $alert-wp-checkbox-icon-transform: rotate(45deg) !default;
ion-alert {
.backdrop {
ion-backdrop {
background: $alert-wp-backdrop-background;
}
}

View File

@ -0,0 +1,25 @@
@import "../../globals.core";
// Backdrop
// --------------------------------------------------
$backdrop-color: #000 !default;
ion-backdrop {
position: absolute;
top: 0;
left: 0;
z-index: $z-index-backdrop;
display: block;
width: 100%;
height: 100%;
background-color: $backdrop-color;
opacity: 0.01;
transform: translateZ(0);
}
ion-backdrop.hide-backdrop {
display: none;
}

View File

@ -0,0 +1,61 @@
import {Directive, ViewEncapsulation, HostListener, ElementRef, Input} from '@angular/core';
import {isTrueProperty} from '../../util/util';
const DISABLE_SCROLL = 'disable-scroll';
/**
* @private
*/
@Directive({
selector: 'ion-backdrop',
host: {
'role': 'presentation',
'tappable': '',
'disable-activated': ''
},
})
export class Backdrop {
private static nuBackDrops: number = 0;
private static push() {
if (this.nuBackDrops === 0) {
console.debug('adding .disable-scroll to body');
document.body.classList.add(DISABLE_SCROLL);
} else {
console.warn('several backdrops on screen? probably a bug');
}
this.nuBackDrops++;
}
private static pop() {
if (this.nuBackDrops === 0) {
console.error('pop requires a push');
return;
}
this.nuBackDrops--;
if (this.nuBackDrops === 0) {
console.debug('removing .disable-scroll from body');
document.body.classList.remove(DISABLE_SCROLL);
}
}
private pushed: boolean = false;
@Input() disableScroll = true;
constructor(public elementRef: ElementRef) {}
ngOnInit() {
if (isTrueProperty(this.disableScroll)) {
Backdrop.push();
this.pushed = true;
}
}
ngOnDestroy() {
if (this.pushed) {
Backdrop.pop();
this.pushed = false;
}
}
}

View File

@ -3,7 +3,6 @@
// Loading Indicator
// --------------------------------------------------
ion-loading {
position: absolute;
top: 0;
@ -26,10 +25,3 @@ ion-loading {
opacity: 0;
}
// Loading Backdrop
// -----------------------------------------
.hide-backdrop {
display: none;
}

View File

@ -156,7 +156,7 @@ export class Loading extends ViewController {
@Component({
selector: 'ion-loading',
template:
'<div disable-activated class="backdrop" [class.hide-backdrop]="!d.showBackdrop" role="presentation"></div>' +
'<ion-backdrop [class.hide-backdrop]="!d.showBackdrop"></ion-backdrop>' +
'<div class="loading-wrapper">' +
'<div *ngIf="showSpinner" class="loading-spinner">' +
'<ion-spinner [name]="d.spinner"></ion-spinner>' +
@ -240,7 +240,7 @@ export interface LoadingOptions {
super(opts);
let ele = enteringView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.loading-wrapper'));
wrapper.fromTo('opacity', '0.01', '1').fromTo('scale', '1.1', '1');
@ -261,7 +261,7 @@ export interface LoadingOptions {
super(opts);
let ele = leavingView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.loading-wrapper'));
wrapper.fromTo('opacity', '1', '0').fromTo('scale', '1', '0.9');
@ -282,7 +282,7 @@ export interface LoadingOptions {
super(opts);
let ele = enteringView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.loading-wrapper'));
wrapper.fromTo('opacity', '0.01', '1').fromTo('scale', '1.1', '1');
@ -303,7 +303,7 @@ export interface LoadingOptions {
super(opts);
let ele = leavingView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.loading-wrapper'));
wrapper.fromTo('opacity', '1', '0').fromTo('scale', '1', '0.9');
@ -324,7 +324,7 @@ export interface LoadingOptions {
super(opts);
let ele = enteringView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.loading-wrapper'));
wrapper.fromTo('opacity', '0.01', '1').fromTo('scale', '1.3', '1');
@ -345,7 +345,7 @@ export interface LoadingOptions {
super(opts);
let ele = leavingView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.loading-wrapper'));
wrapper.fromTo('opacity', '1', '0').fromTo('scale', '1', '1.3');

View File

@ -28,7 +28,7 @@ ion-menu[side=right] {
left: auto;
}
ion-menu .backdrop {
ion-menu ion-backdrop {
z-index: -1;
display: none;
}
@ -74,13 +74,13 @@ ion-menu[type=overlay] {
left: -8px; // make up for the box-shadow hanging over on the left
z-index: $z-index-menu-overlay;
.backdrop {
ion-backdrop {
left: -3000px;
display: block;
width: 6000px;
opacity: .01;
opacity: 0.01;
transform: translate3d(-9999px, 0, 0);
&.show-backdrop {

View File

@ -1,4 +1,4 @@
import {Component, forwardRef, Directive, Host, EventEmitter, ElementRef, NgZone, Input, Output, Renderer, ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core';
import {Component, forwardRef, Directive, Host, EventEmitter, ElementRef, NgZone, Input, Output, Renderer, ChangeDetectionStrategy, ViewEncapsulation, ViewChild} from '@angular/core';
import {Ion} from '../ion';
import {Config} from '../../config/config';
@ -8,6 +8,7 @@ import {MenuContentGesture, MenuTargetGesture} from './menu-gestures';
import {MenuController} from './menu-controller';
import {MenuType} from './menu-types';
import {isTrueProperty} from '../../util/util';
import {Backdrop} from '../backdrop/backdrop';
/**
@ -180,8 +181,7 @@ import {isTrueProperty} from '../../util/util';
},
template:
'<ng-content></ng-content>' +
'<div tappable disable-activated class="backdrop"></div>',
directives: [forwardRef(() => MenuBackdrop)],
'<ion-backdrop (click)="bdClick($event)" disableScroll="false"></ion-backdrop>',
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
})
@ -206,7 +206,7 @@ export class Menu extends Ion {
/**
* @private
*/
backdrop: MenuBackdrop;
@ViewChild(Backdrop) backdrop: Backdrop;
/**
* @private
@ -363,6 +363,16 @@ export class Menu extends Ion {
self._menuCtrl.register(self);
}
/**
* @private
*/
bdClick(ev) {
console.debug('backdrop clicked');
ev.preventDefault();
ev.stopPropagation();
this._menuCtrl.close();
}
/**
* @private
*/
@ -609,31 +619,3 @@ export class Menu extends Ion {
}
}
/**
* @private
*/
@Directive({
selector: '.backdrop',
host: {
'(click)': 'clicked($event)',
}
})
export class MenuBackdrop {
constructor(@Host() private _menuCtrl: Menu, public elementRef: ElementRef) {
_menuCtrl.backdrop = this;
}
/**
* @private
*/
private clicked(ev: UIEvent) {
console.debug('backdrop clicked');
ev.preventDefault();
ev.stopPropagation();
this._menuCtrl.close();
}
}

View File

@ -20,7 +20,7 @@ $modal-inset-height-large: 600px !default;
width: 100%;
height: 100%;
.backdrop {
ion-backdrop {
@media not all and (min-width: $modal-inset-min-width) and (min-height: $modal-inset-min-height-small) {
display: none;
}

View File

@ -156,7 +156,7 @@ export class Modal extends ViewController {
@Component({
selector: 'ion-modal',
template:
'<div class="backdrop"></div>' +
'<ion-backdrop disableScroll="false"></ion-backdrop>' +
'<div class="modal-wrapper">' +
'<div #viewport></div>' +
'</div>'
@ -189,7 +189,7 @@ class ModalSlideIn extends Transition {
super(opts);
let ele = enteringView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
backdrop.fromTo('opacity', 0.01, 0.4);
let wrapper = new Animation(ele.querySelector('.modal-wrapper'));
let page = <HTMLElement> ele.querySelector('ion-page');
@ -222,7 +222,7 @@ class ModalSlideOut extends Transition {
let ele = leavingView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
backdrop.fromTo('opacity', 0.4, 0.0);
let wrapperEle = <HTMLElement> ele.querySelector('.modal-wrapper');
let wrapperEleRect = wrapperEle.getBoundingClientRect();
@ -249,7 +249,7 @@ class ModalMDSlideIn extends Transition {
super(opts);
let ele = enteringView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
backdrop.fromTo('opacity', 0.01, 0.4);
let wrapper = new Animation(ele.querySelector('.modal-wrapper'));
wrapper.fromTo('translateY', '40px', '0px');
@ -281,7 +281,7 @@ class ModalMDSlideOut extends Transition {
super(opts);
let ele = leavingView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
backdrop.fromTo('opacity', 0.4, 0.0);
let wrapper = new Animation(ele.querySelector('.modal-wrapper'));
wrapper.fromTo('translateY', '0px', '40px');

View File

@ -308,7 +308,7 @@ class ContactUs {
<f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f>
<f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f>
<ion-list>
<ion-item *ngFor="#item of items">
<ion-item *ngFor="let item of items">
Item Number: {{item.value}}
</ion-item>
</ion-list>

View File

@ -435,7 +435,7 @@ class PickerColumnCmp {
@Component({
selector: 'ion-picker-cmp',
template:
'<div (click)="bdClick()" tappable disable-activated class="backdrop" role="presentation"></div>' +
'<ion-backdrop (click)="bdClick()"></ion-backdrop>' +
'<div class="picker-wrapper">' +
'<div class="picker-toolbar">' +
'<div *ngFor="let b of d.buttons" class="picker-toolbar-button" [ngClass]="b.cssRole">' +
@ -665,7 +665,7 @@ class PickerSlideIn extends Transition {
super(opts);
let ele = enteringView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.picker-wrapper'));
backdrop.fromTo('opacity', 0.01, 0.26);
@ -682,7 +682,7 @@ class PickerSlideOut extends Transition {
super(opts);
let ele = leavingView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.picker-wrapper'));
backdrop.fromTo('opacity', 0.26, 0);

View File

@ -43,11 +43,3 @@ ion-popover {
height: auto;
}
}
// Popover Backdrop
// -----------------------------------------
.hide-backdrop {
display: none;
}

View File

@ -155,7 +155,7 @@ export class Popover extends ViewController {
@Component({
selector: 'ion-popover',
template:
'<div class="backdrop" (touchmove)="bdTouch($event)" (click)="bdClick($event)" [class.hide-backdrop]="!d.showBackdrop" disable-activated tappable role="presentation"></div>' +
'<ion-backdrop (click)="bdClick($event)" [class.hide-backdrop]="!d.showBackdrop"></ion-backdrop>' +
'<div class="popover-wrapper">' +
'<div class="popover-arrow"></div>' +
'<div class="popover-content">' +
@ -383,7 +383,7 @@ class PopoverPopIn extends PopoverTransition {
let ele = enteringView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.popover-wrapper'));
wrapper.fromTo('opacity', '0.01', '1');
@ -411,7 +411,7 @@ class PopoverPopOut extends PopoverTransition {
super(opts);
let ele = leavingView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.popover-wrapper'));
wrapper.fromTo('opacity', '1', '0');

View File

@ -35,3 +35,8 @@ ion-scroll {
}
}
body.disable-scroll scroll-content {
overflow-y: hidden;
overflow-x: hidden;
}

View File

@ -243,7 +243,7 @@ class ToastMdSlideIn extends Transition {
super(opts);
let ele = enteringView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.toast-wrapper'));
backdrop.fromTo('opacity', 0, 0);
@ -258,7 +258,7 @@ class ToastMdSlideOut extends Transition {
let ele = leavingView.pageRef().nativeElement;
let wrapper = new Animation(ele.querySelector('.toast-wrapper'));
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
wrapper.fromTo('translateY', '0%', '120%');
backdrop.fromTo('opacity', 0, 0);
@ -271,7 +271,7 @@ class ToastWpPopIn extends Transition {
super(opts);
let ele = enteringView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.toast-wrapper'));
wrapper.fromTo('opacity', '0.01', '1').fromTo('scale', '1.3', '1');
@ -286,7 +286,7 @@ class ToastWpPopOut extends Transition {
super(opts);
let ele = leavingView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.toast-wrapper'));
wrapper.fromTo('opacity', '1', '0').fromTo('scale', '1', '1.3');

View File

@ -4,6 +4,7 @@ import {CORE_DIRECTIVES, FORM_DIRECTIVES} from '@angular/common';
import {Menu} from '../components/menu/menu';
import {MenuToggle} from '../components/menu/menu-toggle';
import {MenuClose} from '../components/menu/menu-close';
import {Backdrop} from '../components/backdrop/backdrop';
import {Badge} from '../components/badge/badge';
import {Button} from '../components/button/button';
import {Content} from '../components/content/content';
@ -120,6 +121,7 @@ export const IONIC_DIRECTIVES: any[] = [
MenuToggle,
MenuClose,
Backdrop,
Badge,
Button,
Content,

View File

@ -58,28 +58,6 @@ focus-ctrl {
}
}
// Backdrop
// --------------------------------------------------
$backdrop-color: #000 !default;
.backdrop {
position: absolute;
top: 0;
left: 0;
z-index: $z-index-backdrop;
display: block;
width: 100%;
height: 100%;
background-color: $backdrop-color;
opacity: .01;
transform: translateZ(0);
}
// Click Block
// --------------------------------------------------
// Fill the screen to block clicks (a better pointer-events: none)