feat(popover, actionsheet): add inital render

This commit is contained in:
mhartington
2017-08-02 13:02:42 -04:00
parent 5e999a728f
commit 02670801d5
22 changed files with 1338 additions and 0 deletions

View File

@ -0,0 +1,32 @@
@import "../../themes/ionic.globals";
// Loading Controller
// --------------------------------------------------
ion-loading-controller {
display: none;
}
// Loading Controller Backdrop
// --------------------------------------------------
/// @prop - Color of the backdrop
$loading-backdrop-color: #000 !default;
.loading-backdrop {
position: absolute;
top: 0;
left: 0;
z-index: $z-index-backdrop;
display: block;
width: 100%;
height: 100%;
background-color: $loading-backdrop-color;
opacity: .01;
transform: translateZ(0);
}

View File

@ -0,0 +1,79 @@
import { Component, Listen } from '@stencil/core';
import { ActionSheetEvent, ActionSheetOptions, ActionSheet, IonicControllerApi } from '../../index';
@Component({
tag: 'ion-action-sheet-controller',
styleUrl: 'action-sheet-controller.scss'
})
export class ActionSheetController implements IonicControllerApi {
private ids = 0;
private actionsheetResolves: {[actionsheetId: string]: Function} = {};
private actionsheets: ActionSheet[] = [];
private appRoot: Element;
ionViewDidLoad() {
this.appRoot = document.querySelector('ion-app') || document.body;
Ionic.loadController('action-sheet', this);
}
load(opts?: ActionSheetOptions) {
// create ionic's wrapping ion-actionsheet component
const actionsheet = document.createElement('ion-action-sheet');
const id = this.ids++;
// give this actionsheet a unique id
actionsheet.id = `action-sheet-${id}`;
actionsheet.style.zIndex = (20000 + id).toString();
// convert the passed in actionsheet options into props
// that get passed down into the new actionsheet
Object.assign(actionsheet, opts);
// append the actionsheet element to the document body
this.appRoot.appendChild(actionsheet as any);
// store the resolve function to be called later up when the actionsheet loads
return new Promise<ActionSheet>(resolve => {
this.actionsheetResolves[actionsheet.id] = resolve;
});
}
@Listen('body:ionActionSheetDidLoad')
viewDidLoad(ev) {
const actionsheet = ev.detail.actionsheet;
const actionsheetResolve = this.actionsheetResolves[actionsheet.id];
if (actionsheetResolve) {
actionsheetResolve(actionsheet);
delete this.actionsheetResolves[actionsheet.id];
}
}
@Listen('body:ionActionSheetWillPresent')
willPresent(ev: ActionSheetEvent) {
this.actionsheets.push(ev.actionsheet);
}
@Listen('body:ionActionSheetWillDismiss, body:ionActionSheetDidUnload')
willDismiss(ev: ActionSheetEvent) {
const index = this.actionsheets.indexOf(ev.actionsheet);
if (index > -1) {
this.actionsheets.splice(index, 1);
}
}
@Listen('body:keyup.escape')
escapeKeyUp() {
const lastActionSheet = this.actionsheets[this.actionsheets.length - 1];
if (lastActionSheet) {
lastActionSheet.dismiss();
}
}
}

View File

@ -0,0 +1,192 @@
import {
Component,
Element,
Event,
EventEmitter,
Listen,
Prop,
State
} from '@stencil/core';
import { AnimationBuilder, Animation } from '../../index';
import iOSEnterAnimation from './animations/ios.enter';
import iOSLeaveAnimation from './animations/ios.leave';
@Component({
tag: 'ion-action-sheet',
// styleUrls: {
// ios: 'action-sheet.ios.scss',
// md: 'action-sheet.md.scss',
// wp: 'action-sheet.wp.scss'
// },
host: {
theme: 'action-sheet'
}
})
export class ActionSheet {
private animation: Animation;
private durationTimeout: any;
@Element() private el: HTMLElement;
@Event() ionActionSheetDidLoad: EventEmitter;
@Event() ionActionSheetWillPresent: EventEmitter;
@Event() ionActionSheetDidPresent: EventEmitter;
@Event() ionActionSheetWillDismiss: EventEmitter;
@Event() ionActionSheetDidDismiss: EventEmitter;
@Event() ionActionSheetDidUnload: EventEmitter;
@Prop() cssClass: string;
@Prop() title: string;
@Prop() subTitle: string;
@Prop() buttons: ActionSheetButtons[];
@Prop() enableBackdropDismiss: boolean = true;
@Prop() showBackdrop: boolean = true;
@Prop() enterAnimation: AnimationBuilder;
@Prop() exitAnimation: AnimationBuilder;
@Prop() id: string;
@Listen('ionDismiss')
onDismiss(ev: UIEvent) {
ev.stopPropagation();
ev.preventDefault();
this.dismiss();
}
ionViewDidLoad() {
this.ionActionSheetDidLoad.emit({ actionsheet: this });
}
present() {
return new Promise<void>(resolve => {
this._present(resolve);
});
}
private _present(resolve: Function) {
// if (this.animation) {
// this.animation.destroy();
// this.animation = null;
// }
this.ionActionSheetWillPresent.emit(
{ actionsheet: this } as ActionSheetEvent
);
// 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.ionViewDidLoad();
resolve();
// }).play();
}
dismiss() {
//
// if (this.animation) {
// this.animation.destroy();
// this.animation = null;
// }
return new Promise<void>(resolve => {
this.ionActionSheetWillDismiss.emit(
{ actionsheet: this } as ActionSheetEvent
);
// get the user's animation fn if one was provided
let animationBuilder = this.exitAnimation;
// 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();
this.ionActionSheetDidDismiss.emit(
{ actionsheet: this } as ActionSheetEvent
);
Core.dom.write(() => {
this.el.parentNode.removeChild(this.el);
});
resolve();
// }).play();
});
}
ionViewDidUnload() {
this.ionActionSheetDidUnload.emit(
{ actionsheet: this } as ActionSheetEvent
);
}
backdropClick() {
if (this.enableBackdropDismiss) {
// const opts: NavOptions = {
// minClickBlockDuration: 400
// };
this.dismiss();
}
}
render() {
let userCssClass = 'action-sheet-content';
if (this.cssClass) {
userCssClass += ' ' + this.cssClass;
}
return [
<div
onClick={this.backdropClick.bind(this)}
class={{
'action-sheet-backdrop': true,
'hide-backdrop': !this.showBackdrop
}}
/>,
<div class="action-sheet-wrapper" role="dialog">
<div class="action-sheet-container">
<div class="action-sheet-group">
{this.title
? <div class="action-sheet-title">{this.title}</div>
: null}
{this.subTitle
? <div class="action-sheet-sub-title">{this.subTitle}</div>
: null}
{this.buttons.map(b =>
<ion-button onClick={() => b.handler()}>{b.text}</ion-button>
)}
</div>
</div>
</div>
];
}
}
export interface ActionSheetOptions {
title?: string;
subTitle?: string;
cssClass?: string;
buttons?: (ActionSheetButton | string)[];
enableBackdropDismiss?: boolean;
}
export interface ActionSheetButtons {
text?: string;
role?: string;
icon?: string;
cssClass?: string;
handler?: () => boolean | void;
}
export interface ActionSheetEvent {
actionsheet: ActionSheet;
}

View File

@ -0,0 +1,109 @@
@import "../../themes/ionic.globals.ios";
@import "./loading";
// iOS Loading Indicator
// --------------------------------------------------
// deprecated
$loading-ios-padding: null !default;
/// @prop - Padding top of the loading wrapper
$loading-ios-padding-top: 24px !default;
/// @prop - Padding end of the loading wrapper
$loading-ios-padding-end: 34px !default;
/// @prop - Padding bottom of the loading wrapper
$loading-ios-padding-bottom: $loading-ios-padding-top !default;
/// @prop - Padding start of the loading wrapper
$loading-ios-padding-start: $loading-ios-padding-end !default;
/// @prop - Max width of the loading wrapper
$loading-ios-max-width: 270px !default;
/// @prop - Maximum height of the loading wrapper
$loading-ios-max-height: 90% !default;
/// @prop - Border radius of the loading wrapper
$loading-ios-border-radius: 8px !default;
/// @prop - Text color of the loading wrapper
$loading-ios-text-color: #000 !default;
/// @prop - Background of the loading wrapper
$loading-ios-background: #f8f8f8 !default;
/// @prop - Font weight of the loading content
$loading-ios-content-font-weight: bold !default;
/// @prop - Color of the loading spinner
$loading-ios-spinner-color: #69717d !default;
/// @prop - Color of the ios loading spinner
$loading-ios-spinner-ios-color: $loading-ios-spinner-color !default;
/// @prop - Color of the bubbles loading spinner
$loading-ios-spinner-bubbles-color: $loading-ios-spinner-color !default;
/// @prop - Color of the circles loading spinner
$loading-ios-spinner-circles-color: $loading-ios-spinner-color !default;
/// @prop - Color of the crescent loading spinner
$loading-ios-spinner-crescent-color: $loading-ios-spinner-color !default;
/// @prop - Color of the dots loading spinner
$loading-ios-spinner-dots-color: $loading-ios-spinner-color !default;
.loading-ios .loading-wrapper {
@include border-radius($loading-ios-border-radius);
max-width: $loading-ios-max-width;
max-height: $loading-ios-max-height;
color: $loading-ios-text-color;
background: $loading-ios-background;
@include deprecated-variable(padding, $loading-ios-padding) {
@include padding($loading-ios-padding-top, $loading-ios-padding-end, $loading-ios-padding-bottom, $loading-ios-padding-start);
}
}
// iOS Loading Content
// -----------------------------------------
.loading-ios .loading-content {
font-weight: $loading-ios-content-font-weight;
}
.loading-ios .loading-spinner + .loading-content {
@include margin-horizontal(16px, null);
}
// iOS Loading Spinner fill colors
// -----------------------------------------
.loading-ios .spinner-ios line,
.loading-ios .spinner-ios-small line {
stroke: $loading-ios-spinner-ios-color;
}
.loading-ios .spinner-bubbles circle {
fill: $loading-ios-spinner-bubbles-color;
}
.loading-ios .spinner-circles circle {
fill: $loading-ios-spinner-circles-color;
}
.loading-ios .spinner-crescent circle {
stroke: $loading-ios-spinner-crescent-color;
}
.loading-ios .spinner-dots circle {
fill: $loading-ios-spinner-dots-color;
}

View File

@ -0,0 +1,110 @@
@import "../../themes/ionic.globals.md";
@import "./loading";
// Material Design Loading Indicator
// --------------------------------------------------
// deprecated
$loading-md-padding: null !default;
/// @prop - Padding top of the loading wrapper
$loading-md-padding-top: 24px !default;
/// @prop - Padding end of the loading wrapper
$loading-md-padding-end: $loading-md-padding-top !default;
/// @prop - Padding bottom of the loading wrapper
$loading-md-padding-bottom: $loading-md-padding-top !default;
/// @prop - Padding start of the loading wrapper
$loading-md-padding-start: $loading-md-padding-end !default;
/// @prop - Max width of the loading wrapper
$loading-md-max-width: 280px !default;
/// @prop - Maximum height of the loading wrapper
$loading-md-max-height: 90% !default;
/// @prop - Border radius of the loading wrapper
$loading-md-border-radius: 2px !default;
/// @prop - Text color of the loading wrapper
$loading-md-text-color: rgba(0, 0, 0, .5) !default;
/// @prop - Background of the loading wrapper
$loading-md-background: #fafafa !default;
/// @prop - Box shadow color of the loading wrapper
$loading-md-box-shadow-color: rgba(0, 0, 0, .4) !default;
/// @prop - Box shadow of the loading wrapper
$loading-md-box-shadow: 0 16px 20px $loading-md-box-shadow-color !default;
/// @prop - Color of the loading spinner
$loading-md-spinner-color: color($colors-md, primary) !default;
/// @prop - Color of the ios loading spinner
$loading-md-spinner-ios-color: $loading-md-spinner-color !default;
/// @prop - Color of the bubbles loading spinner
$loading-md-spinner-bubbles-color: $loading-md-spinner-color !default;
/// @prop - Color of the circles loading spinner
$loading-md-spinner-circles-color: $loading-md-spinner-color !default;
/// @prop - Color of the crescent loading spinner
$loading-md-spinner-crescent-color: $loading-md-spinner-color !default;
/// @prop - Color of the dots loading spinner
$loading-md-spinner-dots-color: $loading-md-spinner-color !default;
.loading-md .loading-wrapper {
@include border-radius($loading-md-border-radius);
max-width: $loading-md-max-width;
max-height: $loading-md-max-height;
color: $loading-md-text-color;
background: $loading-md-background;
box-shadow: $loading-md-box-shadow;
@include deprecated-variable(padding, $loading-md-padding) {
@include padding($loading-md-padding-top, $loading-md-padding-end, $loading-md-padding-bottom, $loading-md-padding-start);
}
}
// Material Design Loading Content
// -----------------------------------------
.loading-md .loading-spinner + .loading-content {
@include margin-horizontal(16px, null);
}
// Material Design Loading Spinner fill colors
// -----------------------------------------
.loading-md .spinner-ios line,
.loading-md .spinner-ios-small line {
stroke: $loading-md-spinner-ios-color;
}
.loading-md .spinner-bubbles circle {
fill: $loading-md-spinner-bubbles-color;
}
.loading-md .spinner-circles circle {
fill: $loading-md-spinner-circles-color;
}
.loading-md .spinner-crescent circle {
stroke: $loading-md-spinner-crescent-color;
}
.loading-md .spinner-dots circle {
fill: $loading-md-spinner-dots-color;
}

View File

@ -0,0 +1,37 @@
@import "../../themes/ionic.globals";
// Loading Indicator
// --------------------------------------------------
ion-loading {
@include position(0, 0, 0, 0);
position: absolute;
z-index: $z-index-overlay;
display: flex;
align-items: center;
justify-content: center;
contain: strict;
}
ion-loading ion-gesture {
display: block;
width: 100%;
height: 100%;
visibility: inherit;
}
.loading-wrapper {
z-index: $z-index-overlay-wrapper;
display: flex;
align-items: center;
opacity: 0;
}

View File

@ -0,0 +1,102 @@
@import "../../themes/ionic.globals.wp";
@import "./loading";
// Windows Loading Indicator
// --------------------------------------------------
// deprecated
$loading-wp-padding: null !default;
/// @prop - Padding top of the loading wrapper
$loading-wp-padding-top: 20px !default;
/// @prop - Padding end of the loading wrapper
$loading-wp-padding-end: $loading-wp-padding-top !default;
/// @prop - Padding bottom of the loading wrapper
$loading-wp-padding-bottom: $loading-wp-padding-top !default;
/// @prop - Padding start of the loading wrapper
$loading-wp-padding-start: $loading-wp-padding-end !default;
/// @prop - Max width of the loading wrapper
$loading-wp-max-width: 280px !default;
/// @prop - Maximum height of the loading wrapper
$loading-wp-max-height: 90% !default;
/// @prop - Border radius of the loading wrapper
$loading-wp-border-radius: 2px !default;
/// @prop - Text color of the loading wrapper
$loading-wp-text-color: #fff !default;
/// @prop - Background of the loading wrapper
$loading-wp-background: #000 !default;
/// @prop - Color of the loading spinner
$loading-wp-spinner-color: $loading-wp-text-color !default;
/// @prop - Color of the ios loading spinner
$loading-wp-spinner-ios-color: $loading-wp-spinner-color !default;
/// @prop - Color of the bubbles loading spinner
$loading-wp-spinner-bubbles-color: $loading-wp-spinner-color !default;
/// @prop - Color of the circles loading spinner
$loading-wp-spinner-circles-color: $loading-wp-spinner-color !default;
/// @prop - Color of the crescent loading spinner
$loading-wp-spinner-crescent-color: $loading-wp-spinner-color !default;
/// @prop - Color of the dots loading spinner
$loading-wp-spinner-dots-color: $loading-wp-spinner-color !default;
.loading-wp .loading-wrapper {
@include border-radius($loading-wp-border-radius);
max-width: $loading-wp-max-width;
max-height: $loading-wp-max-height;
color: $loading-wp-text-color;
background: $loading-wp-background;
@include deprecated-variable(padding, $loading-wp-padding) {
@include padding($loading-wp-padding-top, $loading-wp-padding-end, $loading-wp-padding-bottom, $loading-wp-padding-start);
}
}
// Windows Loading Content
// -----------------------------------------
.loading-wp .loading-spinner + .loading-content {
@include margin-horizontal(16px, null);
}
// Windows Loading Spinner fill colors
// -----------------------------------------
.loading-wp .spinner-ios line,
.loading-wp .spinner-ios-small line {
stroke: $loading-wp-spinner-ios-color;
}
.loading-wp .spinner-bubbles circle {
fill: $loading-wp-spinner-bubbles-color;
}
.loading-wp .spinner-circles circle {
fill: $loading-wp-spinner-circles-color;
}
.loading-wp .spinner-crescent circle {
stroke: $loading-wp-spinner-crescent-color;
}
.loading-wp .spinner-dots circle {
fill: $loading-wp-spinner-dots-color;
}

View File

@ -0,0 +1,26 @@
/**
* iOS Loading Enter Animation
*/
export default function(baseElm: HTMLElement) {
const baseAnimation = new Ionic.Animation();
const backdropAnimation = new Ionic.Animation();
backdropAnimation.addElement(baseElm.querySelector('.loading-backdrop'));
const wrapperAnimation = new Ionic.Animation();
wrapperAnimation.addElement(baseElm.querySelector('.loading-wrapper'));
backdropAnimation.fromTo('opacity', 0.01, 0.3);
wrapperAnimation.fromTo('opacity', 0.01, 1)
.fromTo('scale', 1.1, 1);
return baseAnimation
.addElement(baseElm)
.easing('ease-in-out')
.duration(200)
.add(backdropAnimation)
.add(wrapperAnimation);
}

View File

@ -0,0 +1,27 @@
/**
* iOS Loading Leave Animation
*/
export default function(baseElm: HTMLElement) {
const baseAnimation = new Ionic.Animation();
const backdropAnimation = new Ionic.Animation();
backdropAnimation.addElement(baseElm.querySelector('.loading-backdrop'));
const wrapperAnimation = new Ionic.Animation();
wrapperAnimation.addElement(baseElm.querySelector('.loading-wrapper'));
backdropAnimation.fromTo('opacity', 0.3, 0);
wrapperAnimation.fromTo('opacity', 0.99, 0)
.fromTo('scale', 1, 0.9);
return baseAnimation
.addElement(baseElm)
.easing('ease-in-out')
.duration(200)
.add(backdropAnimation)
.add(wrapperAnimation);
}

View File

@ -0,0 +1,36 @@
@import "../../themes/ionic.globals";
// Popover Controller
// --------------------------------------------------
ion-popover-controller {
display: none;
}
// Popover Controller Backdrop
// --------------------------------------------------
/// @prop - Color of the backdrop
$popover-backdrop-color: #000 !default;
.popover-backdrop {
position: absolute;
top: 0;
left: 0;
z-index: $z-index-backdrop;
display: block;
width: 100%;
height: 100%;
background-color: $popover-backdrop-color;
opacity: .01;
transform: translateZ(0);
}
.popover-backdrop.backdrop-no-tappable {
cursor: auto;
}

View File

@ -0,0 +1,79 @@
import { Component, Listen } from '@stencil/core';
import { PopoverEvent, PopoverOptions, Popover, IonicControllerApi } from '../../index';
@Component({
tag: 'ion-popover-controller',
// styleUrl: 'popover-controller.scss'
})
export class PopoverController implements IonicControllerApi {
private ids = 0;
private popoverResolves: {[popoverId: string]: Function} = {};
private popovers: Popover[] = [];
private appRoot: Element;
ionViewDidLoad() {
this.appRoot = document.querySelector('ion-app') || document.body;
Ionic.loadController('popover', this);
}
load(opts?: PopoverOptions) {
// create ionic's wrapping ion-popover component
const popover = document.createElement('ion-popover');
const id = this.ids++;
// give this popover a unique id
popover.id = `popover-${id}`;
popover.style.zIndex = (10000 + id).toString();
// convert the passed in popover options into props
// that get passed down into the new popover
Object.assign(popover, opts);
// append the popover element to the document body
this.appRoot.appendChild(popover as any);
// store the resolve function to be called later up when the popover loads
return new Promise<Popover>(resolve => {
this.popoverResolves[popover.id] = resolve;
});
}
@Listen('body:ionPopoverDidLoad')
viewDidLoad(ev) {
const popover = ev.detail.popover;
const popoverResolve = this.popoverResolves[popover.id];
if (popoverResolve) {
popoverResolve(popover);
delete this.popoverResolves[popover.id];
}
}
@Listen('body:ionPopoverWillPresent')
willPresent(ev: PopoverEvent) {
this.popovers.push(ev.popover);
}
@Listen('body:ionPopoverWillDismiss, body:ionPopoverDidUnload')
willDismiss(ev: PopoverEvent) {
const index = this.popovers.indexOf(ev.popover);
if (index > -1) {
this.popovers.splice(index, 1);
}
}
@Listen('body:keyup.escape')
escapeKeyUp() {
const lastPopover = this.popovers[this.popovers.length - 1];
if (lastPopover) {
lastPopover.dismiss();
}
}
}

View File

@ -0,0 +1,22 @@
export default function(baseElm: HTMLElement) {
const baseAnimation = new Ionic.Animation();
const backdropAnimation = new Ionic.Animation();
backdropAnimation.addElement(baseElm.querySelector('.popover-backdrop'));
const wrapperAnimation = new Ionic.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);
return baseAnimation
.addElement(baseElm)
.easing('ease-in-out')
.duration(200)
.add(backdropAnimation)
.add(wrapperAnimation);
}

View File

@ -0,0 +1,28 @@
/**
* iOS Modal Leave Animation
*/
export default function(baseElm: HTMLElement) {
const baseAnimation = new Ionic.Animation();
const backdropAnimation = new Ionic.Animation();
backdropAnimation.addElement(baseElm.querySelector('.modal-backdrop'));
const wrapperAnimation = new Ionic.Animation();
const wrapperElm = baseElm.querySelector('.modal-wrapper');
wrapperAnimation.addElement(wrapperElm);
const wrapperElmRect = wrapperElm.getBoundingClientRect();
wrapperAnimation.beforeStyles({ 'opacity': 1 })
.fromTo('translateY', '0%', `${window.innerHeight - wrapperElmRect.top}px`);
backdropAnimation.fromTo('opacity', 0.4, 0.0);
return baseAnimation
.addElement(baseElm)
.easing('ease-out')
.duration(250)
.add(backdropAnimation)
.add(wrapperAnimation);
}

View File

@ -0,0 +1,24 @@
@import "../../themes/ionic.globals.ios";
@import "./modal";
// iOS Modals
// --------------------------------------------------
/// @prop - Background color for the modal
$modal-ios-background-color: $background-ios-color !default;
/// @prop - Border radius for the modal
$modal-ios-border-radius: 10px !default;
.modal-wrapper-ios {
// 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) {
@include border-radius($modal-ios-border-radius);
overflow: hidden;
}
}

View File

@ -0,0 +1,54 @@
@import "../../themes/ionic.globals";
@import "./popover";
// Material Design Popover
// --------------------------------------------------
/// @prop - Width of the popover content
$popover-md-width: 250px !default;
/// @prop - Min width of the popover content
$popover-md-min-width: 0 !default;
/// @prop - Minimum height of the popover content
$popover-md-min-height: 0 !default;
/// @prop - Maximum height of the popover content
$popover-md-max-height: 90% !default;
/// @prop - Border radius of the popover content
$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;
/// @prop - Background of the popover content
// $popover-md-background: $background-md-color !default;
$popover-md-background: black !default;
/// @prop - Box shadow color of the popover content
$popover-md-box-shadow-color: rgba(0, 0, 0, .3) !default;
/// @prop - Box shadow of the popover content
$popover-md-box-shadow: 0 3px 12px 2px $popover-md-box-shadow-color !default;
.popover-md .popover-content {
@include border-radius($popover-md-border-radius);
@include transform-origin(start, top);
width: $popover-md-width;
min-width: $popover-md-min-width;
min-height: $popover-md-min-height;
max-height: $popover-md-max-height;
color: $popover-md-text-color;
// background: $popover-md-background;
background: #ffffff;
box-shadow: $popover-md-box-shadow;
}
.popover-md .popover-viewport {
opacity: 0;
transition-delay: 100ms;
}

View File

@ -0,0 +1,55 @@
@import "../../themes/ionic.globals";
// Popover
// --------------------------------------------------
ion-popover {
@include position(0, 0, 0, 0);
position: absolute;
z-index: $z-index-overlay;
display: flex;
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;
}
.popover-content {
position: absolute;
z-index: $z-index-overlay-wrapper;
display: flex;
overflow: auto;
flex-direction: column;
}
.popover-content ion-content, .popover-content .scroll-content {
contain: none;
}
.popover-content .scroll-content {
position: relative;
}

View File

@ -0,0 +1,180 @@
import { Component, Element, Event, EventEmitter, Listen, Prop } from '@stencil/core';
import { AnimationBuilder, Animation } 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',
md: 'popover.md.scss',
// wp: 'popover.wp.scss'
},
host: {
theme: 'popover'
}
})
export class Popover {
@Element() private el: HTMLElement;
@Event() ionPopoverDidLoad: EventEmitter;
@Event() ionPopoverWillPresent: EventEmitter;
@Event() ionPopoverDidPresent: EventEmitter;
@Event() ionPopoverWillDismiss: EventEmitter;
@Event() ionPopoverDidDismiss: EventEmitter;
@Event() ionPopoverDidUnload: EventEmitter;
@Prop() mode: string;
@Prop() color: string;
@Prop() component: string;
@Prop() componentProps: any = {};
@Prop() cssClass: string;
@Prop() enableBackdropDismiss: boolean = true;
@Prop() enterAnimation: AnimationBuilder;
@Prop() exitAnimation: AnimationBuilder;
@Prop() ev: Event;
@Prop() id: string;
@Prop() showBackdrop: boolean = true;
private animation: Animation;
@Listen('ionDismiss')
onDismiss(ev: UIEvent) {
ev.stopPropagation();
ev.preventDefault();
this.dismiss();
}
ionViewDidLoad() {
this.ionPopoverDidLoad.emit({ popover: this });
}
present() {
return new Promise<void>(resolve => {
this._present(resolve);
});
}
private _present(resolve: Function) {
if (this.animation) {
this.animation.destroy();
this.animation = null;
}
this.ionPopoverWillPresent.emit({ popover: this });
// get the user's animation fn if one was provided
// 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 });
resolve();
// }).play();
}
dismiss() {
// 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;
// }
// build the animation and kick it off
// this.animation = animationBuilder(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();
});
}
ionViewDidUnload() {
this.ionPopoverDidUnload.emit({ popover: this });
}
backdropClick() {
if (this.enableBackdropDismiss) {
// const opts: NavOptions = {
// minClickBlockDuration: 400
// };
this.dismiss();
}
}
render() {
const ThisComponent = this.component;
let userCssClasses = 'popover-content';
if (this.cssClass) {
userCssClasses += ` ${this.cssClass}`;
}
const dialogClasses = createThemedClasses(this.mode, this.color, 'popover-wrapper');
const thisComponentClasses = createThemedClasses(this.mode, this.color, userCssClasses);
return [
<div
onClick={this.backdropClick.bind(this)}
class={{
'popover-backdrop': true,
'hide-backdrop': !this.showBackdrop
}}
></div>,
<div
role='dialog'
class={dialogClasses}
>
<ThisComponent
props={this.componentProps}
class={thisComponentClasses}
>
</ThisComponent>
</div>
];
}
}
export interface PopoverOptions {
component: string;
componentProps?: any;
showBackdrop?: boolean;
enableBackdropDismiss?: boolean;
enterAnimation?: AnimationBuilder;
exitAnimation?: AnimationBuilder;
cssClass?: string;
ev: Event;
}
export interface PopoverEvent {
popover: Popover;
}

View File

@ -0,0 +1,16 @@
@import "../../themes/ionic.globals.wp";
@import "./modal";
// Windows Modals
// --------------------------------------------------
/// @prop - Background color for the modal
$modal-wp-background-color: $background-wp-color !default;
.modal-wrapper-wp {
@include transform(translate3d(0, 40px, 0));
opacity: .01;
}

View File

@ -1,5 +1,7 @@
import { AnimationController } from './components/animation/animation';
import { Animation, AnimationBuilder } from './components/animation/animation-interface';
import { ActionSheet, ActionSheetButtons, ActionSheetEvent, ActionSheetOptions } from './components/action-sheet/action-sheet';
import { ActionSheetController } from './components/action-sheet-controller/action-sheet-controller';
import { Loading, LoadingEvent, LoadingOptions } from './components/loading/loading';
import { LoadingController } from './components/loading-controller/loading-controller';
import { GestureDetail, GestureCallback } from './components/gesture/gesture';
@ -8,6 +10,10 @@ import { MenuType } from './components/menu/menu-types';
import { MenuController } from './components/menu/menu-controller';
import { Modal, ModalOptions, ModalEvent } from './components/modal/modal';
import { ModalController } from './components/modal-controller/modal-controller';
import { Popover, PopoverEvent, PopoverOptions } from './components/popover/popover'
import { PopoverController } from './components/popover-controller/popover-controller'
import { Scroll, ScrollCallback, ScrollDetail } from './components/scroll/scroll';
import { Segment } from './components/segment/segment';
import { SegmentButton, SegmentButtonEvent } from './components/segment-button/segment-button';
@ -41,6 +47,11 @@ export interface BooleanInputComponent extends BaseInputComponent {
export {
ActionSheet,
ActionSheetButtons,
ActionSheetEvent,
ActionSheetOptions,
ActionSheetController,
Animation,
AnimationBuilder,
AnimationController,
@ -57,6 +68,10 @@ export {
ModalController,
ModalOptions,
ModalEvent,
Popover,
PopoverController,
PopoverEvent,
PopoverOptions,
Scroll,
ScrollCallback,
ScrollDetail,