@ionic/core
Version:
Base components for Ionic
563 lines (562 loc) • 18.1 kB
JavaScript
import { Component, Element, Event, Host, Method, Prop, h, readTask } from '@stencil/core';
import { getIonMode } from '../../global/ionic-global';
import { createButtonActiveGesture } from '../../utils/gesture/button-active';
import { BACKDROP, dismiss, eventMethod, isCancel, prepareOverlay, present, safeCall } from '../../utils/overlays';
import { getClassMap } from '../../utils/theme';
import { iosEnterAnimation } from './animations/ios.enter';
import { iosLeaveAnimation } from './animations/ios.leave';
import { mdEnterAnimation } from './animations/md.enter';
import { mdLeaveAnimation } from './animations/md.leave';
/**
* @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use.
*/
export class ActionSheet {
constructor() {
this.presented = false;
/**
* If `true`, the keyboard will be automatically dismissed when the overlay is presented.
*/
this.keyboardClose = true;
/**
* An array of buttons for the action sheet.
*/
this.buttons = [];
/**
* If `true`, the action sheet will be dismissed when the backdrop is clicked.
*/
this.backdropDismiss = true;
/**
* If `true`, the action sheet will be translucent.
* Only applies when the mode is `"ios"` and the device supports
* [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility).
*/
this.translucent = false;
/**
* If `true`, the action sheet will animate.
*/
this.animated = true;
this.onBackdropTap = () => {
this.dismiss(undefined, BACKDROP);
};
this.dispatchCancelHandler = (ev) => {
const role = ev.detail.role;
if (isCancel(role)) {
const cancelButton = this.getButtons().find(b => b.role === 'cancel');
this.callButtonHandler(cancelButton);
}
};
}
/**
* Present the action sheet overlay after it has been created.
*/
present() {
return present(this, 'actionSheetEnter', iosEnterAnimation, mdEnterAnimation);
}
connectedCallback() {
prepareOverlay(this.el);
}
/**
* Dismiss the action sheet overlay after it has been presented.
*
* @param data Any data to emit in the dismiss events.
* @param role The role of the element that is dismissing the action sheet.
* This can be useful in a button handler for determining which button was
* clicked to dismiss the action sheet.
* Some examples include: ``"cancel"`, `"destructive"`, "selected"`, and `"backdrop"`.
*/
dismiss(data, role) {
return dismiss(this, data, role, 'actionSheetLeave', iosLeaveAnimation, mdLeaveAnimation);
}
/**
* Returns a promise that resolves when the action sheet did dismiss.
*/
onDidDismiss() {
return eventMethod(this.el, 'ionActionSheetDidDismiss');
}
/**
* Returns a promise that resolves when the action sheet will dismiss.
*
*/
onWillDismiss() {
return eventMethod(this.el, 'ionActionSheetWillDismiss');
}
async buttonClick(button) {
const role = button.role;
if (isCancel(role)) {
return this.dismiss(undefined, role);
}
const shouldDismiss = await this.callButtonHandler(button);
if (shouldDismiss) {
return this.dismiss(undefined, button.role);
}
return Promise.resolve();
}
async callButtonHandler(button) {
if (button) {
// a handler has been provided, execute it
// pass the handler the values from the inputs
const rtn = await safeCall(button.handler);
if (rtn === false) {
// if the return value of the handler is false then do not dismiss
return false;
}
}
return true;
}
getButtons() {
return this.buttons.map(b => {
return (typeof b === 'string')
? { text: b }
: b;
});
}
disconnectedCallback() {
if (this.gesture) {
this.gesture.destroy();
this.gesture = undefined;
}
}
componentDidLoad() {
/**
* Do not create gesture if:
* 1. A gesture already exists
* 2. App is running in MD mode
* 3. A wrapper ref does not exist
*/
const { groupEl, wrapperEl } = this;
if (this.gesture || getIonMode(this) === 'md' || !wrapperEl || !groupEl) {
return;
}
readTask(() => {
const isScrollable = groupEl.scrollHeight > groupEl.clientHeight;
if (!isScrollable) {
this.gesture = createButtonActiveGesture(wrapperEl, (refEl) => refEl.classList.contains('action-sheet-button'));
this.gesture.enable(true);
}
});
}
render() {
const mode = getIonMode(this);
const allButtons = this.getButtons();
const cancelButton = allButtons.find(b => b.role === 'cancel');
const buttons = allButtons.filter(b => b.role !== 'cancel');
return (h(Host, { role: "dialog", "aria-modal": "true", tabindex: "-1", style: {
zIndex: `${20000 + this.overlayIndex}`,
}, class: Object.assign(Object.assign({ [mode]: true }, getClassMap(this.cssClass)), { 'action-sheet-translucent': this.translucent }), onIonActionSheetWillDismiss: this.dispatchCancelHandler, onIonBackdropTap: this.onBackdropTap },
h("ion-backdrop", { tappable: this.backdropDismiss }),
h("div", { tabindex: "0" }),
h("div", { class: "action-sheet-wrapper ion-overlay-wrapper", role: "dialog", ref: el => this.wrapperEl = el },
h("div", { class: "action-sheet-container" },
h("div", { class: "action-sheet-group", ref: el => this.groupEl = el },
this.header !== undefined &&
h("div", { class: {
'action-sheet-title': true,
'action-sheet-has-sub-title': this.subHeader !== undefined
} },
this.header,
this.subHeader && h("div", { class: "action-sheet-sub-title" }, this.subHeader)),
buttons.map(b => h("button", { type: "button", class: buttonClass(b), onClick: () => this.buttonClick(b) },
h("span", { class: "action-sheet-button-inner" },
b.icon && h("ion-icon", { icon: b.icon, lazy: false, class: "action-sheet-icon" }),
b.text),
mode === 'md' && h("ion-ripple-effect", null)))),
cancelButton &&
h("div", { class: "action-sheet-group action-sheet-group-cancel" },
h("button", { type: "button", class: buttonClass(cancelButton), onClick: () => this.buttonClick(cancelButton) },
h("span", { class: "action-sheet-button-inner" },
cancelButton.icon &&
h("ion-icon", { icon: cancelButton.icon, lazy: false, class: "action-sheet-icon" }),
cancelButton.text),
mode === 'md' && h("ion-ripple-effect", null))))),
h("div", { tabindex: "0" })));
}
static get is() { return "ion-action-sheet"; }
static get encapsulation() { return "scoped"; }
static get originalStyleUrls() { return {
"ios": ["action-sheet.ios.scss"],
"md": ["action-sheet.md.scss"]
}; }
static get styleUrls() { return {
"ios": ["action-sheet.ios.css"],
"md": ["action-sheet.md.css"]
}; }
static get properties() { return {
"overlayIndex": {
"type": "number",
"mutable": false,
"complexType": {
"original": "number",
"resolved": "number",
"references": {}
},
"required": true,
"optional": false,
"docs": {
"tags": [{
"text": undefined,
"name": "internal"
}],
"text": ""
},
"attribute": "overlay-index",
"reflect": false
},
"keyboardClose": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "If `true`, the keyboard will be automatically dismissed when the overlay is presented."
},
"attribute": "keyboard-close",
"reflect": false,
"defaultValue": "true"
},
"enterAnimation": {
"type": "unknown",
"mutable": false,
"complexType": {
"original": "AnimationBuilder",
"resolved": "((baseEl: any, opts?: any) => Animation) | undefined",
"references": {
"AnimationBuilder": {
"location": "import",
"path": "../../interface"
}
}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Animation to use when the action sheet is presented."
}
},
"leaveAnimation": {
"type": "unknown",
"mutable": false,
"complexType": {
"original": "AnimationBuilder",
"resolved": "((baseEl: any, opts?: any) => Animation) | undefined",
"references": {
"AnimationBuilder": {
"location": "import",
"path": "../../interface"
}
}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Animation to use when the action sheet is dismissed."
}
},
"buttons": {
"type": "unknown",
"mutable": false,
"complexType": {
"original": "(ActionSheetButton | string)[]",
"resolved": "(string | ActionSheetButton)[]",
"references": {
"ActionSheetButton": {
"location": "import",
"path": "../../interface"
}
}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "An array of buttons for the action sheet."
},
"defaultValue": "[]"
},
"cssClass": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string | string[]",
"resolved": "string | string[] | undefined",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Additional classes to apply for custom CSS. If multiple classes are\nprovided they should be separated by spaces."
},
"attribute": "css-class",
"reflect": false
},
"backdropDismiss": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "If `true`, the action sheet will be dismissed when the backdrop is clicked."
},
"attribute": "backdrop-dismiss",
"reflect": false,
"defaultValue": "true"
},
"header": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string | undefined",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Title for the action sheet."
},
"attribute": "header",
"reflect": false
},
"subHeader": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string | undefined",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Subtitle for the action sheet."
},
"attribute": "sub-header",
"reflect": false
},
"translucent": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "If `true`, the action sheet will be translucent.\nOnly applies when the mode is `\"ios\"` and the device supports\n[`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility)."
},
"attribute": "translucent",
"reflect": false,
"defaultValue": "false"
},
"animated": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "If `true`, the action sheet will animate."
},
"attribute": "animated",
"reflect": false,
"defaultValue": "true"
}
}; }
static get events() { return [{
"method": "didPresent",
"name": "ionActionSheetDidPresent",
"bubbles": true,
"cancelable": true,
"composed": true,
"docs": {
"tags": [],
"text": "Emitted after the alert has presented."
},
"complexType": {
"original": "void",
"resolved": "void",
"references": {}
}
}, {
"method": "willPresent",
"name": "ionActionSheetWillPresent",
"bubbles": true,
"cancelable": true,
"composed": true,
"docs": {
"tags": [],
"text": "Emitted before the alert has presented."
},
"complexType": {
"original": "void",
"resolved": "void",
"references": {}
}
}, {
"method": "willDismiss",
"name": "ionActionSheetWillDismiss",
"bubbles": true,
"cancelable": true,
"composed": true,
"docs": {
"tags": [],
"text": "Emitted before the alert has dismissed."
},
"complexType": {
"original": "OverlayEventDetail",
"resolved": "OverlayEventDetail<any>",
"references": {
"OverlayEventDetail": {
"location": "import",
"path": "../../interface"
}
}
}
}, {
"method": "didDismiss",
"name": "ionActionSheetDidDismiss",
"bubbles": true,
"cancelable": true,
"composed": true,
"docs": {
"tags": [],
"text": "Emitted after the alert has dismissed."
},
"complexType": {
"original": "OverlayEventDetail",
"resolved": "OverlayEventDetail<any>",
"references": {
"OverlayEventDetail": {
"location": "import",
"path": "../../interface"
}
}
}
}]; }
static get methods() { return {
"present": {
"complexType": {
"signature": "() => Promise<void>",
"parameters": [],
"references": {
"Promise": {
"location": "global"
}
},
"return": "Promise<void>"
},
"docs": {
"text": "Present the action sheet overlay after it has been created.",
"tags": []
}
},
"dismiss": {
"complexType": {
"signature": "(data?: any, role?: string | undefined) => Promise<boolean>",
"parameters": [{
"tags": [{
"text": "data Any data to emit in the dismiss events.",
"name": "param"
}],
"text": "Any data to emit in the dismiss events."
}, {
"tags": [{
"text": "role The role of the element that is dismissing the action sheet.\nThis can be useful in a button handler for determining which button was\nclicked to dismiss the action sheet.\nSome examples include: ``\"cancel\"`, `\"destructive\"`, \"selected\"`, and `\"backdrop\"`.",
"name": "param"
}],
"text": "The role of the element that is dismissing the action sheet.\nThis can be useful in a button handler for determining which button was\nclicked to dismiss the action sheet.\nSome examples include: ``\"cancel\"`, `\"destructive\"`, \"selected\"`, and `\"backdrop\"`."
}],
"references": {
"Promise": {
"location": "global"
}
},
"return": "Promise<boolean>"
},
"docs": {
"text": "Dismiss the action sheet overlay after it has been presented.",
"tags": [{
"name": "param",
"text": "data Any data to emit in the dismiss events."
}, {
"name": "param",
"text": "role The role of the element that is dismissing the action sheet.\nThis can be useful in a button handler for determining which button was\nclicked to dismiss the action sheet.\nSome examples include: ``\"cancel\"`, `\"destructive\"`, \"selected\"`, and `\"backdrop\"`."
}]
}
},
"onDidDismiss": {
"complexType": {
"signature": "<T = any>() => Promise<OverlayEventDetail<T>>",
"parameters": [],
"references": {
"Promise": {
"location": "global"
},
"OverlayEventDetail": {
"location": "import",
"path": "../../interface"
},
"T": {
"location": "global"
}
},
"return": "Promise<OverlayEventDetail<T>>"
},
"docs": {
"text": "Returns a promise that resolves when the action sheet did dismiss.",
"tags": []
}
},
"onWillDismiss": {
"complexType": {
"signature": "<T = any>() => Promise<OverlayEventDetail<T>>",
"parameters": [],
"references": {
"Promise": {
"location": "global"
},
"OverlayEventDetail": {
"location": "import",
"path": "../../interface"
},
"T": {
"location": "global"
}
},
"return": "Promise<OverlayEventDetail<T>>"
},
"docs": {
"text": "Returns a promise that resolves when the action sheet will dismiss.",
"tags": []
}
}
}; }
static get elementRef() { return "el"; }
}
const buttonClass = (button) => {
return Object.assign({ 'action-sheet-button': true, 'ion-activatable': true, 'ion-focusable': true, [`action-sheet-${button.role}`]: button.role !== undefined }, getClassMap(button.cssClass));
};