mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-09 08:09:32 +08:00
feat(popover): add initial animation
This commit is contained in:
@ -21,6 +21,7 @@
|
||||
component: 'profile-page',
|
||||
ev: clickEvt
|
||||
}).then(popover => {
|
||||
console.log(popover)
|
||||
popover.present()
|
||||
});
|
||||
});
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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 - Border radius for the modal
|
||||
$modal-ios-border-radius: 10px !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;
|
||||
|
||||
|
||||
.modal-wrapper-ios {
|
||||
// hidden by default to prevent flickers, the animation will show it
|
||||
@include transform(translate3d(0, 100%, 0));
|
||||
.popover-ios .popover-content {
|
||||
@include border-radius($popover-ios-border-radius);
|
||||
|
||||
@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);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
// Popover Arrow
|
||||
// -----------------------------------------
|
||||
|
||||
.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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
/// @prop - Background of the popover content
|
||||
$popover-wp-background: $background-wp-color !default;
|
||||
|
||||
|
||||
.modal-wrapper-wp {
|
||||
@include transform(translate3d(0, 40px, 0));
|
||||
.popover-wp .popover-content {
|
||||
@include border-radius($popover-wp-border-radius);
|
||||
@include transform-origin(start, top);
|
||||
|
||||
opacity: .01;
|
||||
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;
|
||||
}
|
||||
|
||||
.popover-wp .popover-viewport {
|
||||
opacity: 0;
|
||||
transition-delay: 100ms;
|
||||
}
|
||||
Reference in New Issue
Block a user