mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-10 00:27:41 +08:00
merge release-4.6.1
This commit is contained in:
16
core/src/components.d.ts
vendored
16
core/src/components.d.ts
vendored
@ -2016,6 +2016,10 @@ export namespace Components {
|
||||
* When using a router, it specifies the transition direction when navigating to another page using `href`.
|
||||
*/
|
||||
'routerDirection': RouterDirection;
|
||||
/**
|
||||
* Specifies where to display the linked URL. Only applies when an `href` is provided. Special keywords: `"_blank"`, `"_self"`, `"_parent"`, `"_top"`.
|
||||
*/
|
||||
'target': string | undefined;
|
||||
}
|
||||
interface IonRouterOutlet {
|
||||
/**
|
||||
@ -2029,6 +2033,10 @@ export namespace Components {
|
||||
'commit': (enteringEl: HTMLElement, leavingEl: HTMLElement | undefined, opts?: RouterOutletOptions | undefined) => Promise<boolean>;
|
||||
'delegate'?: FrameworkDelegate;
|
||||
'getRouteId': () => Promise<RouteID | undefined>;
|
||||
/**
|
||||
* The mode determines which platform styles to use.
|
||||
*/
|
||||
'mode': "ios" | "md";
|
||||
'setRouteId': (id: string, params: ComponentProps<null> | undefined, direction: RouterDirection) => Promise<RouteWrite>;
|
||||
'swipeHandler'?: SwipeGestureHandler;
|
||||
}
|
||||
@ -5244,6 +5252,10 @@ declare namespace LocalJSX {
|
||||
* When using a router, it specifies the transition direction when navigating to another page using `href`.
|
||||
*/
|
||||
'routerDirection'?: RouterDirection;
|
||||
/**
|
||||
* Specifies where to display the linked URL. Only applies when an `href` is provided. Special keywords: `"_blank"`, `"_self"`, `"_parent"`, `"_top"`.
|
||||
*/
|
||||
'target'?: string | undefined;
|
||||
}
|
||||
interface IonRouterOutlet extends JSXBase.HTMLAttributes<HTMLIonRouterOutletElement> {
|
||||
/**
|
||||
@ -5254,6 +5266,10 @@ declare namespace LocalJSX {
|
||||
* By default `ion-nav` animates transition between pages based in the mode (ios or material design). However, this property allows to create custom transition using `AnimateBuilder` functions.
|
||||
*/
|
||||
'animation'?: AnimationBuilder;
|
||||
/**
|
||||
* The mode determines which platform styles to use.
|
||||
*/
|
||||
'mode'?: "ios" | "md";
|
||||
}
|
||||
interface IonRow extends JSXBase.HTMLAttributes<HTMLIonRowElement> {}
|
||||
interface IonSearchbar extends JSXBase.HTMLAttributes<HTMLIonSearchbarElement> {
|
||||
|
||||
@ -2,7 +2,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Listen, Me
|
||||
|
||||
import { getIonMode } from '../../global/ionic-global';
|
||||
import { ActionSheetButton, Animation, AnimationBuilder, CssClassMap, OverlayEventDetail, OverlayInterface } from '../../interface';
|
||||
import { BACKDROP, dismiss, eventMethod, isCancel, present } from '../../utils/overlays';
|
||||
import { BACKDROP, dismiss, eventMethod, isCancel, present, safeCall } from '../../utils/overlays';
|
||||
import { getClassMap } from '../../utils/theme';
|
||||
|
||||
import { iosEnterAnimation } from './animations/ios.enter';
|
||||
@ -169,17 +169,13 @@ export class ActionSheet implements ComponentInterface, OverlayInterface {
|
||||
}
|
||||
|
||||
private async callButtonHandler(button: ActionSheetButton | undefined) {
|
||||
if (button && button.handler) {
|
||||
if (button) {
|
||||
// a handler has been provided, execute it
|
||||
// pass the handler the values from the inputs
|
||||
try {
|
||||
const rtn = await button.handler();
|
||||
if (rtn === false) {
|
||||
// if the return value of the handler is false then do not dismiss
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
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;
|
||||
|
||||
@ -2,7 +2,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Listen, Me
|
||||
|
||||
import { getIonMode } from '../../global/ionic-global';
|
||||
import { AlertButton, AlertInput, Animation, AnimationBuilder, CssClassMap, OverlayEventDetail, OverlayInterface } from '../../interface';
|
||||
import { BACKDROP, dismiss, eventMethod, isCancel, present } from '../../utils/overlays';
|
||||
import { BACKDROP, dismiss, eventMethod, isCancel, present, safeCall } from '../../utils/overlays';
|
||||
import { sanitizeDOMString } from '../../utils/sanitization';
|
||||
import { getClassMap } from '../../utils/theme';
|
||||
|
||||
@ -223,17 +223,13 @@ export class Alert implements ComponentInterface, OverlayInterface {
|
||||
input.checked = input === selectedInput;
|
||||
}
|
||||
this.activeId = selectedInput.id;
|
||||
if (selectedInput.handler) {
|
||||
selectedInput.handler(selectedInput);
|
||||
}
|
||||
safeCall(selectedInput.handler, selectedInput);
|
||||
this.el.forceUpdate();
|
||||
}
|
||||
|
||||
private cbClick(selectedInput: AlertInput) {
|
||||
selectedInput.checked = !selectedInput.checked;
|
||||
if (selectedInput.handler) {
|
||||
selectedInput.handler(selectedInput);
|
||||
}
|
||||
safeCall(selectedInput.handler, selectedInput);
|
||||
this.el.forceUpdate();
|
||||
}
|
||||
|
||||
@ -254,7 +250,7 @@ export class Alert implements ComponentInterface, OverlayInterface {
|
||||
if (button && button.handler) {
|
||||
// a handler has been provided, execute it
|
||||
// pass the handler the values from the inputs
|
||||
const returnData = button.handler(data);
|
||||
const returnData = safeCall(button.handler, data);
|
||||
if (returnData === false) {
|
||||
// if the return value of the handler is false then do not dismiss
|
||||
return false;
|
||||
|
||||
@ -132,7 +132,7 @@
|
||||
// --------------------------------------------------
|
||||
|
||||
:host(.fab-button-disabled) {
|
||||
--opacity: .5;
|
||||
opacity: .5;
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@ -29,7 +29,8 @@ export class InfiniteScroll implements ComponentInterface {
|
||||
@Prop() threshold = '15%';
|
||||
|
||||
@Watch('threshold')
|
||||
protected thresholdChanged(val: string) {
|
||||
protected thresholdChanged() {
|
||||
const val = this.threshold;
|
||||
if (val.lastIndexOf('%') > -1) {
|
||||
this.thrPx = 0;
|
||||
this.thrPc = (parseFloat(val) / 100);
|
||||
@ -53,10 +54,12 @@ export class InfiniteScroll implements ComponentInterface {
|
||||
|
||||
@Watch('disabled')
|
||||
protected disabledChanged() {
|
||||
if (this.disabled) {
|
||||
const disabled = this.disabled;
|
||||
if (disabled) {
|
||||
this.isLoading = false;
|
||||
this.isBusy = false;
|
||||
}
|
||||
this.enableScrollEvents(!disabled);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -79,7 +82,8 @@ export class InfiniteScroll implements ComponentInterface {
|
||||
await contentEl.componentOnReady();
|
||||
this.scrollEl = await contentEl.getScrollElement();
|
||||
}
|
||||
this.thresholdChanged(this.threshold);
|
||||
this.thresholdChanged();
|
||||
this.disabledChanged();
|
||||
if (this.position === 'top') {
|
||||
writeTask(() => {
|
||||
if (this.scrollEl) {
|
||||
@ -90,6 +94,7 @@ export class InfiniteScroll implements ComponentInterface {
|
||||
}
|
||||
|
||||
componentDidUnload() {
|
||||
this.enableScrollEvents(false);
|
||||
this.scrollEl = undefined;
|
||||
}
|
||||
|
||||
@ -199,16 +204,26 @@ export class InfiniteScroll implements ComponentInterface {
|
||||
);
|
||||
}
|
||||
|
||||
private enableScrollEvents(shouldListen: boolean) {
|
||||
if (this.scrollEl) {
|
||||
if (shouldListen) {
|
||||
this.scrollEl.addEventListener('scroll', this.onScroll);
|
||||
} else {
|
||||
this.scrollEl.removeEventListener('scroll', this.onScroll);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const mode = getIonMode(this);
|
||||
const disabled = this.disabled;
|
||||
return (
|
||||
<Host
|
||||
class={{
|
||||
[mode]: true,
|
||||
'infinite-scroll-loading': this.isLoading,
|
||||
'infinite-scroll-enabled': !this.disabled
|
||||
'infinite-scroll-enabled': !disabled
|
||||
}}
|
||||
onScroll={this.disabled ? undefined : this.onScroll}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -149,7 +149,7 @@
|
||||
// Item: Disabled
|
||||
// --------------------------------------------------
|
||||
|
||||
:host(.item-interactive-disabled) {
|
||||
:host(.item-interactive-disabled:not(.item-multiple-inputs)) {
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
}
|
||||
@ -397,7 +397,9 @@ button, a {
|
||||
// Multiple inputs in an item should have the input
|
||||
// cover relative to themselves instead of the item
|
||||
|
||||
:host(.item-multiple-inputs) ::slotted(ion-checkbox),
|
||||
:host(.item-multiple-inputs) ::slotted(ion-datetime),
|
||||
:host(.item-multiple-inputs) ::slotted(ion-radio),
|
||||
:host(.item-multiple-inputs) ::slotted(ion-select) {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@ -128,18 +128,26 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
|
||||
}
|
||||
|
||||
componentDidLoad() {
|
||||
// Check for multiple inputs to change the position to relative
|
||||
const inputs = this.el.querySelectorAll('ion-select, ion-datetime');
|
||||
this.multipleInputs = inputs.length > 1 ? true : false;
|
||||
// The following elements have a clickable cover that is relative to the entire item
|
||||
const covers = this.el.querySelectorAll('ion-checkbox, ion-datetime, ion-select, ion-radio');
|
||||
|
||||
// The following elements can accept focus alongside the previous elements
|
||||
// therefore if these elements are also a child of item, we don't want the
|
||||
// input cover on top of those interfering with their clicks
|
||||
const inputs = this.el.querySelectorAll('ion-input, ion-range, ion-searchbar, ion-segment, ion-textarea, ion-toggle');
|
||||
|
||||
// Check for multiple inputs to change the position of the input cover to relative
|
||||
// for all of the covered inputs above
|
||||
this.multipleInputs = covers.length + inputs.length > 1;
|
||||
}
|
||||
|
||||
// If the item contains an input including a radio, checkbox, datetime, etc.
|
||||
// then the item will have a clickable input cover that should
|
||||
// get the hover, focused and activated states UNLESS it has multiple
|
||||
// inputs, then those need to individually get the click
|
||||
// If the item contains an input including a checkbox, datetime, select, or radio
|
||||
// then the item will have a clickable input cover that covers the item
|
||||
// that should get the hover, focused and activated states UNLESS it has multiple
|
||||
// inputs, then those need to individually get each click
|
||||
private hasCover(): boolean {
|
||||
const inputs = this.el.querySelectorAll('ion-checkbox, ion-datetime, ion-select, ion-radio');
|
||||
return inputs.length > 0 && !this.multipleInputs;
|
||||
return inputs.length === 1 && !this.multipleInputs;
|
||||
}
|
||||
|
||||
// If the item has an href or button property it will render a native
|
||||
|
||||
19
core/src/components/item/test/disabled/e2e.ts
Normal file
19
core/src/components/item/test/disabled/e2e.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
test('item: disabled', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/item/test/disabled?ionic:_testing=true'
|
||||
});
|
||||
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
});
|
||||
|
||||
test('item: disabled-rtl', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/item/test/disabled?ionic:_testing=true&rtl=true'
|
||||
});
|
||||
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
});
|
||||
197
core/src/components/item/test/disabled/index.html
Normal file
197
core/src/components/item/test/disabled/index.html
Normal file
@ -0,0 +1,197 @@
|
||||
<!DOCTYPE html>
|
||||
<html dir="ltr">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Item - Disabled</title>
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script src="../../../../../dist/ionic.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Item: Disabled</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content class="ion-padding-vertical">
|
||||
<ion-list>
|
||||
<ion-list-header>
|
||||
<ion-label>Single Input Disabled Items</ion-label>
|
||||
</ion-list-header>
|
||||
|
||||
<ion-item disabled>
|
||||
<ion-label>Disabled Item</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item disabled button>
|
||||
<ion-label>Disabled Item Button</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item disabled href="#">
|
||||
<ion-label>Disabled Item Anchor</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Disabled Datetime</ion-label>
|
||||
<ion-datetime disabled value="2019"></ion-datetime>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Disabled Select</ion-label>
|
||||
<ion-select disabled>
|
||||
<ion-select-option value="">No Game Console</ion-select-option>
|
||||
<ion-select-option value="nes">NES</ion-select-option>
|
||||
<ion-select-option value="n64" selected>Nintendo64</ion-select-option>
|
||||
<ion-select-option value="ps">PlayStation</ion-select-option>
|
||||
<ion-select-option value="genesis">Sega Genesis</ion-select-option>
|
||||
<ion-select-option value="saturn">Sega Saturn</ion-select-option>
|
||||
<ion-select-option value="snes">SNES</ion-select-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Disabled Input</ion-label>
|
||||
<ion-input placeholder="Disabled" disabled></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Disabled Toggle</ion-label>
|
||||
<ion-toggle disabled checked slot="end"></ion-toggle>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Disabled Checkbox</ion-label>
|
||||
<ion-checkbox disabled checked slot="start"></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Disabled Range</ion-label>
|
||||
<ion-range disabled value="10"></ion-range>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
<ion-list>
|
||||
<ion-list-header>
|
||||
<ion-label>Multiple Input Disabled Items</ion-label>
|
||||
</ion-list-header>
|
||||
|
||||
<ion-item>
|
||||
<ion-checkbox disabled slot="start"></ion-checkbox>
|
||||
<ion-input placeholder="Disabled Checkbox w/ Input"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-checkbox disabled slot="end"></ion-checkbox>
|
||||
<ion-input placeholder="Disabled Checkbox w/ Input"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-radio disabled slot="start"></ion-radio>
|
||||
<ion-input placeholder="Disabled Radio w/ Input"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-radio disabled slot="end"></ion-radio>
|
||||
<ion-input placeholder="Disabled Radio w/ Input"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-checkbox disabled slot="start"></ion-checkbox>
|
||||
<ion-label>Checkbox + Radio</ion-label>
|
||||
<ion-radio slot="end"></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-checkbox disabled slot="start"></ion-checkbox>
|
||||
<ion-radio slot="start"></ion-radio>
|
||||
<ion-label>Checkbox + Radio</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Disabled Selects</ion-label>
|
||||
<ion-select placeholder="month">
|
||||
<ion-select-option value="1">January</ion-select-option>
|
||||
<ion-select-option value="2">February</ion-select-option>
|
||||
<ion-select-option value="3">March</ion-select-option>
|
||||
</ion-select>
|
||||
<ion-select disabled placeholder="year">
|
||||
<ion-select-option value="1990">1990</ion-select-option>
|
||||
<ion-select-option value="1991">1991</ion-select-option>
|
||||
<ion-select-option value="1992">1992</ion-select-option>
|
||||
<ion-select-option value="1993">1993</ion-select-option>
|
||||
<ion-select-option value="1994">1994</ion-select-option>
|
||||
<ion-select-option value="1995">1995</ion-select-option>
|
||||
<ion-select-option value="1996">1996</ion-select-option>
|
||||
<ion-select-option value="1997">1997</ion-select-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-checkbox slot="start"></ion-checkbox>
|
||||
<ion-label>Checkbox + Range</ion-label>
|
||||
<ion-range disabled value="45"></ion-range>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-checkbox slot="start"></ion-checkbox>
|
||||
<ion-label>Checkbox + Toggle</ion-label>
|
||||
<ion-toggle disabled value="45"></ion-toggle>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-checkbox disabled slot="start"></ion-checkbox>
|
||||
<ion-label>Checkbox + Buttons</ion-label>
|
||||
<ion-button slot="end">Default</ion-button>
|
||||
<ion-button slot="end">Buttons</ion-button>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Disabled Input</ion-label>
|
||||
<ion-input slot="start" placeholder="Disabled" disabled></ion-input>
|
||||
<ion-segment>
|
||||
<ion-segment-button value="friends">
|
||||
<ion-label>Friends</ion-label>
|
||||
</ion-segment-button>
|
||||
<ion-segment-button value="enemies">
|
||||
<ion-label>Enemies</ion-label>
|
||||
</ion-segment-button>
|
||||
</ion-segment>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-checkbox slot="start" disabled></ion-checkbox>
|
||||
<ion-label>Disabled Checkbox</ion-label>
|
||||
<ion-searchbar></ion-searchbar>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
|
||||
<ion-footer>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-button onClick="toggleDisabled()">Toggle</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-footer>
|
||||
</ion-app>
|
||||
|
||||
<script>
|
||||
const disabledEls = document.querySelectorAll("[disabled]");
|
||||
|
||||
function toggleDisabled() {
|
||||
for (var i = 0; i < disabledEls.length; i++) {
|
||||
disabledEls[i].disabled = !disabledEls[i].disabled;
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</html>
|
||||
@ -34,7 +34,7 @@
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
:host-context(.item-interactive-disabled) {
|
||||
:host-context(.item-interactive-disabled:not(.item-multiple-inputs)) {
|
||||
cursor: default;
|
||||
opacity: .3;
|
||||
pointer-events: none;
|
||||
|
||||
@ -2,7 +2,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Listen, Me
|
||||
|
||||
import { getIonMode } from '../../global/ionic-global';
|
||||
import { Animation, AnimationBuilder, CssClassMap, OverlayEventDetail, OverlayInterface, PickerButton, PickerColumn } from '../../interface';
|
||||
import { dismiss, eventMethod, present } from '../../utils/overlays';
|
||||
import { dismiss, eventMethod, present, safeCall } from '../../utils/overlays';
|
||||
import { getClassMap } from '../../utils/theme';
|
||||
|
||||
import { iosEnterAnimation } from './animations/ios.enter';
|
||||
@ -175,17 +175,9 @@ export class Picker implements ComponentInterface, OverlayInterface {
|
||||
// }
|
||||
|
||||
// keep the time of the most recent button click
|
||||
let shouldDismiss = true;
|
||||
|
||||
if (button.handler) {
|
||||
// a handler has been provided, execute it
|
||||
// pass the handler the values from the inputs
|
||||
if (button.handler(this.getSelected()) === false) {
|
||||
// if the return value of the handler is false then do not dismiss
|
||||
shouldDismiss = false;
|
||||
}
|
||||
}
|
||||
|
||||
// a handler has been provided, execute it
|
||||
// pass the handler the values from the inputs
|
||||
const shouldDismiss = safeCall(button.handler, this.getSelected()) !== false;
|
||||
if (shouldDismiss) {
|
||||
return this.dismiss();
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ The router link component is used for navigating to a specified link. Similar to
|
||||
| `href` | `href` | Contains a URL or a URL fragment that the hyperlink points to. If this property is set, an anchor tag will be rendered. | `string \| undefined` | `undefined` |
|
||||
| `rel` | `rel` | Specifies the relationship of the target object to the link object. The value is a space-separated list of [link types](https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types). | `string \| undefined` | `undefined` |
|
||||
| `routerDirection` | `router-direction` | When using a router, it specifies the transition direction when navigating to another page using `href`. | `"back" \| "forward" \| "root"` | `'forward'` |
|
||||
| `target` | `target` | Specifies where to display the linked URL. Only applies when an `href` is provided. Special keywords: `"_blank"`, `"_self"`, `"_parent"`, `"_top"`. | `string \| undefined` | `undefined` |
|
||||
|
||||
|
||||
## CSS Custom Properties
|
||||
|
||||
@ -36,6 +36,13 @@ export class RouterLink implements ComponentInterface {
|
||||
*/
|
||||
@Prop() routerDirection: RouterDirection = 'forward';
|
||||
|
||||
/**
|
||||
* Specifies where to display the linked URL.
|
||||
* Only applies when an `href` is provided.
|
||||
* Special keywords: `"_blank"`, `"_self"`, `"_parent"`, `"_top"`.
|
||||
*/
|
||||
@Prop() target: string | undefined;
|
||||
|
||||
private onClick = (ev: Event) => {
|
||||
openURL(this.href, ev, this.routerDirection);
|
||||
}
|
||||
@ -44,7 +51,8 @@ export class RouterLink implements ComponentInterface {
|
||||
const mode = getIonMode(this);
|
||||
const attrs = {
|
||||
href: this.href,
|
||||
rel: this.rel
|
||||
rel: this.rel,
|
||||
target: this.target
|
||||
};
|
||||
return (
|
||||
<Host
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<ion-router-link href="https://ionicframework.com" rel="external" style="text-decoration: underline">Underline Router Link</ion-router-link>
|
||||
<ion-router-link href="https://ionicframework.com" rel="external" target="_blank" style="text-decoration: underline">External Router Link</ion-router-link>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
@ -30,10 +30,11 @@ For handling Router Guards, the older `ionViewCanEnter` and `ionViewCanLeave` ha
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Attribute | Description | Type | Default |
|
||||
| ----------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ----------- |
|
||||
| `animated` | `animated` | If `true`, the router-outlet should animate the transition of components. | `boolean` | `true` |
|
||||
| `animation` | -- | By default `ion-nav` animates transition between pages based in the mode (ios or material design). However, this property allows to create custom transition using `AnimateBuilder` functions. | `((Animation: Animation, baseEl: any, opts?: any) => Promise<Animation>) \| undefined` | `undefined` |
|
||||
| Property | Attribute | Description | Type | Default |
|
||||
| ----------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ------------------ |
|
||||
| `animated` | `animated` | If `true`, the router-outlet should animate the transition of components. | `boolean` | `true` |
|
||||
| `animation` | -- | By default `ion-nav` animates transition between pages based in the mode (ios or material design). However, this property allows to create custom transition using `AnimateBuilder` functions. | `((Animation: Animation, baseEl: any, opts?: any) => Promise<Animation>) \| undefined` | `undefined` |
|
||||
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `getIonMode(this)` |
|
||||
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
@ -21,6 +21,11 @@ export class RouterOutlet implements ComponentInterface, NavOutlet {
|
||||
|
||||
@Element() el!: HTMLElement;
|
||||
|
||||
/**
|
||||
* The mode determines which platform styles to use.
|
||||
*/
|
||||
@Prop({ mutable: true }) mode = getIonMode(this);
|
||||
|
||||
/** @internal */
|
||||
@Prop() delegate?: FrameworkDelegate;
|
||||
|
||||
@ -147,8 +152,7 @@ export class RouterOutlet implements ComponentInterface, NavOutlet {
|
||||
// emit nav will change event
|
||||
this.ionNavWillChange.emit();
|
||||
|
||||
const mode = getIonMode(this);
|
||||
const { el } = this;
|
||||
const { el, mode } = this;
|
||||
const animated = this.animated && config.getBoolean('animated', true);
|
||||
const animationBuilder = this.animation || opts.animationBuilder || config.get('navAnimation');
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ import { Component, ComponentInterface, Listen, Prop, h } from '@stencil/core';
|
||||
|
||||
import { getIonMode } from '../../global/ionic-global';
|
||||
import { SelectPopoverOption } from '../../interface';
|
||||
import { safeCall } from '../../utils/overlays';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -28,8 +29,8 @@ export class SelectPopover implements ComponentInterface {
|
||||
@Listen('ionSelect')
|
||||
onSelect(ev: any) {
|
||||
const option = this.options.find(o => o.value === ev.target.value);
|
||||
if (option && option.handler) {
|
||||
option.handler();
|
||||
if (option) {
|
||||
safeCall(option.handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -34,9 +34,11 @@ export class TabBar implements ComponentInterface {
|
||||
@Prop() selectedTab?: string;
|
||||
@Watch('selectedTab')
|
||||
selectedTabChanged() {
|
||||
this.ionTabBarChanged.emit({
|
||||
tab: this.selectedTab
|
||||
});
|
||||
if (this.selectedTab !== undefined) {
|
||||
this.ionTabBarChanged.emit({
|
||||
tab: this.selectedTab
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -2,7 +2,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Method, Pr
|
||||
|
||||
import { getIonMode } from '../../global/ionic-global';
|
||||
import { Animation, AnimationBuilder, Color, CssClassMap, OverlayEventDetail, OverlayInterface, ToastButton } from '../../interface';
|
||||
import { dismiss, eventMethod, isCancel, present } from '../../utils/overlays';
|
||||
import { dismiss, eventMethod, isCancel, present, safeCall } from '../../utils/overlays';
|
||||
import { sanitizeDOMString } from '../../utils/sanitization';
|
||||
import { createColorClasses, getClassMap } from '../../utils/theme';
|
||||
|
||||
@ -212,7 +212,7 @@ export class Toast implements ComponentInterface, OverlayInterface {
|
||||
// a handler has been provided, execute it
|
||||
// pass the handler the values from the inputs
|
||||
try {
|
||||
const rtn = await button.handler();
|
||||
const rtn = await safeCall(button.handler);
|
||||
if (rtn === false) {
|
||||
// if the return value of the handler is false then do not dismiss
|
||||
return false;
|
||||
|
||||
@ -177,6 +177,7 @@ export interface IonicConfig {
|
||||
persistConfig?: boolean;
|
||||
_forceStatusbarPadding?: boolean;
|
||||
_testing?: boolean;
|
||||
_zoneGate?: (h: () => any) => any;
|
||||
}
|
||||
|
||||
export function setupConfig(config: IonicConfig) {
|
||||
|
||||
@ -203,16 +203,6 @@ const overlayAnimation = async (
|
||||
return hasCompleted;
|
||||
};
|
||||
|
||||
export const autoFocus = (containerEl: HTMLElement): HTMLElement | undefined => {
|
||||
const focusableEls = containerEl.querySelectorAll('a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex="0"]');
|
||||
if (focusableEls.length > 0) {
|
||||
const el = focusableEls[0] as HTMLInputElement;
|
||||
el.focus();
|
||||
return el;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const eventMethod = <T>(element: HTMLElement, eventName: string): Promise<T> => {
|
||||
let resolve: (detail: T) => void;
|
||||
const promise = new Promise<T>(r => resolve = r);
|
||||
@ -244,4 +234,20 @@ const isDescendant = (parent: HTMLElement, child: HTMLElement | null) => {
|
||||
return false;
|
||||
};
|
||||
|
||||
const defaultGate = (h: any) => h();
|
||||
|
||||
export const safeCall = (handler: any, arg?: any) => {
|
||||
if (typeof handler === 'function') {
|
||||
const jmp = config.get('_zoneGate', defaultGate);
|
||||
return jmp(() => {
|
||||
try {
|
||||
return handler(arg);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const BACKDROP = 'backdrop';
|
||||
|
||||
Reference in New Issue
Block a user