Merge branch 'core/overlay' into mono-refactor

This commit is contained in:
Brandy Carney
2017-08-15 17:10:15 -04:00
37 changed files with 1945 additions and 30 deletions

View File

@ -162,8 +162,8 @@
<ion-button>Default</ion-button>
<ion-loading-ctrl></ion-loading-ctrl>
<ion-modal-ctrl></ion-modal-ctrl>
<ion-loading-controller></ion-loading-controller>
<ion-modal-controller></ion-modal-controller>
<script>
@ -219,8 +219,8 @@
console.log('open profile modal for userId:', userId);
var modalCtrl = document.querySelector('ion-modal-ctrl');
modalCtrl.create({ component: 'profile-page', componentProps: { userId: userId } }).then(modal => {
var modalController = document.querySelector('ion-modal-controller');
modalController.create({ component: 'profile-page', componentProps: { userId: userId } }).then(modal => {
console.log('start presenting modal, userId:', modal.componentProps.userId);
modal.present().then(() => {
@ -271,8 +271,8 @@
document.getElementById('presentLoading').addEventListener('click', function() {
console.log('open loading');
var loadingCtrl = document.querySelector('ion-loading-ctrl');
loadingCtrl.create({ duration: 1000 }).then(loading => {
var loadingController = document.querySelector('ion-loading-controller');
loadingController.create({ duration: 1000 }).then(loading => {
console.log('start presenting loading');
loading.present().then(() => {
@ -285,10 +285,10 @@
document.getElementById('toggleMenu').addEventListener('click', function() {
console.log('toggle menu');
Ionic.controller('menu').then(menuCtrl => {
Ionic.controller('menu').then(menucontroller => {
console.log('start menu toggle');
menuCtrl.toggle().then(() => {
menucontroller.toggle().then(() => {
console.log('finished menu toggle');
});
});

View File

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

View File

@ -0,0 +1,189 @@
@import "../../themes/ionic.globals.ios";
@import "./action-sheet";
// iOS Action Sheet
// --------------------------------------------------
/// @prop - Text align of the action sheet
$action-sheet-ios-text-align: center !default;
// deprecated
$action-sheet-ios-padding: null !default;
/// @prop - Padding top of the action sheet
$action-sheet-ios-padding-top: 0 !default;
/// @prop - Padding end of the action sheet
$action-sheet-ios-padding-end: 10px !default;
/// @prop - Padding bottom of the action sheet
$action-sheet-ios-padding-bottom: $action-sheet-ios-padding-top !default;
/// @prop - Padding start of the action sheet
$action-sheet-ios-padding-start: $action-sheet-ios-padding-end !default;
/// @prop - Bottom margin of the action sheet button group
$action-sheet-ios-group-margin-bottom: 10px !default;
/// @prop - Background color of the action sheet
$action-sheet-ios-background: #f9f9f9 !default;
/// @prop - Border color of the action sheet
$action-sheet-ios-border-color: #d6d6da !default;
/// @prop - Border radius of the action sheet
$action-sheet-ios-border-radius: 13px !default;
/// @prop - Padding of the action sheet title
$action-sheet-ios-title-padding: 1.5rem !default;
/// @prop - Color of the action sheet title
$action-sheet-ios-title-color: #8f8f8f !default;
/// @prop - Font size of the action sheet title
$action-sheet-ios-title-font-size: 1.3rem !default;
/// @prop - Font weight of the action sheet title
$action-sheet-ios-title-font-weight: 400 !default;
/// @prop - Border radius of the action sheet title
$action-sheet-ios-title-border-radius: 0 !default;
/// @prop - Minimum height of the action sheet button
$action-sheet-ios-button-min-height: 5.6rem !default;
/// @prop - Padding of the action sheet button
$action-sheet-ios-button-padding: 18px !default;
/// @prop - Text color of the action sheet button
$action-sheet-ios-button-text-color: #007aff !default;
/// @prop - Fill color of the action sheet button icon
$action-sheet-ios-button-icon-fill-color: $action-sheet-ios-button-text-color !default;
/// @prop - Font size of the action sheet button icon
$action-sheet-ios-button-icon-font-size: 1.4em !default;
/// @prop - Padding right of the action sheet button icon
$action-sheet-ios-button-icon-padding-right: 0.1em !default;
/// @prop - Height of the action sheet button icon
$action-sheet-ios-button-icon-height: .7em !default;
/// @prop - Margin top of the action sheet button icon
$action-sheet-ios-button-icon-margin-top: -10px !default;
/// @prop - Font size of the action sheet button
$action-sheet-ios-button-font-size: 2rem !default;
/// @prop - Border width of the action sheet button
$action-sheet-ios-button-border-width: $hairlines-width !default;
/// @prop - Border style of the action sheet button
$action-sheet-ios-button-border-style: solid !default;
/// @prop - Border color of the action sheet button
$action-sheet-ios-button-border-color: #d1d3d6 !default;
/// @prop - Background color of the action sheet button
$action-sheet-ios-button-background: transparent !default;
/// @prop - Background color of the activated action sheet button
$action-sheet-ios-button-background-activated: #ebebeb !default;
/// @prop - Destructive text color of the action sheet button
$action-sheet-ios-button-destructive-text-color: #f53d3d !default;
/// @prop - Destructive fill color of the action sheet button icon
$action-sheet-ios-button-destructive-icon-fill-color: $action-sheet-ios-button-destructive-text-color !default;
/// @prop - Background color of the action sheet cancel button
$action-sheet-ios-button-cancel-background: #fff !default;
/// @prop - Font weight of the action sheet cancel button
$action-sheet-ios-button-cancel-font-weight: 600 !default;
.action-sheet-ios {
@include text-align($action-sheet-ios-text-align);
}
.action-sheet-ios .action-sheet-container {
@include deprecated-variable(padding, $action-sheet-ios-padding) {
@include padding($action-sheet-ios-padding-top, $action-sheet-ios-padding-end, $action-sheet-ios-padding-bottom, $action-sheet-ios-padding-start);
}
}
.action-sheet-ios .action-sheet-group {
@include border-radius($action-sheet-ios-border-radius);
@include margin(null, null, $action-sheet-ios-group-margin-bottom - 2, null);
overflow: hidden;
background: $action-sheet-ios-background;
}
.action-sheet-ios .action-sheet-group:last-child {
@include margin(null, null, $action-sheet-ios-group-margin-bottom, null);
}
.action-sheet-ios .action-sheet-title {
@include padding($action-sheet-ios-title-padding);
@include text-align($action-sheet-ios-text-align);
@include border-radius($action-sheet-ios-title-border-radius);
border-bottom: $action-sheet-ios-button-border-width $action-sheet-ios-button-border-style $action-sheet-ios-border-color;
font-size: $action-sheet-ios-title-font-size;
font-weight: $action-sheet-ios-title-font-weight;
color: $action-sheet-ios-title-color;
}
.action-sheet-ios .action-sheet-button {
@include margin(0);
@include padding($action-sheet-ios-button-padding);
min-height: $action-sheet-ios-button-min-height;
border-bottom: $action-sheet-ios-button-border-width $action-sheet-ios-button-border-style $action-sheet-ios-border-color;
font-size: $action-sheet-ios-button-font-size;
color: $action-sheet-ios-button-text-color;
background: $action-sheet-ios-button-background;
}
.action-sheet-ios .action-sheet-button .action-sheet-icon {
fill: $action-sheet-ios-button-icon-fill-color;
font-size: $action-sheet-ios-button-icon-font-size;
padding-right: $action-sheet-ios-button-icon-padding-right;
height: $action-sheet-ios-button-icon-height;
margin-top: $action-sheet-ios-button-icon-margin-top;
}
.action-sheet-ios .action-sheet-button:last-child {
border-bottom-color: transparent;
}
.action-sheet-ios .action-sheet-button.activated {
@include margin(-$action-sheet-ios-button-border-width, null, null, null);
border-top: $action-sheet-ios-button-border-width $action-sheet-ios-button-border-style $action-sheet-ios-button-background-activated;
border-bottom-color: $action-sheet-ios-button-background-activated;
background: $action-sheet-ios-button-background-activated;
}
.action-sheet-ios .action-sheet-selected {
font-weight: bold;
background: #fff;
}
.action-sheet-ios .action-sheet-destructive {
color: $action-sheet-ios-button-destructive-text-color;
}
.action-sheet-ios .action-sheet-destructive .action-sheet-icon {
fill: $action-sheet-ios-button-destructive-icon-fill-color;
}
.action-sheet-ios .action-sheet-cancel {
font-weight: $action-sheet-ios-button-cancel-font-weight;
background: $action-sheet-ios-button-cancel-background;
}

View File

@ -0,0 +1,158 @@
@import "../../themes/ionic.globals.md";
@import "./action-sheet";
// Material Design Action Sheet
// --------------------------------------------------
/// @prop - Text align of the action sheet
$action-sheet-md-text-align: start !default;
/// @prop - Background color of the action sheet
$action-sheet-md-background: #fafafa !default;
/// @prop - Bottom margin of the action sheet button group
$action-sheet-md-group-margin-bottom: 8px !default;
/// @prop - Color of the action sheet title
$action-sheet-md-title-color: #757575 !default;
/// @prop - Font size of the action sheet title
$action-sheet-md-title-font-size: 1.6rem !default;
// deprecated
$action-sheet-md-title-padding: null !default;
/// @prop - Padding top of the action sheet title
$action-sheet-md-title-padding-top: 11px !default;
/// @prop - Padding end of the action sheet title
$action-sheet-md-title-padding-end: 16px !default;
/// @prop - Padding bottom of the action sheet title
$action-sheet-md-title-padding-bottom: 17px !default;
/// @prop - Padding start of the action sheet title
$action-sheet-md-title-padding-start: $action-sheet-md-title-padding-end !default;
/// @prop - Minimum height of the action sheet button
$action-sheet-md-button-min-height: 4.8rem !default;
/// @prop - Text color of the action sheet button
$action-sheet-md-button-text-color: #222 !default;
/// @prop - Font size of the action sheet button
$action-sheet-md-button-font-size: 1.6rem !default;
// deprecated
$action-sheet-md-button-padding: null !default;
/// @prop - Padding top of the action sheet button
$action-sheet-md-button-padding-top: 0 !default;
/// @prop - Padding end of the action sheet button
$action-sheet-md-button-padding-end: 16px !default;
/// @prop - Padding bottom of the action sheet button
$action-sheet-md-button-padding-bottom: $action-sheet-md-button-padding-top !default;
/// @prop - Padding start of the action sheet button
$action-sheet-md-button-padding-start: $action-sheet-md-button-padding-end !default;
/// @prop - Background color of the action sheet button
$action-sheet-md-button-background: transparent !default;
/// @prop - Background color of the action sheet activated button
$action-sheet-md-button-background-activated: #f1f1f1 !default;
/// @prop - Font size of the icon in the action sheet button
$action-sheet-md-icon-font-size: 2.4rem !default;
/// @prop - Width of the icon in the action sheet button
$action-sheet-md-icon-width: 2.3rem !default;
/// @prop - Text align of the icon in the action sheet button
$action-sheet-md-icon-text-align: center !default;
/// @prop - Vertical align of the icon in the action sheet button
$action-sheet-md-icon-vertical-align: middle !default;
// deprecated
$action-sheet-md-icon-margin: null !default;
/// @prop - Margin top of the icon in the action sheet button
$action-sheet-md-icon-margin-top: 0 !default;
/// @prop - Margin end of the icon in the action sheet button
$action-sheet-md-icon-margin-end: 32px !default;
/// @prop - Margin bottom of the icon in the action sheet button
$action-sheet-md-icon-margin-bottom: 0 !default;
/// @prop - Margin start of the icon in the action sheet button
$action-sheet-md-icon-margin-start: 0 !default;
.action-sheet-md .action-sheet-container {
@include padding(.8rem, 0);
background: $action-sheet-md-background;
}
.action-sheet-md .action-sheet-title,
.action-sheet-md .action-sheet-sub-title {
@include text-align($action-sheet-md-text-align);
font-size: $action-sheet-md-title-font-size;
color: $action-sheet-md-title-color;
@include deprecated-variable(padding, $action-sheet-md-title-padding) {
@include padding($action-sheet-md-title-padding-top, $action-sheet-md-title-padding-end, $action-sheet-md-title-padding-bottom, $action-sheet-md-title-padding-start);
}
}
.action-sheet-md .action-sheet-button {
@include text-align($action-sheet-md-text-align);
position: relative;
overflow: hidden;
min-height: $action-sheet-md-button-min-height;
font-size: $action-sheet-md-button-font-size;
color: $action-sheet-md-button-text-color;
background: $action-sheet-md-button-background;
@include deprecated-variable(padding, $action-sheet-md-button-padding) {
@include padding($action-sheet-md-button-padding-top, $action-sheet-md-button-padding-end, $action-sheet-md-button-padding-bottom, $action-sheet-md-button-padding-start);
}
}
.action-sheet-md .action-sheet-button.activated {
background: $action-sheet-md-button-background-activated;
}
.action-sheet-md .action-sheet-icon {
@include padding(0);
@include text-align($action-sheet-md-icon-text-align);
width: $action-sheet-md-icon-width;
font-size: $action-sheet-md-icon-font-size;
vertical-align: $action-sheet-md-icon-vertical-align;
@include deprecated-variable(margin, $action-sheet-md-icon-margin) {
@include margin($action-sheet-md-icon-margin-top, $action-sheet-md-icon-margin-end, $action-sheet-md-icon-margin-bottom, $action-sheet-md-icon-margin-start);
}
}
.action-sheet-md .action-sheet-group {
overflow: hidden;
}
.action-sheet-md .action-sheet-group .button-inner {
justify-content: flex-start;
}
.action-sheet-md .action-sheet-selected {
font-weight: bold;
}

View File

@ -0,0 +1,42 @@
@import "../../themes/ionic.globals";
// Action Sheet
// --------------------------------------------------
/// @prop - Width of the action sheet
$action-sheet-width: 100% !default;
/// @prop - Maximum width of the action sheet
$action-sheet-max-width: 500px !default;
ion-action-sheet {
@include position(0, null, null, 0);
position: absolute;
z-index: $z-index-overlay;
display: block;
width: $action-sheet-width;
height: $action-sheet-width;
}
.action-sheet-wrapper {
@include position(null, 0, 0, 0);
@include margin(auto);
@include transform(translate3d(0, 100%, 0));
position: absolute;
z-index: $z-index-overlay-wrapper;
display: block;
width: $action-sheet-width;
max-width: $action-sheet-max-width;
}
.action-sheet-button {
width: $action-sheet-width;
}
ion-action-sheet-controller {
display: none;
}

View File

@ -0,0 +1,254 @@
import { Component, CssClassMap, Element, Event, EventEmitter, Listen, Prop } from '@stencil/core';
import { AnimationBuilder, Animation, AnimationController, Config } 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;
@Element() private el: HTMLElement;
@Event() private ionActionSheetDidLoad: EventEmitter;
@Event() private ionActionSheetDidPresent: EventEmitter;
@Event() private ionActionSheetWillPresent: EventEmitter;
@Event() private ionActionSheetWillDismiss: EventEmitter;
@Event() private ionActionSheetDidDismiss: EventEmitter;
@Event() private ionActionSheetDidUnload: EventEmitter;
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
@Prop({ context: 'config' }) config: Config;
@Prop() cssClass: string;
@Prop() title: string;
@Prop() subTitle: string;
@Prop() buttons: ActionSheetButton[];
@Prop() enableBackdropDismiss: boolean = true;
@Prop() showBackdrop: boolean = true;
@Prop() enterAnimation: AnimationBuilder;
@Prop() exitAnimation: AnimationBuilder;
@Prop() id: string;
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 });
// get the user's animation fn if one was provided
let animationBuilder = this.enterAnimation;
if (!animationBuilder) {
// user did not provide a custom animation fn
// decide from the config which animation to use
animationBuilder = iOSEnterAnimation;
}
// build the animation and kick it off
this.animationCtrl.create(animationBuilder, this.el).then(animation => {
this.animation = animation;
animation.onFinish((a: any) => {
a.destroy();
this.ionViewDidEnter();
resolve();
}).play();
});
}
dismiss() {
if (this.animation) {
this.animation.destroy();
this.animation = null;
}
return new Promise(resolve => {
this.ionActionSheetWillDismiss.emit({ actionSheet: 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
animationBuilder = iOSLeaveAnimation;
}
// build the animation and kick it off
this.animationCtrl.create(animationBuilder, this.el).then(animation => {
this.animation = animation;
animation.onFinish((a: any) => {
a.destroy();
this.ionActionSheetDidDismiss.emit({ actionSheet: this });
Context.dom.write(() => {
this.el.parentNode.removeChild(this.el);
});
resolve();
}).play();
});
});
}
protected ionViewDidUnload() {
this.ionActionSheetDidUnload.emit({ actionSheet: this });
}
@Listen('ionDismiss')
protected onDismiss(ev: UIEvent) {
ev.stopPropagation();
ev.preventDefault();
this.dismiss();
}
protected ionViewDidLoad() {
this.ionActionSheetDidLoad.emit({ actionSheet: this });
}
protected ionViewDidEnter() {
this.ionActionSheetDidPresent.emit({ loading: this });
}
protected backdropClick() {
if (this.enableBackdropDismiss) {
// const opts: NavOptions = {
// minClickBlockDuration: 400
// };
this.dismiss();
}
}
protected click(button: ActionSheetButton) {
let shouldDismiss = true;
if (button.handler) {
if (button.handler() === false) {
shouldDismiss = false;
}
}
if (shouldDismiss) {
this.dismiss();
}
}
protected render() {
let userCssClass = 'action-sheet-content';
if (this.cssClass) {
userCssClass += ' ' + this.cssClass;
}
let cancelButton: ActionSheetButton;
let buttons = this.buttons
.map(b => {
if (typeof b === 'string') {
b = { text: b };
}
if (!b.cssClass) {
b.cssClass = '';
}
if (b.role === 'cancel') {
cancelButton = b;
return null;
}
return b;
})
.filter(b => b !== null);
return [
<ion-backdrop
onClick={this.backdropClick.bind(this)}
class="action-sheet-backdrop"
/>,
<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}
{buttons.map(b =>
<button class={this.buttonClass(b)} onClick={() => this.click(b)}>
<span class="button-inner">
{b.icon
? <ion-icon name={b.icon} class="action-sheet-icon" />
: null}
{b.text}
</span>
</button>
)}
</div>
{cancelButton
? <div class="action-sheet-group">
<button
class={this.buttonClass(cancelButton)}
onClick={() => this.click(cancelButton)}
>
{cancelButton.icon
? <ion-icon
name={cancelButton.icon}
class="action-sheet-icon"
/>
: null}
{cancelButton.text}
</button>
</div>
: null}
</div>
</div>
];
}
buttonClass(button: ActionSheetButton): CssClassMap {
let buttonClass: string[] = !button.role
? ['action-sheet-button']
: [`action-sheet-button`, `action-sheet-${button.role}`];
return buttonClass.reduce((prevValue: any, cssClass: any) => {
prevValue[cssClass] = true;
return prevValue;
}, {});
}
}
export interface ActionSheetOptions {
title?: string;
subTitle?: string;
cssClass?: string;
buttons?: (ActionSheetButton | string)[];
enableBackdropDismiss?: boolean;
}
export interface ActionSheetButton {
text?: string;
role?: string;
icon?: string;
cssClass?: string;
handler?: () => boolean | void;
}
export interface ActionSheetEvent {
detail: {
actionSheet: ActionSheet;
};
}

View File

@ -0,0 +1,168 @@
@import "../../themes/ionic.globals.wp";
@import "./action-sheet";
// Windows Action Sheet
// --------------------------------------------------
/// @prop - Text align of the action sheet
$action-sheet-wp-text-align: start !default;
/// @prop - Background color of the action sheet
$action-sheet-wp-background: #fff !default;
/// @prop - Box shadow color of the action sheet
$action-sheet-wp-box-shadow-color: rgba(0, 0, 0, .2) !default;
/// @prop - Box shadow of the action sheet
$action-sheet-wp-box-shadow: 0 -1px 0 $action-sheet-wp-box-shadow-color !default;
// deprecated
$action-sheet-wp-title-padding: null !default;
/// @prop - Padding top of the action sheet title
$action-sheet-wp-title-padding-top: 11px !default;
/// @prop - Padding end of the action sheet title
$action-sheet-wp-title-padding-end: 16px !default;
/// @prop - Padding bottom of the action sheet title
$action-sheet-wp-title-padding-bottom: 17px !default;
/// @prop - Padding start of the action sheet title
$action-sheet-wp-title-padding-start: $action-sheet-wp-title-padding-end !default;
/// @prop - Font size of the action sheet title
$action-sheet-wp-title-font-size: 2rem !default;
/// @prop - Color of the action sheet title
$action-sheet-wp-title-color: #4d4d4d !default;
/// @prop - Text align of the action sheet title
$action-sheet-wp-title-text-align: $action-sheet-wp-text-align !default;
/// @prop - Height of the action sheet button
$action-sheet-wp-button-height: 4.8rem !default;
/// @prop - Text color of the action sheet button
$action-sheet-wp-button-text-color: #4d4d4d !default;
/// @prop - Font size of the action sheet button
$action-sheet-wp-button-font-size: 1.5rem !default;
// deprecated
$action-sheet-wp-button-padding: null !default;
/// @prop - Padding top of the action sheet button
$action-sheet-wp-button-padding-top: 0 !default;
/// @prop - Padding end of the action sheet button
$action-sheet-wp-button-padding-end: 16px !default;
/// @prop - Padding bottom of the action sheet button
$action-sheet-wp-button-padding-bottom: $action-sheet-wp-button-padding-top !default;
/// @prop - Padding start of the action sheet button
$action-sheet-wp-button-padding-start: $action-sheet-wp-button-padding-end !default;
/// @prop - Text align of the action sheet button
$action-sheet-wp-button-text-align: $action-sheet-wp-text-align !default;
/// @prop - Background color of the action sheet button
$action-sheet-wp-button-background: transparent !default;
/// @prop - Background color of the action sheet activated button
$action-sheet-wp-button-background-activated: $list-wp-activated-background-color !default;
/// @prop - Font size of the icon in the action sheet button
$action-sheet-wp-icon-font-size: 2.4rem !default;
/// @prop - Width of the icon in the action sheet button
$action-sheet-wp-icon-width: 2.3rem !default;
/// @prop - Text align of the icon in the action sheet button
$action-sheet-wp-icon-text-align: center !default;
/// @prop - Vertical align of the icon in the action sheet button
$action-sheet-wp-icon-vertical-align: middle !default;
// deprecated
$action-sheet-wp-icon-margin: null !default;
/// @prop - Margin top of the icon in the action sheet button
$action-sheet-wp-icon-margin-top: 0 !default;
/// @prop - Margin end of the icon in the action sheet button
$action-sheet-wp-icon-margin-end: 20px !default;
/// @prop - Margin bottom of the icon in the action sheet button
$action-sheet-wp-icon-margin-bottom: 0 !default;
/// @prop - Margin start of the icon in the action sheet button
$action-sheet-wp-icon-margin-start: 0 !default;
.action-sheet-wp .action-sheet-wrapper {
box-shadow: $action-sheet-wp-box-shadow;
}
.action-sheet-wp .action-sheet-title {
@include text-align($action-sheet-wp-title-text-align);
font-size: $action-sheet-wp-title-font-size;
color: $action-sheet-wp-title-color;
@include deprecated-variable(padding, $action-sheet-wp-title-padding) {
@include padding($action-sheet-wp-title-padding-top, $action-sheet-wp-title-padding-end, $action-sheet-wp-title-padding-bottom, $action-sheet-wp-title-padding-start);
}
}
.action-sheet-wp .action-sheet-button {
@include text-align($action-sheet-wp-button-text-align);
min-height: $action-sheet-wp-button-height;
font-size: $action-sheet-wp-button-font-size;
color: $action-sheet-wp-button-text-color;
background: $action-sheet-wp-button-background;
@include deprecated-variable(padding, $action-sheet-wp-button-padding) {
@include padding($action-sheet-wp-button-padding-top, $action-sheet-wp-button-padding-end, $action-sheet-wp-button-padding-bottom, $action-sheet-wp-button-padding-start);
}
}
.action-sheet-wp .action-sheet-button.activated {
background: $action-sheet-wp-button-background-activated;
}
.action-sheet-wp .action-sheet-icon {
@include padding(0);
@include text-align($action-sheet-wp-icon-text-align);
width: $action-sheet-wp-icon-width;
font-size: $action-sheet-wp-icon-font-size;
vertical-align: $action-sheet-wp-icon-vertical-align;
@include deprecated-variable(margin, $action-sheet-wp-icon-margin) {
@include margin($action-sheet-wp-icon-margin-top, $action-sheet-wp-icon-margin-end, $action-sheet-wp-icon-margin-bottom, $action-sheet-wp-icon-margin-start);
}
}
.action-sheet-wp .action-sheet-container {
@include padding(.8rem, 0);
background: $action-sheet-wp-background;
}
.action-sheet-wp .action-sheet-group .button-inner {
justify-content: flex-start;
}
.action-sheet-wp .action-sheet-selected {
font-weight: bold;
}
.action-sheet-wp .action-sheet-cancel {
background: $action-sheet-wp-button-background;
}

View File

@ -0,0 +1,25 @@
import { Animation } from '../../../index';
/**
* iOS Action Sheet Animation
*/
export default function(Animation: Animation, baseElm: HTMLElement) {
const baseAnimation = new Animation();
const backdropAnimation = new Animation();
backdropAnimation.addElement(baseElm.querySelector('.action-sheet-backdrop'));
const wrapperAnimation = new Animation();
wrapperAnimation.addElement(baseElm.querySelector('.action-sheet-wrapper'));
backdropAnimation.fromTo('opacity', 0.01, 0.4);
wrapperAnimation.fromTo('translateY', '100%', '0%');
return baseAnimation
.addElement(baseElm)
.easing('cubic-bezier(.36,.66,.04,1)')
.duration(400)
.add(backdropAnimation)
.add(wrapperAnimation);
}

View File

@ -0,0 +1,25 @@
import { Animation } from '../../../index';
/**
* iOS Action Sheet Leave Animation
*/
export default function(Animation: Animation, baseElm: HTMLElement) {
const baseAnimation = new Animation();
const backdropAnimation = new Animation();
backdropAnimation.addElement(baseElm.querySelector('.action-sheet-backdrop'));
const wrapperAnimation = new Animation();
wrapperAnimation.addElement(baseElm.querySelector('.action-sheet-wrapper'));
backdropAnimation.fromTo('opacity', 0.4, 0);
wrapperAnimation.fromTo('translateY', '0%', '100%');
return baseAnimation
.addElement(baseElm)
.easing('cubic-bezier(.36,.66,.04,1)')
.duration(450)
.add(backdropAnimation)
.add(wrapperAnimation);
}

View File

@ -0,0 +1,125 @@
<!DOCTYPE html>
<html dir="ltr">
<head>
<meta charset="UTF-8">
<title>Action Sheet Basic</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<script src="/dist/ionic.js"></script>
</head>
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Action Sheet</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding>
<ion-action-sheet-controller></ion-action-sheet-controller>
<ion-button block onclick="presentActionSheet1()">Present Action Sheet 1</ion-button>
<ion-button block onclick="presentActionSheet2()">Present Action Sheet 2</ion-button>
<ion-button block onclick="presentActionSheet3()">Present Action Sheet 3</ion-button>
</ion-content>
</ion-app>
<script>
function presentActionSheet1() {
var mode = Ionic.mode;
var actionSheetController = document.querySelector('ion-action-sheet-controller');
actionSheetController.create({
title: "Albums",
buttons: [{
text: 'Delete',
role: 'destructive',
icon: 'trash',
handler: () => {
console.log('Delete clicked');
}
}, {
text: 'Share',
icon: 'share',
handler: () => {
console.log('Share clicked');
}
}, {
text: 'Play (open modal)',
icon: 'arrow-dropright-circle',
handler: () => {
console.log('Play clicked');
}
}, {
text: 'Favorite',
icon: mode !== 'ios' ? 'heart' : null,
handler: () => {
console.log('Favorite clicked');
}
}, {
text: 'Cancel',
role: 'cancel',
icon: mode !== 'ios' ? 'close' : null,
handler: () => {
console.log('Cancel clicked');
}
}]
})
.then(actionSheet => {
actionSheet.present()
});
}
function presentActionSheet2() {
var actionSheetController = document.querySelector('ion-action-sheet-controller');
actionSheetController.create({
buttons: [{
text: 'Archive',
handler: () => {
console.log('Archive clicked');
}
}, {
text: 'Destructive',
role: 'destructive',
handler: () => {
console.log('Destructive clicked');
}
}, {
text: 'Cancel',
role: 'cancel',
handler: () => {
console.log('Cancel clicked');
}
}]
})
.then(actionSheet => {
actionSheet.present()
});
}
function presentActionSheet3() {
var actionSheetController = document.querySelector('ion-action-sheet-controller');
actionSheetController.create({
buttons: [{
text: 'Open Alert',
handler: () => {
console.log('Open Alert clicked');
}
}, {
text: 'Cancel',
role: 'cancel',
handler: () => {
console.log('Cancel clicked');
}
}]
})
.then(actionSheet => {
actionSheet.present()
});
}
</script>
</body>
</html>

View File

@ -4,7 +4,7 @@ import { Animator } from './animator';
@Component({
tag: 'ion-animation'
tag: 'ion-animation-controller'
})
export class AnimationController {

View File

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

View File

@ -0,0 +1,9 @@
import { Component } from '@stencil/core';
@Component({
tag: 'ion-backdrop',
styleUrl: 'backdrop.scss'
})
export class Backdrop {
}

View File

@ -2,7 +2,7 @@ import { Component } from '@stencil/core';
@Component({
tag: 'ion-gesture-ctrl'
tag: 'ion-gesture-controller'
})
export class GestureController {
private id: number = 0;

View File

@ -1,9 +1,9 @@
import { Component, Listen, Method } from '@stencil/core';
import { LoadingEvent, LoadingOptions, Loading } from '../../index';
import { Loading, LoadingEvent, LoadingOptions } from '../../index';
@Component({
tag: 'ion-loading-ctrl'
tag: 'ion-loading-controller'
})
export class LoadingController {
private ids = 0;

View File

@ -32,7 +32,7 @@ export class Loading {
@State() private showSpinner: boolean = null;
@State() private spinner: string;
@Prop({ connect: 'ion-animation' }) animationCtrl: AnimationController;
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
@Prop({ context: 'config' }) config: Config;
@Prop() cssClass: string;
@Prop() content: string;

View File

@ -3,7 +3,7 @@ import { Modal, ModalEvent, ModalOptions } from '../../index';
@Component({
tag: 'ion-modal-ctrl'
tag: 'ion-modal-controller'
})
export class ModalController {
private ids = 0;

View File

@ -27,7 +27,7 @@ export class Modal {
@Event() ionModalDidDismiss: EventEmitter;
@Event() ionModalDidUnload: EventEmitter;
@Prop({ connect: 'ion-animation' }) animationCtrl: AnimationController;
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
@Prop() mode: string;
@Prop() color: string;
@Prop() component: string;

View File

@ -0,0 +1,72 @@
import { Component, Listen, Method } from '@stencil/core';
import { PopoverEvent, PopoverOptions, Popover } from '../../index';
@Component({
tag: 'ion-popover-controller'
})
export class PopoverController {
private ids = 0;
private popoverResolves: {[popoverId: string]: Function} = {};
private popovers: Popover[] = [];
@Method()
create(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
const appRoot = document.querySelector('ion-app') || document.body;
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')
protected viewDidLoad(ev: PopoverEvent) {
const popover = ev.detail.popover;
const popoverResolve = this.popoverResolves[popover.id];
if (popoverResolve) {
popoverResolve(popover);
delete this.popoverResolves[popover.id];
}
}
@Listen('body:ionPopoverWillPresent')
protected willPresent(ev: PopoverEvent) {
this.popovers.push(ev.detail.popover);
}
@Listen('body:ionPopoverWillDismiss, body:ionPopoverDidUnload')
protected willDismiss(ev: PopoverEvent) {
const index = this.popovers.indexOf(ev.detail.popover);
if (index > -1) {
this.popovers.splice(index, 1);
}
}
@Listen('body:keyup.escape')
protected escapeKeyUp() {
const lastPopover = this.popovers[this.popovers.length - 1];
if (lastPopover) {
lastPopover.dismiss();
}
}
}

View File

@ -0,0 +1,20 @@
import { Animation } from '../../../index';
/**
* iOS Popover Enter Animation
*/
export default function(Animation: Animation, baseElm: HTMLElement) {
const baseAnimation = new Animation();
const backdropAnimation = new Animation();
backdropAnimation.addElement(baseElm.querySelector('.popover-backdrop'));
backdropAnimation.fromTo('opacity', 0.01, 0.08);
return baseAnimation
.addElement(baseElm)
.easing('ease')
.duration(100)
.add(backdropAnimation);
}

View File

@ -0,0 +1,24 @@
import { Animation } from '../../../index';
/**
* iOS Popover Leave Animation
*/
export default function(Animation: Animation, baseElm: HTMLElement) {
const baseAnimation = new Animation();
const backdropAnimation = new Animation();
backdropAnimation.addElement(baseElm.querySelector('.popover-backdrop'));
const wrapperAnimation = new Animation();
wrapperAnimation.addElement(baseElm.querySelector('.popover-wrapper'));
wrapperAnimation.fromTo('opacity', 0.99, 0);
backdropAnimation.fromTo('opacity', 0.08, 0);
return baseAnimation
.addElement(baseElm)
.easing('ease')
.duration(500)
.add(backdropAnimation)
.add(wrapperAnimation);
}

View File

@ -0,0 +1,81 @@
@import "../../themes/ionic.globals.ios";
@import "./popover";
// iOS Popover
// --------------------------------------------------
/// @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;
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;
}

View File

@ -0,0 +1,52 @@
@import "../../themes/ionic.globals.md";
@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;
/// @prop - Background of the popover content
$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;
/// @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;
box-shadow: $popover-md-box-shadow;
}
.popover-md .popover-viewport {
// opacity: 0;
transition-delay: 100ms;
}

View File

@ -0,0 +1,74 @@
@import "../../themes/ionic.globals";
// Popover
// --------------------------------------------------
/// @prop - Color of the backdrop
$popover-backdrop-color: #000 !default;
ion-popover {
@include position(0, 0, 0, 0);
position: absolute;
z-index: $z-index-overlay;
display: flex;
align-items: center;
justify-content: center;
}
.popover-wrapper {
z-index: $z-index-overlay-wrapper;
opacity: 0;
}
.popover-content {
position: absolute;
z-index: $z-index-overlay-wrapper;
display: flex;
overflow: auto;
flex-direction: column;
}
.popover-content ion-content,
.popover-content ion-scroll {
contain: none;
}
.popover-content ion-scroll {
position: relative;
}
ion-popover-controller {
display: none;
}
// Popover Backdrop
// --------------------------------------------------
.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,332 @@
import { Component, Element, Event, EventEmitter, Listen, Prop, State } from '@stencil/core';
import { AnimationBuilder, Animation, AnimationController, Config } 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 {
private animation: Animation;
@State() positioned: boolean = false;
@Element() private el: HTMLElement;
@Event() private ionPopoverDidLoad: EventEmitter;
@Event() private ionPopoverDidPresent: EventEmitter;
@Event() private ionPopoverWillPresent: EventEmitter;
@Event() private ionPopoverWillDismiss: EventEmitter;
@Event() private ionPopoverDidDismiss: EventEmitter;
@Event() private ionPopoverDidUnload: EventEmitter;
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
@Prop({ context: 'config' }) config: Config;
@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;
present() {
return new Promise<void>(resolve => {
this._present(resolve);
});
}
private positionPopover() {
const props = POPOVER_POSITION_PROPERTIES[this.mode];
console.debug('Position popover', this.el, this.ev, props);
// Declare the popover elements
let contentEl = this.el.querySelector('.popover-content') as HTMLElement;
let arrowEl = this.el.querySelector('.popover-arrow') as HTMLElement;
// If no event was passed, hide the arrow
if (!this.ev) {
arrowEl.style.display = 'none';
}
// Set the default transform origin direction
let origin = {
y: 'top',
x: 'left'
}
// Popover content width and height
const popover = {
width: contentEl.getBoundingClientRect().width,
height: contentEl.getBoundingClientRect().height
}
// Window body width and height
// TODO need to check if portrait/landscape?
const body = {
width: window.screen.width,
height: window.screen.height
}
// If ev was passed, use that for target element
let targetDim = this.ev && this.ev.target && (this.ev.target as HTMLElement).getBoundingClientRect();
// The target is the object that dispatched the event that was passed
let target = {
top: (targetDim && 'top' in targetDim) ? targetDim.top : (body.height / 2) - (popover.height / 2),
left: (targetDim && 'left' in targetDim) ? targetDim.left : (body.width / 2) - (popover.width / 2),
width: targetDim && targetDim.width || 0,
height: targetDim && targetDim.height || 0
};
// If the popover should be centered to the target
if (props.centerTarget) {
target.left = (targetDim && 'left' in targetDim) ? targetDim.left : (body.width / 2);
}
// The arrow that shows above the popover on iOS
let arrowDim = arrowEl.getBoundingClientRect();
const arrow = {
width: arrowDim.width,
height: arrowDim.height
}
let arrowCSS = {
top: target.top + target.height,
left: target.left + (target.width / 2) - (arrow.width / 2)
};
let popoverCSS = {
top: target.top + target.height + (arrow.height - 1),
left: target.left
};
// If the popover should be centered to the target
if (props.centerTarget) {
popoverCSS.left = target.left + (target.width / 2) - (popover.width / 2)
}
// If the popover left is less than the padding it is off screen
// to the left so adjust it, else if the width of the popover
// exceeds the body width it is off screen to the right so adjust
if (popoverCSS.left < props.padding) {
popoverCSS.left = props.padding;
} else if (popover.width + props.padding + popoverCSS.left > body.width) {
popoverCSS.left = body.width - popover.width - props.padding;
origin.x = 'right';
}
// If the popover when popped down stretches past bottom of screen,
// make it pop up if there's room above
if (this.showFromBottom(target, popover, body)) {
this.el.className = this.el.className + ' popover-bottom';
origin.y = 'bottom';
popoverCSS.top = target.top - popover.height;
if (props.showArrow) {
arrowCSS.top = target.top - (arrow.height + 1);
popoverCSS.top = target.top - popover.height - (arrow.height - 1);
}
// If the popover exceeds the viewport then cut the bottom off
} else if (this.exceedsViewport(target, popover, body)) {
contentEl.style.bottom = props.padding + props.unit;
}
arrowEl.style.top = arrowCSS.top + 'px';
arrowEl.style.left = arrowCSS.left + 'px';
contentEl.style.top = popoverCSS.top + 'px';
contentEl.style.left = popoverCSS.left + 'px';
contentEl.style.transformOrigin = origin.y + ' ' + origin.x;
// Since the transition starts before styling is done we
// want to wait for the styles to apply before showing the wrapper
this.positioned = true;
}
private showFromBottom(target: any, popover: any, body: any): boolean {
return target.top + target.height + popover.height > body.height && target.top - popover.height > 0;
}
private exceedsViewport(target: any, popover: any, body: any): boolean {
return target.top + target.height + popover.height > body.height;
}
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;
if (!animationBuilder) {
// user did not provide a custom animation fn
// decide from the config which animation to use
animationBuilder = iOSEnterAnimation;
}
// build the animation and kick it off
this.animationCtrl.create(animationBuilder, this.el).then(animation => {
this.animation = animation;
animation.onFinish((a: any) => {
a.destroy();
this.ionViewDidEnter();
this.positionPopover();
resolve();
}).play();
});
}
dismiss() {
if (this.animation) {
this.animation.destroy();
this.animation = null;
}
return new Promise(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
animationBuilder = iOSLeaveAnimation;
}
// build the animation and kick it off
this.animationCtrl.create(animationBuilder, this.el).then(animation => {
this.animation = animation;
animation.onFinish((a: any) => {
a.destroy();
this.ionPopoverDidDismiss.emit({ popover: this });
Context.dom.write(() => {
this.el.parentNode.removeChild(this.el);
});
resolve();
}).play();
});
});
}
protected ionViewDidUnload() {
this.ionPopoverDidUnload.emit({ popover: this });
}
@Listen('ionDismiss')
protected onDismiss(ev: UIEvent) {
ev.stopPropagation();
ev.preventDefault();
this.dismiss();
}
protected ionViewDidLoad() {
this.ionPopoverDidLoad.emit({ popover: this });
}
protected ionViewDidEnter() {
this.ionPopoverDidPresent.emit({ popover: this });
}
protected backdropClick() {
if (this.enableBackdropDismiss) {
// const opts: NavOptions = {
// minClickBlockDuration: 400
// };
this.dismiss();
}
}
render() {
const ThisComponent = this.component;
const wrapperClasses = createThemedClasses(this.mode, this.color, 'popover-wrapper');
const wrapperStyle = this.positioned ? { 'opacity' : '1' } : {};
return [
<ion-backdrop
onClick={this.backdropClick.bind(this)}
class="popover-backdrop"
/>,
<div class={wrapperClasses} style={wrapperStyle}>
<div class="popover-arrow"/>
<div class="popover-content">
<div class="popover-viewport">
<ThisComponent
props={this.componentProps}
class={this.cssClass}
/>
</div>
</div>
</div>
];
}
}
export interface PopoverOptions {
component: string;
componentProps?: any;
showBackdrop?: boolean;
enableBackdropDismiss?: boolean;
enterAnimation?: AnimationBuilder;
exitAnimation?: AnimationBuilder;
cssClass?: string;
ev: Event;
}
export interface PopoverEvent {
detail: {
popover: Popover;
};
}
export const POPOVER_POSITION_PROPERTIES: any = {
ios: {
padding: 2,
unit: '%',
showArrow: true,
centerTarget: true
},
md: {
padding: 12,
unit: 'px',
showArrow: false,
centerTarget: false
},
wp: {
padding: 12,
unit: 'px',
showArrow: false,
centerTarget: false
}
}

View File

@ -0,0 +1,49 @@
@import "../../themes/ionic.globals.wp";
@import "./popover";
// Windows Popover
// --------------------------------------------------
/// @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;
.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;
}
.popover-wp .popover-viewport {
// opacity: 0;
transition-delay: 100ms;
}

View File

@ -0,0 +1,102 @@
<!DOCTYPE html>
<html dir="ltr">
<head>
<meta charset="UTF-8">
<title>Ionic Popover</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<script src="/dist/ionic.js"></script>
</head>
<body>
<ion-app>
<ion-header>
<ion-toolbar color="primary">
<ion-buttons slot="start">
<ion-button onclick="presentPopover('profile-page', event)">
<ion-icon slot="icon-only" name="person"></ion-icon>
</ion-button>
</ion-buttons>
<ion-title>Popover</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding>
<ion-button block onclick="presentPopover('profile-page', event)">Show Popover</ion-button>
<ion-button block onclick="presentPopover('list-page', event)">Show Long List Popover</ion-button>
<ion-button block onclick="presentPopover('profile-page')">No Event Popover</ion-button>
<ion-popover-controller></ion-popover-controller>
</ion-content>
<ion-footer>
<ion-toolbar color="primary">
<ion-buttons slot="end">
<ion-button onclick="presentPopover('profile-page', event)">
<ion-icon slot="icon-only" name="person"></ion-icon>
</ion-button>
</ion-buttons>
<ion-title>Popover</ion-title>
</ion-toolbar>
</ion-footer>
</ion-app>
<script>
function presentPopover(componentName, event) {
var popoverController = document.querySelector('ion-popover-controller');
popoverController.create({
component: componentName,
ev: event
}).then(popover => {
popover.present();
});
}
class ProfilePage extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
this.innerHTML = `
<ion-content>
<ion-list>
<ion-list-header>Ionic</ion-list-header>
<ion-item><ion-label>Item 0</ion-label></ion-item>
<ion-item><ion-label>Item 1</ion-label></ion-item>
<ion-item><ion-label>Item 2</ion-label></ion-item>
<ion-item><ion-label>Item 3</ion-label></ion-item>
</ion-list>
</ion-content>
`;
}
}
customElements.define('profile-page', ProfilePage);
class ListPage extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
let items = '';
for (var i = 0; i < 30; i++) {
items += '<ion-item><ion-label>Item ' + i + '</ion-label></ion-item>';
}
this.innerHTML = `
<ion-content>
<ion-list>
<ion-list-header>Ionic</ion-list-header>
` + items + `
</ion-list>
</ion-content>
`;
}
}
customElements.define('list-page', ListPage);
</script>
</body>
</html>

View File

@ -1,5 +1,8 @@
import { AnimationController } from './components/animation/animation';
import { Animation, AnimationBuilder } from './components/animation/animation-interface';
import { AnimationController } from './components/animation-controller/animation-controller';
import { Animation, AnimationBuilder } from './components/animation-controller/animation-interface';
import { ActionSheet, ActionSheetButton, ActionSheetEvent, ActionSheetOptions } from './components/action-sheet/action-sheet';
import { ActionSheetController } from './components/action-sheet-controller/action-sheet-controller';
import { Backdrop } from './components/backdrop/backdrop'
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 +11,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,9 +48,15 @@ export interface BooleanInputComponent extends BaseInputComponent {
export {
ActionSheet,
ActionSheetButton,
ActionSheetEvent,
ActionSheetOptions,
ActionSheetController,
Animation,
AnimationBuilder,
AnimationController,
Backdrop,
GestureCallback,
GestureDetail,
Loading,
@ -57,6 +70,10 @@ export {
ModalController,
ModalOptions,
ModalEvent,
Popover,
PopoverController,
PopoverEvent,
PopoverOptions,
Scroll,
ScrollCallback,
ScrollDetail,

View File

@ -12,7 +12,7 @@
For example:
$colors: (
primary: #327eff,
primary: #488aff,
secondary: (base: #32db64, contrast: #000),
);";
@ -28,7 +28,7 @@
For example:
$colors: (
#{$color-name}: #327eff
#{$color-name}: #488aff
);";
}
@ -253,7 +253,7 @@
//
// Example values
// --------------------------------------------------
// primary | #327eff | #444
// primary | #488aff | #444
// map key | map value | hex color not in map
// --------------------------------------------------
//
@ -278,7 +278,7 @@
}
// If the value is a map then get the contrast
// from the map (e.g. (base: #327eff, contrast: blue))
// from the map (e.g. (base: #488aff, contrast: blue))
@if (type-of($color-value) == map) {
// If the map has the contrast key then use that
// otherwise it is a map with just a base so get

View File

@ -6,7 +6,7 @@
$colors: (
primary: #327eff,
primary: #488aff,
secondary: #32db64,
danger: #f53d3d,
light: #f4f4f4,

View File

@ -4,9 +4,11 @@ exports.config = {
publicPath: '/dist',
generateCollection: true,
bundles: [
{ components: ['ion-animation'] },
{ components: ['ion-animation-controller'] },
{ components: ['ion-app', 'ion-content', 'ion-fixed', 'ion-footer', 'ion-header', 'ion-navbar', 'ion-page', 'ion-title', 'ion-toolbar'] },
{ components: ['ion-action-sheet', 'ion-action-sheet-controller'] },
{ components: ['ion-avatar', 'ion-badge', 'ion-thumbnail'] },
{ components: ['ion-backdrop'] },
{ components: ['ion-button', 'ion-buttons', 'ion-icon'] },
{ components: ['ion-card', 'ion-card-content', 'ion-card-header', 'ion-card-title'] },
{ components: ['ion-chip', 'ion-chip-button'] },
@ -15,9 +17,10 @@ exports.config = {
{ components: ['ion-grid', 'ion-row', 'ion-col'] },
{ components: ['ion-item', 'ion-item-divider', 'ion-item-sliding', 'ion-item-options', 'ion-item-option', 'ion-label', 'ion-list', 'ion-list-header', 'ion-skeleton-text'] },
{ components: ['ion-input', 'ion-textarea'] },
{ components: ['ion-loading', 'ion-loading-ctrl'] },
{ components: ['ion-loading', 'ion-loading-controller'] },
{ components: ['ion-menu'], priority: 'low' },
{ components: ['ion-modal', 'ion-modal-ctrl'] },
{ components: ['ion-modal', 'ion-modal-controller'] },
{ components: ['ion-popover', 'ion-popover-controller'] },
{ components: ['ion-searchbar'] },
{ components: ['ion-segment', 'ion-segment-button'] },
{ components: ['ion-slides', 'ion-slide'] },

View File

@ -12,7 +12,7 @@
For example:
$colors: (
primary: #327eff,
primary: #488aff,
secondary: (base: #32db64, contrast: #000),
);";
@ -28,7 +28,7 @@
For example:
$colors: (
#{$color-name}: #327eff
#{$color-name}: #488aff
);";
}
@ -253,7 +253,7 @@
//
// Example values
// --------------------------------------------------
// primary | #327eff | #444
// primary | #488aff | #444
// map key | map value | hex color not in map
// --------------------------------------------------
//
@ -278,7 +278,7 @@
}
// If the value is a map then get the contrast
// from the map (e.g. (base: #327eff, contrast: blue))
// from the map (e.g. (base: #488aff, contrast: blue))
@if (type-of($color-value) == map) {
// If the map has the contrast key then use that
// otherwise it is a map with just a base so get

View File

@ -6,7 +6,7 @@
$colors: (
primary: #327eff,
primary: #488aff,
secondary: #32db64,
danger: #f53d3d,
light: #f4f4f4,