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', component: 'profile-page',
ev: clickEvt ev: clickEvt
}).then(popover => { }).then(popover => {
console.log(popover)
popover.present() 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 baseAnimation = new Animation();
const backdropAnimation = new Animation(); const backdropAnimation = new Animation();
backdropAnimation.addElement(baseElm.querySelector('.popover-backdrop')); backdropAnimation.addElement(baseElm.querySelector('.popover-backdrop'));
const wrapperAnimation = new Animation(); const wrapperAnimation = new Animation();
wrapperAnimation.addElement(baseElm.querySelector('.popover-wrapper')); wrapperAnimation.addElement(baseElm.querySelector('.popover-wrapper'));
backdropAnimation.fromTo('opacity', 0.01, 0.3); wrapperAnimation.fromTo('opacity', 0.01, 1);
backdropAnimation.fromTo('opacity', 0.01, 0.08);
wrapperAnimation.fromTo('opacity', 0.01, 1)
.fromTo('scale', 1.1, 1);
return baseAnimation return baseAnimation
.addElement(baseElm) .addElement(baseElm)
.easing('ease-in-out') .easing('ease')
.duration(200) .duration(100)
.add(backdropAnimation) .add(backdropAnimation)
.add(wrapperAnimation); .add(wrapperAnimation);
} }

View File

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

View File

@ -1,24 +1,81 @@
@import "../../themes/ionic.globals.ios"; @import "../../themes/ionic.globals.ios";
@import "./modal"; @import "./popover";
// iOS Popover
// iOS Modals
// -------------------------------------------------- // --------------------------------------------------
/// @prop - Background color for the modal /// @prop - Width of the popover content
$modal-ios-background-color: $background-ios-color !default; $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 - Border radius for the modal /// @prop - Background of the popover content
$modal-ios-border-radius: 10px !default; $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;
color: $popover-ios-text-color;
background: $popover-ios-background;
}
.modal-wrapper-ios { // Popover Arrow
// hidden by default to prevent flickers, the animation will show it // -----------------------------------------
@include transform(translate3d(0, 100%, 0));
@media only screen and (min-width: $modal-inset-min-width) and (min-height: $modal-inset-min-height-small) { .popover-ios .popover-arrow {
@include border-radius($modal-ios-border-radius); 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;
}
overflow: hidden; .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"; @import "./popover";
// Material Design Popover // Material Design Popover
// -------------------------------------------------- // --------------------------------------------------
@ -19,12 +20,10 @@ $popover-md-max-height: 90% !default;
$popover-md-border-radius: 2px !default; $popover-md-border-radius: 2px !default;
/// @prop - Text color of the popover content /// @prop - Text color of the popover content
// $popover-md-text-color: $text-md-color !default; $popover-md-text-color: $text-md-color !default;
$popover-md-text-color: black !default;
/// @prop - Background of the popover content /// @prop - Background of the popover content
// $popover-md-background: $background-md-color !default; $popover-md-background: $background-md-color !default;
$popover-md-background: black !default;
/// @prop - Box shadow color of the popover content /// @prop - Box shadow color of the popover content
$popover-md-box-shadow-color: rgba(0, 0, 0, .3) !default; $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; max-height: $popover-md-max-height;
color: $popover-md-text-color; color: $popover-md-text-color;
// background: $popover-md-background; background: $popover-md-background;
background: #ffffff;
box-shadow: $popover-md-box-shadow; box-shadow: $popover-md-box-shadow;
} }

View File

@ -3,6 +3,7 @@
// Popover // Popover
// -------------------------------------------------- // --------------------------------------------------
ion-popover { ion-popover {
@include position(0, 0, 0, 0); @include position(0, 0, 0, 0);
@ -14,25 +15,12 @@ ion-popover {
align-items: center; align-items: center;
justify-content: 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 { .popover-wrapper {
z-index: $z-index-overlay-wrapper; z-index: $z-index-overlay-wrapper;
// opacity: 0; opacity: 0;
opacity: 1;
} }
.popover-content { .popover-content {
@ -46,7 +34,8 @@ ion-popover {
flex-direction: column; flex-direction: column;
} }
.popover-content ion-content, .popover-content .scroll-content { .popover-content ion-content,
.popover-content .scroll-content {
contain: none; contain: none;
} }

View File

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

View File

@ -1,16 +1,49 @@
@import "../../themes/ionic.globals.wp"; @import "../../themes/ionic.globals.wp";
@import "./modal"; @import "./popover";
// Windows Popover
// Windows Modals
// -------------------------------------------------- // --------------------------------------------------
/// @prop - Background color for the modal /// @prop - Width of the popover content
$modal-wp-background-color: $background-wp-color !default; $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 { /// @prop - Background of the popover content
@include transform(translate3d(0, 40px, 0)); $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;
} }