UNPKG

@ionic/core

Version:

Base components for Ionic

563 lines (562 loc) • 18.1 kB
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)); };