feat(popover): add initial animation

This commit is contained in:
mhartington
2017-08-08 14:55:27 -04:00
parent c1e2133461
commit e404b19733
8 changed files with 209 additions and 124 deletions

View File

@ -21,6 +21,7 @@
component: 'profile-page',
ev: clickEvt
}).then(popover => {
console.log(popover)
popover.present()
});
});

View File

@ -1,23 +1,26 @@
import { Animation } from '../../../index';
import { Animation, PopoverOptions } from '../../../index';
export default function(Animation: Animation, baseElm: HTMLElement) {
export default function popoverEnter(
Animation: Animation,
baseElm: HTMLElement,
evtSource: Event
) {
const baseAnimation = new Animation();
const backdropAnimation = new Animation();
backdropAnimation.addElement(baseElm.querySelector('.popover-backdrop'));
const wrapperAnimation = new Animation();
wrapperAnimation.addElement(baseElm.querySelector('.popover-wrapper'));
backdropAnimation.fromTo('opacity', 0.01, 0.3);
wrapperAnimation.fromTo('opacity', 0.01, 1)
.fromTo('scale', 1.1, 1);
wrapperAnimation.fromTo('opacity', 0.01, 1);
backdropAnimation.fromTo('opacity', 0.01, 0.08);
return baseAnimation
.addElement(baseElm)
.easing('ease-in-out')
.duration(200)
.easing('ease')
.duration(100)
.add(backdropAnimation)
.add(wrapperAnimation);
}

View File

@ -7,22 +7,19 @@ export default function(Animation: Animation, baseElm: HTMLElement) {
const baseAnimation = new Animation();
const backdropAnimation = new Animation();
backdropAnimation.addElement(baseElm.querySelector('.modal-backdrop'));
backdropAnimation.addElement(baseElm.querySelector('.popover-backdrop'));
const wrapperAnimation = new Animation();
const wrapperElm = baseElm.querySelector('.modal-wrapper');
wrapperAnimation.addElement(wrapperElm);
const wrapperElmRect = wrapperElm.getBoundingClientRect();
const wrapperElm = baseElm.querySelector('.popover-wrapper');
wrapperAnimation.beforeStyles({ 'opacity': 1 })
.fromTo('translateY', '0%', `${window.innerHeight - wrapperElmRect.top}px`);
wrapperAnimation.fromTo('opacity', 0.99, 0);
backdropAnimation.fromTo('opacity', 0.08, 0);
backdropAnimation.fromTo('opacity', 0.4, 0.0);
return baseAnimation
.addElement(baseElm)
.easing('ease-out')
.duration(250)
.easing('ease')
.duration(500)
.add(backdropAnimation)
.add(wrapperAnimation);
}

View File

@ -1,24 +1,81 @@
@import "../../themes/ionic.globals.ios";
@import "./modal";
@import "./popover";
// iOS Modals
// iOS Popover
// --------------------------------------------------
/// @prop - Background color for the modal
$modal-ios-background-color: $background-ios-color !default;
/// @prop - Width of the popover content
$popover-ios-width: 200px !default;
/// @prop - Min width of the popover content
$popover-ios-min-width: 0 !default;
/// @prop - Minimum height of the popover content
$popover-ios-min-height: 0 !default;
/// @prop - Maximum height of the popover content
$popover-ios-max-height: 90% !default;
/// @prop - Border radius of the popover content
$popover-ios-border-radius: 10px !default;
/// @prop - Text color of the popover content
$popover-ios-text-color: $text-ios-color !default;
/// @prop - Background of the popover content
$popover-ios-background: $background-ios-color !default;
/// @prop - Background of the popover arrow
$popover-ios-arrow-background: $popover-ios-background !default;
.popover-ios .popover-content {
@include border-radius($popover-ios-border-radius);
width: $popover-ios-width;
min-width: $popover-ios-min-width;
min-height: $popover-ios-min-height;
max-height: $popover-ios-max-height;
/// @prop - Border radius for the modal
$modal-ios-border-radius: 10px !default;
color: $popover-ios-text-color;
background: $popover-ios-background;
}
.modal-wrapper-ios {
// hidden by default to prevent flickers, the animation will show it
@include transform(translate3d(0, 100%, 0));
// Popover Arrow
// -----------------------------------------
@media only screen and (min-width: $modal-inset-min-width) and (min-height: $modal-inset-min-height-small) {
@include border-radius($modal-ios-border-radius);
.popover-ios .popover-arrow {
position: absolute;
display: block;
overflow: hidden;
width: 20px;
height: 10px;
}
.popover-ios .popover-arrow::after {
@include position(3px, null, null, 3px);
@include border-radius(3px);
position: absolute;
z-index: $z-index-overlay-wrapper;
width: 14px;
height: 14px;
background-color: $popover-ios-arrow-background;
content: "";
transform: rotate(45deg);
}
.popover-ios.popover-bottom .popover-arrow {
top: auto;
bottom: -10px;
}
.popover-ios.popover-bottom .popover-arrow::after {
top: -6px;
}

View File

@ -1,5 +1,6 @@
@import "../../themes/ionic.globals";
@import "../../themes/ionic.globals.md";
@import "./popover";
// Material Design Popover
// --------------------------------------------------
@ -19,12 +20,10 @@ $popover-md-max-height: 90% !default;
$popover-md-border-radius: 2px !default;
/// @prop - Text color of the popover content
// $popover-md-text-color: $text-md-color !default;
$popover-md-text-color: black !default;
$popover-md-text-color: $text-md-color !default;
/// @prop - Background of the popover content
// $popover-md-background: $background-md-color !default;
$popover-md-background: black !default;
$popover-md-background: $background-md-color !default;
/// @prop - Box shadow color of the popover content
$popover-md-box-shadow-color: rgba(0, 0, 0, .3) !default;
@ -43,8 +42,7 @@ $popover-md-box-shadow: 0 3px 12px 2px $popover-md-box-shadow-col
max-height: $popover-md-max-height;
color: $popover-md-text-color;
// background: $popover-md-background;
background: #ffffff;
background: $popover-md-background;
box-shadow: $popover-md-box-shadow;
}

View File

@ -3,6 +3,7 @@
// Popover
// --------------------------------------------------
ion-popover {
@include position(0, 0, 0, 0);
@ -14,25 +15,12 @@ ion-popover {
align-items: center;
justify-content: center;
.popover-backdrop {
left: 0;
top: 0;
position: absolute;
bottom: 0px;
z-index: 2;
display: block;
width: 100%;
background-color: #000;
opacity: 0.3;
}
}
.popover-wrapper {
z-index: $z-index-overlay-wrapper;
// opacity: 0;
opacity: 1;
opacity: 0;
}
.popover-content {
@ -46,7 +34,8 @@ ion-popover {
flex-direction: column;
}
.popover-content ion-content, .popover-content .scroll-content {
.popover-content ion-content,
.popover-content .scroll-content {
contain: none;
}

View File

@ -1,17 +1,23 @@
import { Component, Element, Event, EventEmitter, Listen, Prop } from '@stencil/core';
import { AnimationBuilder, Animation } from '../../index';
import {
Component,
Element,
Event,
EventEmitter,
Listen,
Prop
} from '@stencil/core';
import { AnimationBuilder, Animation, Ionic } from '../../index';
import { createThemedClasses } from '../../utils/theme';
import iOSEnterAnimation from './animations/ios.enter';
import iOSLeaveAnimation from './animations/ios.leave';
@Component({
tag: 'ion-popover',
styleUrls: {
// ios: 'popover.ios.scss',
ios: 'popover.ios.scss',
md: 'popover.md.scss',
// wp: 'popover.wp.scss'
wp: 'popover.wp.scss'
},
host: {
theme: 'popover'
@ -39,8 +45,6 @@ export class Popover {
@Prop() id: string;
@Prop() showBackdrop: boolean = true;
private animation: Animation;
@Listen('ionDismiss')
@ -69,50 +73,49 @@ export class Popover {
this.ionPopoverWillPresent.emit({ popover: this });
// get the user's animation fn if one was provided
// let animationBuilder = this.enterAnimation
// ? this.enterAnimation
// : iOSEnterAnimation;
let animationBuilder = this.enterAnimation
? this.enterAnimation
: iOSEnterAnimation;
//
// build the animation and kick it off
// this.animation = animationBuilder(this.el);
// this.animation.onFinish((a: any) => {
// a.destroy();
// this.ionPopoverDidPresent.emit({ popover: this });
Ionic.controller('animation').then(Animation => {
this.animation = animationBuilder(Animation, this.el, this.ev);
this.animation
.onFinish((a: any) => {
a.destroy();
this.ionPopoverDidPresent.emit({ popover: this });
resolve();
// }).play();
})
.play();
});
}
dismiss() {
// if (this.animation) {
// this.animation.destroy();
// this.animation = null;
// }
if (this.animation) {
this.animation.destroy();
this.animation = null;
}
return new Promise<void>(resolve => {
this.ionPopoverWillDismiss.emit({ popover: this });
// get the user's animation fn if one was provided
// let animationBuilder = this.exitAnimation;
//
// if (!animationBuilder) {
// // user did not provide a custom animation fn
// // decide from the config which animation to use
// // TODO!!
// animationBuilder = iOSLeaveAnimation;
// }
let animationBuilder = this.exitAnimation
? this.exitAnimation
: iOSLeaveAnimation;
// build the animation and kick it off
// this.animation = animationBuilder(this.el);
// this.animation.onFinish((a: any) => {
// a.destroy();
Ionic.controller('animation').then(Animation => {
this.animation = animationBuilder(Animation, this.el);
this.animation
.onFinish((a: any) => {
a.destroy();
this.ionPopoverDidDismiss.emit({ popover: this });
Core.dom.write(() => {
this.el.parentNode.removeChild(this.el);
});
resolve();
// }).play();
})
.play();
});
});
}
@ -137,32 +140,37 @@ export class Popover {
userCssClasses += ` ${this.cssClass}`;
}
const dialogClasses = createThemedClasses(this.mode, this.color, 'popover-wrapper');
const thisComponentClasses = createThemedClasses(this.mode, this.color, userCssClasses);
const dialogClasses = createThemedClasses(
this.mode,
this.color,
'popover-wrapper'
);
const thisComponentClasses = createThemedClasses(
this.mode,
this.color,
userCssClasses
);
return [
<div
<ion-backdrop
onClick={this.backdropClick.bind(this)}
class={{
'popover-backdrop': true,
'hide-backdrop': !this.showBackdrop
}}
></div>,
<div
role='dialog'
class={dialogClasses}
>
class="popover-backdrop"
/>,
<div class={dialogClasses}>
<div class="popover-arrow" />
<div class="popover-content">
<div class="popover-viewport">
<ThisComponent
props={this.componentProps}
class={thisComponentClasses}
>
</ThisComponent>
/>
</div>
</div>
</div>
];
}
}
export interface PopoverOptions {
component: string;
componentProps?: any;
@ -174,9 +182,8 @@ export interface PopoverOptions {
ev: Event;
}
export interface PopoverEvent {
detail: {
popover: Popover;
}
};
}

View File

@ -1,16 +1,49 @@
@import "../../themes/ionic.globals.wp";
@import "./modal";
@import "./popover";
// Windows Modals
// Windows Popover
// --------------------------------------------------
/// @prop - Background color for the modal
$modal-wp-background-color: $background-wp-color !default;
/// @prop - Width of the popover content
$popover-wp-width: 200px !default;
/// @prop - Min width of the popover content
$popover-wp-min-width: 0 !default;
/// @prop - Minimum height of the popover content
$popover-wp-min-height: 0 !default;
/// @prop - Maximum height of the popover content
$popover-wp-max-height: 90% !default;
/// @prop - Border of the popover content
$popover-wp-border: 2px solid #ccc !default;
/// @prop - Border radius of the popover content
$popover-wp-border-radius: 0 !default;
/// @prop - Text color of the popover content
$popover-wp-text-color: $text-wp-color !default;
.modal-wrapper-wp {
@include transform(translate3d(0, 40px, 0));
/// @prop - Background of the popover content
$popover-wp-background: $background-wp-color !default;
.popover-wp .popover-content {
@include border-radius($popover-wp-border-radius);
@include transform-origin(start, top);
width: $popover-wp-width;
min-width: $popover-wp-min-width;
min-height: $popover-wp-min-height;
max-height: $popover-wp-max-height;
border: $popover-wp-border;
color: $popover-wp-text-color;
background: $popover-wp-background;
}
opacity: .01;
.popover-wp .popover-viewport {
opacity: 0;
transition-delay: 100ms;
}