mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-09 16:16:41 +08:00
feat(popover): add initial animation
This commit is contained in:
@ -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()
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 - Border radius for the modal
|
/// @prop - Min width of the popover content
|
||||||
$modal-ios-border-radius: 10px !default;
|
$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 {
|
.popover-ios .popover-content {
|
||||||
// hidden by default to prevent flickers, the animation will show it
|
@include border-radius($popover-ios-border-radius);
|
||||||
@include transform(translate3d(0, 100%, 0));
|
|
||||||
|
|
||||||
@media only screen and (min-width: $modal-inset-min-width) and (min-height: $modal-inset-min-height-small) {
|
width: $popover-ios-width;
|
||||||
@include border-radius($modal-ios-border-radius);
|
min-width: $popover-ios-min-width;
|
||||||
|
min-height: $popover-ios-min-height;
|
||||||
|
max-height: $popover-ios-max-height;
|
||||||
|
|
||||||
overflow: hidden;
|
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";
|
@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,12 +42,11 @@ $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;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popover-md .popover-viewport {
|
.popover-md .popover-viewport {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition-delay: 100ms;
|
transition-delay: 100ms;
|
||||||
}
|
}
|
||||||
@ -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,10 +34,11 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popover-content .scroll-content {
|
.popover-content .scroll-content {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
/// @prop - Background of the popover content
|
||||||
|
$popover-wp-background: $background-wp-color !default;
|
||||||
|
|
||||||
|
|
||||||
.modal-wrapper-wp {
|
.popover-wp .popover-content {
|
||||||
@include transform(translate3d(0, 40px, 0));
|
@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