mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-10 00:27:41 +08:00
chore(): sync with main
This commit is contained in:
@ -254,7 +254,7 @@ rules:
|
||||
- visibility
|
||||
- z-index
|
||||
|
||||
property-blacklist:
|
||||
property-disallowed-list:
|
||||
- background-position
|
||||
- right
|
||||
- left
|
||||
|
||||
@ -110,10 +110,16 @@
|
||||
text-align: $action-sheet-ios-text-align;
|
||||
}
|
||||
|
||||
.action-sheet-title.action-sheet-has-sub-title {
|
||||
font-weight: $action-sheet-ios-title-with-sub-title-font-weight;
|
||||
}
|
||||
|
||||
.action-sheet-sub-title {
|
||||
@include padding($action-sheet-ios-sub-title-padding-top, $action-sheet-ios-sub-title-padding-end, $action-sheet-ios-sub-title-padding-bottom, $action-sheet-ios-sub-title-padding-start);
|
||||
|
||||
font-size: $action-sheet-ios-sub-title-font-size;
|
||||
|
||||
font-weight: $action-sheet-ios-title-font-weight;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -55,6 +55,9 @@ $action-sheet-ios-title-font-size: 13px !default;
|
||||
/// @prop - Font weight of the action sheet title
|
||||
$action-sheet-ios-title-font-weight: 400 !default;
|
||||
|
||||
/// @prop - Font weight of the action sheet title when it has a sub title
|
||||
$action-sheet-ios-title-with-sub-title-font-weight: 600 !default;
|
||||
|
||||
/// @prop - Border width of the action sheet title
|
||||
$action-sheet-ios-title-border-width: $hairlines-width !default;
|
||||
|
||||
@ -72,10 +75,10 @@ $action-sheet-ios-title-border-color: rgba($text-col
|
||||
// --------------------------------------------------
|
||||
|
||||
/// @prop - Font size of the action sheet sub title
|
||||
$action-sheet-ios-sub-title-font-size: 12px !default;
|
||||
$action-sheet-ios-sub-title-font-size: 13px !default;
|
||||
|
||||
/// @prop - Padding top of the action sheet sub title
|
||||
$action-sheet-ios-sub-title-padding-top: 15px !default;
|
||||
$action-sheet-ios-sub-title-padding-top: 6px !default;
|
||||
|
||||
/// @prop - Padding end of the action sheet sub title
|
||||
$action-sheet-ios-sub-title-padding-end: 0 !default;
|
||||
@ -103,7 +106,7 @@ $action-sheet-ios-button-text-color: ion-color(prim
|
||||
$action-sheet-ios-button-icon-font-size: 28px !default;
|
||||
|
||||
/// @prop - Padding right of the action sheet button icon
|
||||
$action-sheet-ios-button-icon-padding-right: .1em !default;
|
||||
$action-sheet-ios-button-icon-padding-right: .3em !default;
|
||||
|
||||
/// @prop - Font size of the action sheet button
|
||||
$action-sheet-ios-button-font-size: 20px !default;
|
||||
|
||||
@ -258,7 +258,10 @@ export class ActionSheet implements ComponentInterface, OverlayInterface {
|
||||
<div class="action-sheet-container">
|
||||
<div class="action-sheet-group" ref={el => this.groupEl = el}>
|
||||
{this.header !== undefined &&
|
||||
<div class="action-sheet-title">
|
||||
<div class={{
|
||||
'action-sheet-title': true,
|
||||
'action-sheet-has-sub-title': this.subHeader !== undefined
|
||||
}}>
|
||||
{this.header}
|
||||
{this.subHeader && <div class="action-sheet-sub-title">{this.subHeader}</div>}
|
||||
</div>
|
||||
|
||||
@ -34,6 +34,39 @@ Any of the defined [CSS Custom Properties](#css-custom-properties) can be used t
|
||||
|
||||
> If you are building an Ionic Angular app, the styles need to be added to a global stylesheet file. Read [Style Placement](#style-placement) in the Angular section below for more information.
|
||||
|
||||
## Interfaces
|
||||
|
||||
### ActionSheetButton
|
||||
|
||||
```typescript
|
||||
interface ActionSheetButton {
|
||||
text?: string;
|
||||
role?: 'cancel' | 'destructive' | 'selected' | string;
|
||||
icon?: string;
|
||||
cssClass?: string | string[];
|
||||
handler?: () => boolean | void | Promise<boolean | void>;
|
||||
}
|
||||
```
|
||||
|
||||
### ActionSheetOptions
|
||||
|
||||
```typescript
|
||||
interface ActionSheetOptions {
|
||||
header?: string;
|
||||
subHeader?: string;
|
||||
cssClass?: string | string[];
|
||||
buttons: (ActionSheetButton | string)[];
|
||||
backdropDismiss?: boolean;
|
||||
translucent?: boolean;
|
||||
animated?: boolean;
|
||||
mode?: Mode;
|
||||
keyboardClose?: boolean;
|
||||
id?: string;
|
||||
|
||||
enterAnimation?: AnimationBuilder;
|
||||
leaveAnimation?: AnimationBuilder;
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Auto Generated Below -->
|
||||
|
||||
|
||||
@ -41,6 +41,76 @@ Any of the defined [CSS Custom Properties](#css-custom-properties) can be used t
|
||||
|
||||
> If you are building an Ionic Angular app, the styles need to be added to a global stylesheet file. Read [Style Placement](#style-placement) in the Angular section below for more information.
|
||||
|
||||
## Interfaces
|
||||
|
||||
### AlertButton
|
||||
|
||||
```typescript
|
||||
interface AlertButton {
|
||||
text: string;
|
||||
role?: string;
|
||||
cssClass?: string | string[];
|
||||
handler?: (value: any) => boolean | void | {[key: string]: any};
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### AlertInput
|
||||
|
||||
```typescript
|
||||
interface AlertInput {
|
||||
type?: TextFieldTypes | 'checkbox' | 'radio' | 'textarea';
|
||||
name?: string;
|
||||
placeholder?: string;
|
||||
value?: any;
|
||||
label?: string;
|
||||
checked?: boolean;
|
||||
disabled?: boolean;
|
||||
id?: string;
|
||||
handler?: (input: AlertInput) => void;
|
||||
min?: string | number;
|
||||
max?: string | number;
|
||||
cssClass?: string | string[];
|
||||
attributes?: AlertInputAttributes | AlertTextareaAttributes;
|
||||
tabindex?: number;
|
||||
}
|
||||
```
|
||||
|
||||
### AlertInputAttributes
|
||||
|
||||
```typescript
|
||||
interface AlertInputAttributes extends JSXBase.InputHTMLAttributes<HTMLInputElement> {}
|
||||
```
|
||||
|
||||
### AlertOptions
|
||||
|
||||
```typescript
|
||||
interface AlertOptions {
|
||||
header?: string;
|
||||
subHeader?: string;
|
||||
message?: string | IonicSafeString;
|
||||
cssClass?: string | string[];
|
||||
inputs?: AlertInput[];
|
||||
buttons?: (AlertButton | string)[];
|
||||
backdropDismiss?: boolean;
|
||||
translucent?: boolean;
|
||||
animated?: boolean;
|
||||
|
||||
mode?: Mode;
|
||||
keyboardClose?: boolean;
|
||||
id?: string;
|
||||
|
||||
enterAnimation?: AnimationBuilder;
|
||||
leaveAnimation?: AnimationBuilder;
|
||||
}
|
||||
```
|
||||
|
||||
### AlertTextareaAttributes
|
||||
```typescript
|
||||
interface AlertTextareaAttributes extends JSXBase.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
||||
```
|
||||
|
||||
|
||||
|
||||
<!-- Auto Generated Below -->
|
||||
|
||||
|
||||
@ -62,7 +62,6 @@
|
||||
user-select: none;
|
||||
vertical-align: top; // the better option for most scenarios
|
||||
vertical-align: -webkit-baseline-middle; // the best for those that support it
|
||||
pointer-events: auto;
|
||||
|
||||
font-kerning: none;
|
||||
}
|
||||
|
||||
@ -146,9 +146,9 @@
|
||||
display: none;
|
||||
position: absolute;
|
||||
|
||||
/* stylelint-disable property-blacklist */
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
left: -100%;
|
||||
/* stylelint-enable property-blacklist */
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
@ -161,9 +161,9 @@
|
||||
.transition-cover {
|
||||
position: absolute;
|
||||
|
||||
/* stylelint-disable property-blacklist */
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
right: 0;
|
||||
/* stylelint-enable property-blacklist */
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@ -177,9 +177,9 @@
|
||||
display: block;
|
||||
position: absolute;
|
||||
|
||||
/* stylelint-disable property-blacklist */
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
right: 0;
|
||||
/* stylelint-enable property-blacklist */
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
|
||||
width: 10px;
|
||||
height: 100%;
|
||||
|
||||
@ -5,10 +5,10 @@
|
||||
|
||||
ion-item-options {
|
||||
@include multi-dir() {
|
||||
/* stylelint-disable property-blacklist */
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
top: 0;
|
||||
right: 0;
|
||||
/* stylelint-enable property-blacklist */
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
}
|
||||
|
||||
@include ltr() {
|
||||
@ -19,10 +19,10 @@ ion-item-options {
|
||||
justify-content: flex-start;
|
||||
|
||||
&:not(.item-options-end) {
|
||||
/* stylelint-disable property-blacklist */
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
right: auto;
|
||||
left: 0;
|
||||
/* stylelint-enable property-blacklist */
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
|
||||
justify-content: flex-end;
|
||||
}
|
||||
@ -41,10 +41,10 @@ ion-item-options {
|
||||
|
||||
.item-options-start {
|
||||
@include multi-dir() {
|
||||
/* stylelint-disable property-blacklist */
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
right: auto;
|
||||
left: 0;
|
||||
/* stylelint-enable property-blacklist */
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
}
|
||||
|
||||
@include ltr() {
|
||||
|
||||
@ -32,7 +32,7 @@ ion-item-sliding .item {
|
||||
|
||||
.item-sliding-active-swipe-end .item-options-end .item-option-expandable {
|
||||
@include multi-dir() {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
/* stylelint-disable-next-line property-disallowed-list */
|
||||
padding-left: 100%;
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@ ion-item-sliding .item {
|
||||
|
||||
.item-sliding-active-swipe-start .item-options-start .item-option-expandable {
|
||||
@include multi-dir() {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
/* stylelint-disable-next-line property-disallowed-list */
|
||||
padding-right: 100%;
|
||||
}
|
||||
|
||||
|
||||
@ -299,7 +299,7 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
|
||||
|
||||
render() {
|
||||
const { counterString, detail, detailIcon, download, fill, labelColorStyles, lines, disabled, href, rel, shape, target, routerAnimation, routerDirection } = this;
|
||||
const childStyles = {};
|
||||
const childStyles = {} as any;
|
||||
const mode = getIonMode(this);
|
||||
const clickable = this.isClickable();
|
||||
const canActivate = this.canActivate();
|
||||
@ -322,10 +322,11 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
|
||||
this.itemStyles.forEach(value => {
|
||||
Object.assign(childStyles, value);
|
||||
});
|
||||
const ariaDisabled = (disabled || childStyles['item-interactive-disabled']) ? 'true' : null;
|
||||
|
||||
return (
|
||||
<Host
|
||||
aria-disabled={disabled ? 'true' : null}
|
||||
aria-disabled={ariaDisabled}
|
||||
class={{
|
||||
...childStyles,
|
||||
...labelColorStyles,
|
||||
|
||||
11
core/src/components/item/test/a11y/e2e.ts
Normal file
11
core/src/components/item/test/a11y/e2e.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
import { AxePuppeteer } from '@axe-core/puppeteer';
|
||||
|
||||
test('item: axe', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/item/test/a11y?ionic:_testing=true'
|
||||
});
|
||||
|
||||
const results = await new AxePuppeteer(page).analyze();
|
||||
expect(results.violations.length).toEqual(0);
|
||||
});
|
||||
105
core/src/components/item/test/a11y/index.html
Normal file
105
core/src/components/item/test/a11y/index.html
Normal file
@ -0,0 +1,105 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Item - a11y</title>
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
|
||||
<link href="../../../../../css/core.css" rel="stylesheet">
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main>
|
||||
<h1>Item</h1>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Item with Input</ion-label>
|
||||
<ion-input placeholder="Placeholder"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item disabled>
|
||||
<ion-label>Item disabled with Input</ion-label>
|
||||
<ion-input placeholder="Placeholder"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Item with Input disabled</ion-label>
|
||||
<ion-input placeholder="Placeholder" disabled></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label position="floating">Item with Select</ion-label>
|
||||
<ion-select>
|
||||
<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 disabled>
|
||||
<ion-label position="floating">Item disabled with Select</ion-label>
|
||||
<ion-select>
|
||||
<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 position="floating">Item with Select disabled</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>Item with Toggle</ion-label>
|
||||
<ion-toggle slot="end"></ion-toggle>
|
||||
</ion-item>
|
||||
|
||||
<ion-item disabled>
|
||||
<ion-label>Item disabled with Toggle</ion-label>
|
||||
<ion-toggle slot="end"></ion-toggle>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Item with Toggle disabled</ion-label>
|
||||
<ion-toggle slot="end" disabled></ion-toggle>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Item with Radio</ion-label>
|
||||
<ion-radio slot="start" value="biff"></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<ion-item disabled>
|
||||
<ion-label>Item disabled with Radio</ion-label>
|
||||
<ion-radio slot="start" value="biff"></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Item with Radio disabled</ion-label>
|
||||
<ion-radio slot="start" value="biff" disabled></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@ -37,6 +37,28 @@ Any of the defined [CSS Custom Properties](#css-custom-properties) can be used t
|
||||
|
||||
> If you are building an Ionic Angular app, the styles need to be added to a global stylesheet file. Read [Style Placement](#style-placement) in the Angular section below for more information.
|
||||
|
||||
## Interfaces
|
||||
|
||||
### LoadingOptions
|
||||
|
||||
```typescript
|
||||
interface LoadingOptions {
|
||||
spinner?: SpinnerTypes | null;
|
||||
message?: string | IonicSafeString;
|
||||
cssClass?: string | string[];
|
||||
showBackdrop?: boolean;
|
||||
duration?: number;
|
||||
translucent?: boolean;
|
||||
animated?: boolean;
|
||||
backdropDismiss?: boolean;
|
||||
mode?: Mode;
|
||||
keyboardClose?: boolean;
|
||||
id?: string;
|
||||
|
||||
enterAnimation?: AnimationBuilder;
|
||||
leaveAnimation?: AnimationBuilder;
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Auto Generated Below -->
|
||||
|
||||
@ -139,17 +161,23 @@ import { IonButton, IonContent, IonPage, useIonLoading } from '@ionic/react';
|
||||
interface LoadingProps {}
|
||||
|
||||
const LoadingExample: React.FC<LoadingProps> = () => {
|
||||
const [present] = useIonLoading();
|
||||
const [present, dismiss] = useIonLoading();
|
||||
/**
|
||||
* The recommended way of dismissing is to use the `dismiss` property
|
||||
* on `IonLoading`, but the `dismiss` method returned from `useIonLoading`
|
||||
* can be used for more complex scenarios.
|
||||
*/
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
onClick={() => {
|
||||
present({
|
||||
duration: 3000,
|
||||
message: 'Loading...',
|
||||
duration: 3000
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
Show Loading
|
||||
</IonButton>
|
||||
|
||||
@ -7,17 +7,23 @@ import { IonButton, IonContent, IonPage, useIonLoading } from '@ionic/react';
|
||||
interface LoadingProps {}
|
||||
|
||||
const LoadingExample: React.FC<LoadingProps> = () => {
|
||||
const [present] = useIonLoading();
|
||||
const [present, dismiss] = useIonLoading();
|
||||
/**
|
||||
* The recommended way of dismissing is to use the `dismiss` property
|
||||
* on `IonLoading`, but the `dismiss` method returned from `useIonLoading`
|
||||
* can be used for more complex scenarios.
|
||||
*/
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
onClick={() => {
|
||||
present({
|
||||
duration: 3000,
|
||||
message: 'Loading...',
|
||||
duration: 3000
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
Show Loading
|
||||
</IonButton>
|
||||
|
||||
@ -4,6 +4,7 @@ import { config } from '../../global/config';
|
||||
import { getIonMode } from '../../global/ionic-global';
|
||||
import { Color } from '../../interface';
|
||||
import { ButtonInterface } from '../../utils/element-interface';
|
||||
import { inheritAttributes } from '../../utils/helpers';
|
||||
import { menuController } from '../../utils/menu-controller';
|
||||
import { createColorClasses, hostContext } from '../../utils/theme';
|
||||
import { updateVisibility } from '../menu-toggle/menu-toggle-util';
|
||||
@ -23,6 +24,8 @@ import { updateVisibility } from '../menu-toggle/menu-toggle-util';
|
||||
shadow: true
|
||||
})
|
||||
export class MenuButton implements ComponentInterface, ButtonInterface {
|
||||
private inheritedAttributes: { [k: string]: any } = {};
|
||||
|
||||
@Element() el!: HTMLIonSegmentElement;
|
||||
|
||||
@State() visible = false;
|
||||
@ -54,6 +57,10 @@ export class MenuButton implements ComponentInterface, ButtonInterface {
|
||||
*/
|
||||
@Prop() type: 'submit' | 'reset' | 'button' = 'button';
|
||||
|
||||
componentWillLoad() {
|
||||
this.inheritedAttributes = inheritAttributes(this.el, ['aria-label']);
|
||||
}
|
||||
|
||||
componentDidLoad() {
|
||||
this.visibilityChanged();
|
||||
}
|
||||
@ -69,7 +76,7 @@ export class MenuButton implements ComponentInterface, ButtonInterface {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { color, disabled } = this;
|
||||
const { color, disabled, inheritedAttributes } = this;
|
||||
const mode = getIonMode(this);
|
||||
const menuIcon = config.get('menuIcon', mode === 'ios' ? 'menu-outline' : 'menu-sharp');
|
||||
const hidden = this.autoHide && !this.visible;
|
||||
@ -78,6 +85,8 @@ export class MenuButton implements ComponentInterface, ButtonInterface {
|
||||
type: this.type
|
||||
};
|
||||
|
||||
const ariaLabel = inheritedAttributes['aria-label'] || 'menu';
|
||||
|
||||
return (
|
||||
<Host
|
||||
onClick={this.onClick}
|
||||
@ -99,7 +108,7 @@ export class MenuButton implements ComponentInterface, ButtonInterface {
|
||||
disabled={disabled}
|
||||
class="button-native"
|
||||
part="native"
|
||||
aria-label="menu"
|
||||
aria-label={ariaLabel}
|
||||
>
|
||||
<span class="button-inner">
|
||||
<slot>
|
||||
|
||||
11
core/src/components/menu-button/test/a11y/e2e.ts
Normal file
11
core/src/components/menu-button/test/a11y/e2e.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
import { AxePuppeteer } from '@axe-core/puppeteer';
|
||||
|
||||
test('menu-button: axe', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/menu-button/test/a11y?ionic:_testing=true'
|
||||
});
|
||||
|
||||
const results = await new AxePuppeteer(page).analyze();
|
||||
expect(results.violations.length).toEqual(0);
|
||||
});
|
||||
21
core/src/components/menu-button/test/a11y/index.html
Normal file
21
core/src/components/menu-button/test/a11y/index.html
Normal file
@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Menu Button - a11y</title>
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
|
||||
<link href="../../../../../css/core.css" rel="stylesheet">
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main>
|
||||
<h1>Menu Button</h1>
|
||||
<ion-menu-button auto-hide="false"></ion-menu-button>
|
||||
<ion-menu-button auto-hide="false" aria-label="Custom Label"></ion-menu-button>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@ -29,6 +29,7 @@
|
||||
<ion-menu-button auto-hide="false" color="secondary" class="custom ion-focused"></ion-menu-button>
|
||||
<ion-menu-button auto-hide="false" class="custom-large"></ion-menu-button>
|
||||
<ion-menu-button auto-hide="false" class="custom-large ion-focused"></ion-menu-button>
|
||||
<ion-menu-button auto-hide="false" aria-label="My Custom Menu Button Label"></ion-menu-button>
|
||||
|
||||
<h1>Colors</h1>
|
||||
<ion-menu-button auto-hide="false" color="primary"></ion-menu-button>
|
||||
|
||||
@ -63,7 +63,7 @@
|
||||
--ion-safe-area-right: 0px;
|
||||
|
||||
@include multi-dir() {
|
||||
/* stylelint-disable property-blacklist */
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
@ -75,7 +75,7 @@
|
||||
@include multi-dir() {
|
||||
right: 0;
|
||||
left: auto;
|
||||
/* stylelint-enable property-blacklist */
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -102,6 +102,30 @@ ion-modal.stack-modal {
|
||||
}
|
||||
```
|
||||
|
||||
## Interfaces
|
||||
|
||||
### ModalOptions
|
||||
|
||||
```typescript
|
||||
interface ModalOptions<T extends ComponentRef = ComponentRef> {
|
||||
component: T;
|
||||
componentProps?: ComponentProps<T>;
|
||||
presentingElement?: HTMLElement;
|
||||
showBackdrop?: boolean;
|
||||
backdropDismiss?: boolean;
|
||||
cssClass?: string | string[];
|
||||
animated?: boolean;
|
||||
swipeToClose?: boolean;
|
||||
|
||||
mode?: Mode;
|
||||
keyboardClose?: boolean;
|
||||
id?: string;
|
||||
|
||||
enterAnimation?: AnimationBuilder;
|
||||
leaveAnimation?: AnimationBuilder;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
<!-- Auto Generated Below -->
|
||||
|
||||
@ -549,6 +573,7 @@ In most scenarios, setting a ref on `IonRouterOutlet` and passing that ref's `cu
|
||||
isOpen={show2ndModal}
|
||||
cssClass='my-custom-class'
|
||||
presentingElement={firstModalRef.current}
|
||||
swipeToClose={true}
|
||||
onDidDismiss={() => setShow2ndModal(false)}>
|
||||
<p>This is more modal content</p>
|
||||
<IonButton onClick={() => setShow2ndModal(false)}>Close Modal</IonButton>
|
||||
@ -811,6 +836,47 @@ export default defineComponent({
|
||||
|
||||
> If you need a wrapper element inside of your modal component, we recommend using an `<ion-page>` so that the component dimensions are still computed properly.
|
||||
|
||||
### Swipeable Modals
|
||||
|
||||
Modals in iOS mode have the ability to be presented in a card-style and swiped to close. The card-style presentation and swipe to close gesture are not mutually exclusive, meaning you can pick and choose which features you want to use. For example, you can have a card-style modal that cannot be swiped or a full sized modal that can be swiped.
|
||||
|
||||
> Card style modals when running on iPhone-sized devices do not have backdrops. As a result, the `--backdrop-opacity` variable will not have any effect.
|
||||
|
||||
```html
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-content>
|
||||
<ion-button @click="setOpen(true)">Show Modal</ion-button>
|
||||
<ion-modal
|
||||
:is-open="isOpenRef"
|
||||
css-class="my-custom-class"
|
||||
:swipe-to-close="true"
|
||||
:presenting-element="$parent.$refs.ionRouterOutlet"
|
||||
@didDismiss="setOpen(false)"
|
||||
>
|
||||
<Modal :data="data"></Modal>
|
||||
</ion-modal>
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { IonModal, IonButton, IonContent, IonPage } from '@ionic/vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import Modal from './modal.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: { IonModal, IonButton, Modal, IonContent, IonPage },
|
||||
setup() {
|
||||
const isOpenRef = ref(false);
|
||||
const setOpen = (state: boolean) => isOpenRef.value = state;
|
||||
const data = { content: 'New Content' };
|
||||
return { isOpenRef, setOpen, data }
|
||||
}
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
@ -147,6 +147,7 @@ In most scenarios, setting a ref on `IonRouterOutlet` and passing that ref's `cu
|
||||
isOpen={show2ndModal}
|
||||
cssClass='my-custom-class'
|
||||
presentingElement={firstModalRef.current}
|
||||
swipeToClose={true}
|
||||
onDidDismiss={() => setShow2ndModal(false)}>
|
||||
<p>This is more modal content</p>
|
||||
<IonButton onClick={() => setShow2ndModal(false)}>Close Modal</IonButton>
|
||||
|
||||
@ -93,3 +93,44 @@ export default defineComponent({
|
||||
```
|
||||
|
||||
> If you need a wrapper element inside of your modal component, we recommend using an `<ion-page>` so that the component dimensions are still computed properly.
|
||||
|
||||
### Swipeable Modals
|
||||
|
||||
Modals in iOS mode have the ability to be presented in a card-style and swiped to close. The card-style presentation and swipe to close gesture are not mutually exclusive, meaning you can pick and choose which features you want to use. For example, you can have a card-style modal that cannot be swiped or a full sized modal that can be swiped.
|
||||
|
||||
> Card style modals when running on iPhone-sized devices do not have backdrops. As a result, the `--backdrop-opacity` variable will not have any effect.
|
||||
|
||||
```html
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-content>
|
||||
<ion-button @click="setOpen(true)">Show Modal</ion-button>
|
||||
<ion-modal
|
||||
:is-open="isOpenRef"
|
||||
css-class="my-custom-class"
|
||||
:swipe-to-close="true"
|
||||
:presenting-element="$parent.$refs.ionRouterOutlet"
|
||||
@didDismiss="setOpen(false)"
|
||||
>
|
||||
<Modal :data="data"></Modal>
|
||||
</ion-modal>
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { IonModal, IonButton, IonContent, IonPage } from '@ionic/vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import Modal from './modal.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: { IonModal, IonButton, Modal, IonContent, IonPage },
|
||||
setup() {
|
||||
const isOpenRef = ref(false);
|
||||
const setOpen = (state: boolean) => isOpenRef.value = state;
|
||||
const data = { content: 'New Content' };
|
||||
return { isOpenRef, setOpen, data }
|
||||
}
|
||||
});
|
||||
</script>
|
||||
```
|
||||
@ -169,7 +169,7 @@ describe('NavController', () => {
|
||||
|
||||
describe('insert', () => {
|
||||
|
||||
it('should insert at the begining with no async transition', async () => {
|
||||
it('should insert at the beginning with no async transition', async () => {
|
||||
const view4 = mockView(MockView4);
|
||||
const instance4 = spyOnLifecycles(view4);
|
||||
const opts: NavOptions = {};
|
||||
|
||||
@ -2,7 +2,71 @@
|
||||
|
||||
A Picker is a dialog that displays a row of buttons and columns underneath. It appears on top of the app's content, and at the bottom of the viewport.
|
||||
|
||||
## Interfaces
|
||||
|
||||
### PickerButton
|
||||
|
||||
```typescript
|
||||
interface PickerButton {
|
||||
text?: string;
|
||||
role?: string;
|
||||
cssClass?: string | string[];
|
||||
handler?: (value: any) => boolean | void;
|
||||
}
|
||||
```
|
||||
|
||||
### PickerColumn
|
||||
|
||||
```typescript
|
||||
interface PickerColumn {
|
||||
name: string;
|
||||
align?: string;
|
||||
selectedIndex?: number;
|
||||
prevSelected?: number;
|
||||
prefix?: string;
|
||||
suffix?: string;
|
||||
options: PickerColumnOption[];
|
||||
cssClass?: string | string[];
|
||||
columnWidth?: string;
|
||||
prefixWidth?: string;
|
||||
suffixWidth?: string;
|
||||
optionsWidth?: string;
|
||||
refresh?: () => void;
|
||||
}
|
||||
```
|
||||
|
||||
### PickerColumnOption
|
||||
|
||||
```typescript
|
||||
interface PickerColumnOption {
|
||||
text?: string;
|
||||
value?: any;
|
||||
disabled?: boolean;
|
||||
duration?: number;
|
||||
transform?: string;
|
||||
selected?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
### PickerOptions
|
||||
|
||||
```typescript
|
||||
interface PickerOptions {
|
||||
columns: PickerColumn[];
|
||||
buttons?: PickerButton[];
|
||||
cssClass?: string | string[];
|
||||
showBackdrop?: boolean;
|
||||
backdropDismiss?: boolean;
|
||||
animated?: boolean;
|
||||
|
||||
mode?: Mode;
|
||||
keyboardClose?: boolean;
|
||||
id?: string;
|
||||
|
||||
enterAnimation?: AnimationBuilder;
|
||||
leaveAnimation?: AnimationBuilder;
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Auto Generated Below -->
|
||||
|
||||
|
||||
@ -36,47 +36,6 @@ If you need fine grained control over when the popover is presented and dismisse
|
||||
|
||||
We typically recommend that you write your popovers inline as it streamlines the amount of code in your application. You should only use the `popoverController` for complex use cases where writing a popover inline is impractical. When using a controller, your popover is not created ahead of time, so properties such as `trigger` and `trigger-action` are not applicable here. In addition, nested popovers are not compatible with the controller approach because the popover is automatically added to the root of your application when the `create` method is called.
|
||||
|
||||
## Interfaces
|
||||
|
||||
Below you will find all of the options available to you when using the `popoverController`. These options should be supplied when calling `popoverController.create()`.
|
||||
|
||||
```typescript
|
||||
interface PopoverOptions {
|
||||
component: any;
|
||||
componentProps?: { [key: string]: any };
|
||||
showBackdrop?: boolean;
|
||||
backdropDismiss?: boolean;
|
||||
translucent?: boolean;
|
||||
cssClass?: string | string[];
|
||||
event?: Event;
|
||||
animated?: boolean;
|
||||
|
||||
mode?: 'ios' | 'md';
|
||||
keyboardClose?: boolean;
|
||||
id?: string;
|
||||
|
||||
enterAnimation?: AnimationBuilder;
|
||||
leaveAnimation?: AnimationBuilder;
|
||||
|
||||
size?: PopoverSize;
|
||||
dismissOnSelect?: boolean;
|
||||
reference?: PositionReference;
|
||||
side?: PositionSide;
|
||||
align?: PositionAlign;
|
||||
}
|
||||
```
|
||||
|
||||
## Types
|
||||
|
||||
Below you will find all of the custom types for `ion-popover`:
|
||||
|
||||
```typescript
|
||||
type PopoverSize = 'cover' | 'auto';
|
||||
type TriggerAction = 'click' | 'hover' | 'context-menu';
|
||||
type PositionReference = 'trigger' | 'event';
|
||||
type PositionSide = 'top' | 'right' | 'bottom' | 'left' | 'start' | 'end';
|
||||
type PositionAlign = 'start' | 'center' | 'end';
|
||||
```
|
||||
|
||||
## Customization
|
||||
|
||||
@ -155,6 +114,48 @@ You can use the `dismissOnSelect` property to automatically close the popover wh
|
||||
|
||||
> Nested popovers cannot be created when using the `popoverController` because the popover is automatically added to the root of your application when the `create` method is called.
|
||||
|
||||
## Interfaces
|
||||
|
||||
Below you will find all of the options available to you when using the `popoverController`. These options should be supplied when calling `popoverController.create()`.
|
||||
|
||||
```typescript
|
||||
interface PopoverOptions {
|
||||
component: any;
|
||||
componentProps?: { [key: string]: any };
|
||||
showBackdrop?: boolean;
|
||||
backdropDismiss?: boolean;
|
||||
translucent?: boolean;
|
||||
cssClass?: string | string[];
|
||||
event?: Event;
|
||||
animated?: boolean;
|
||||
|
||||
mode?: 'ios' | 'md';
|
||||
keyboardClose?: boolean;
|
||||
id?: string;
|
||||
|
||||
enterAnimation?: AnimationBuilder;
|
||||
leaveAnimation?: AnimationBuilder;
|
||||
|
||||
size?: PopoverSize;
|
||||
dismissOnSelect?: boolean;
|
||||
reference?: PositionReference;
|
||||
side?: PositionSide;
|
||||
align?: PositionAlign;
|
||||
}
|
||||
```
|
||||
|
||||
## Types
|
||||
|
||||
Below you will find all of the custom types for `ion-popover`:
|
||||
|
||||
```typescript
|
||||
type PopoverSize = 'cover' | 'auto';
|
||||
type TriggerAction = 'click' | 'hover' | 'context-menu';
|
||||
type PositionReference = 'trigger' | 'event';
|
||||
type PositionSide = 'top' | 'right' | 'bottom' | 'left' | 'start' | 'end';
|
||||
type PositionAlign = 'start' | 'center' | 'end';
|
||||
```
|
||||
|
||||
## Accessibility
|
||||
|
||||
### Keyboard Navigation
|
||||
|
||||
@ -46,10 +46,10 @@
|
||||
|
||||
// Extend a bit to overflow. The size of animated distance.
|
||||
.buffer-circles {
|
||||
/* stylelint-disable property-blacklist */
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
right: -10px;
|
||||
left: -10px;
|
||||
/* stylelint-enable property-blacklist */
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
}
|
||||
|
||||
// Determinate progress bar
|
||||
@ -58,7 +58,7 @@
|
||||
.progress,
|
||||
.progress-buffer-bar,
|
||||
.buffer-circles-container {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
/* stylelint-disable-next-line property-disallowed-list */
|
||||
transform-origin: left top;
|
||||
|
||||
transition: transform 150ms linear;
|
||||
@ -88,12 +88,12 @@
|
||||
// --------------------------------------------------
|
||||
|
||||
.indeterminate-bar-primary {
|
||||
/* stylelint-disable property-blacklist */
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: -145.166611%;
|
||||
/* stylelint-enable property-blacklist */
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
|
||||
animation: primary-indeterminate-translate 2s infinite linear;
|
||||
|
||||
@ -104,12 +104,12 @@
|
||||
}
|
||||
|
||||
.indeterminate-bar-secondary {
|
||||
/* stylelint-disable property-blacklist */
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: -54.888891%;
|
||||
/* stylelint-enable property-blacklist */
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
|
||||
animation: secondary-indeterminate-translate 2s infinite linear;
|
||||
|
||||
@ -125,11 +125,11 @@
|
||||
.buffer-circles {
|
||||
background-image: radial-gradient(ellipse at center, var(--buffer-background) 0%, var(--buffer-background) 30%, transparent 30%);
|
||||
|
||||
/* stylelint-disable property-blacklist */
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
background-repeat: repeat-x;
|
||||
background-position: 5px center;
|
||||
background-size: 10px 10px;
|
||||
/* stylelint-enable property-blacklist */
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
|
||||
z-index: 0;
|
||||
animation: buffering 450ms infinite linear;
|
||||
|
||||
@ -105,12 +105,12 @@
|
||||
@include margin-horizontal(-13px, null);
|
||||
|
||||
@include multi-dir() {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
/* stylelint-disable-next-line property-disallowed-list */
|
||||
border-radius: 50% 50% 50% 0;
|
||||
}
|
||||
|
||||
@include rtl() {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
/* stylelint-disable-next-line property-disallowed-list */
|
||||
left: unset;
|
||||
}
|
||||
|
||||
|
||||
@ -82,7 +82,7 @@
|
||||
);
|
||||
|
||||
@include rtl() {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
/* stylelint-disable-next-line property-disallowed-list */
|
||||
left: unset;
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@
|
||||
@include position(calc((var(--height) - var(--bar-height)) / 2), null, null, 0);
|
||||
|
||||
@include rtl() {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
/* stylelint-disable-next-line property-disallowed-list */
|
||||
left: unset;
|
||||
}
|
||||
|
||||
@ -127,7 +127,7 @@
|
||||
);
|
||||
|
||||
@include rtl() {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
/* stylelint-disable-next-line property-disallowed-list */
|
||||
left: unset;
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
# ion-router-outlet
|
||||
|
||||
Router outlet is a component used in routing within an Angular or Vue app. It behaves in a similar way to Angular's built-in router outlet component and Vue's router view component, but contains the logic for providing a stacked navigation, and animating views in and out.
|
||||
Router outlet is a component used in routing within an Angular, React, or Vue app. It behaves in a similar way to Angular's built-in router outlet component and Vue's router view component, but contains the logic for providing a stacked navigation, and animating views in and out.
|
||||
|
||||
> Note: this component should only be used with Angular and Vue projects. For vanilla or Stencil JavaScript projects, use [`ion-router`](../router) and [`ion-route`](../route).
|
||||
> Note: this component should only be used with Angular, React, and Vue projects. For vanilla or Stencil JavaScript projects, use [`ion-router`](../router) and [`ion-route`](../route).
|
||||
|
||||
Although router outlet has methods for navigating around, it's recommended to use the navigation methods in your framework's router.
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@ export class RouterOutlet implements ComponentInterface, NavOutlet {
|
||||
private waitPromise?: Promise<void>;
|
||||
private gesture?: Gesture;
|
||||
private ani?: Animation;
|
||||
private animationEnabled = true;
|
||||
private gestureOrAnimationInProgress = false;
|
||||
|
||||
@Element() el!: HTMLElement;
|
||||
|
||||
@ -61,17 +61,22 @@ export class RouterOutlet implements ComponentInterface, NavOutlet {
|
||||
@Event({ bubbles: false }) ionNavDidChange!: EventEmitter<void>;
|
||||
|
||||
async connectedCallback() {
|
||||
const onStart = () => {
|
||||
this.gestureOrAnimationInProgress = true;
|
||||
if (this.swipeHandler) {
|
||||
this.swipeHandler.onStart();
|
||||
}
|
||||
}
|
||||
|
||||
this.gesture = (await import('../../utils/gesture/swipe-back')).createSwipeBackGesture(
|
||||
this.el,
|
||||
() => !!this.swipeHandler && this.swipeHandler.canStart() && this.animationEnabled,
|
||||
() => this.swipeHandler && this.swipeHandler.onStart(),
|
||||
() => !this.gestureOrAnimationInProgress && !!this.swipeHandler && this.swipeHandler.canStart(),
|
||||
() => onStart(),
|
||||
step => this.ani && this.ani.progressStep(step),
|
||||
(shouldComplete, step, dur) => {
|
||||
if (this.ani) {
|
||||
this.animationEnabled = false;
|
||||
|
||||
this.ani.onFinish(() => {
|
||||
this.animationEnabled = true;
|
||||
this.gestureOrAnimationInProgress = false;
|
||||
|
||||
if (this.swipeHandler) {
|
||||
this.swipeHandler.onEnd(shouldComplete);
|
||||
@ -97,7 +102,8 @@ export class RouterOutlet implements ComponentInterface, NavOutlet {
|
||||
}
|
||||
|
||||
this.ani.progressEnd(shouldComplete ? 1 : 0, newStepValue, dur);
|
||||
|
||||
} else {
|
||||
this.gestureOrAnimationInProgress = false;
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -191,7 +197,34 @@ export class RouterOutlet implements ComponentInterface, NavOutlet {
|
||||
leavingEl,
|
||||
baseEl: el,
|
||||
progressCallback: (opts.progressAnimation
|
||||
? ani => this.ani = ani
|
||||
? ani => {
|
||||
/**
|
||||
* Because this progress callback is called asynchronously
|
||||
* it is possible for the gesture to start and end before
|
||||
* the animation is ever set. In that scenario, we should
|
||||
* immediately call progressEnd so that the transition promise
|
||||
* resolves and the gesture does not get locked up.
|
||||
*/
|
||||
if (ani !== undefined && !this.gestureOrAnimationInProgress) {
|
||||
this.gestureOrAnimationInProgress = true;
|
||||
ani.onFinish(() => {
|
||||
this.gestureOrAnimationInProgress = false;
|
||||
if (this.swipeHandler) {
|
||||
this.swipeHandler.onEnd(false);
|
||||
}
|
||||
}, { oneTimeCallback: true });
|
||||
|
||||
/**
|
||||
* Playing animation to beginning
|
||||
* with a duration of 0 prevents
|
||||
* any flickering when the animation
|
||||
* is later cleaned up.
|
||||
*/
|
||||
ani.progressEnd(0, 0, 0);
|
||||
} else {
|
||||
this.ani = ani;
|
||||
}
|
||||
}
|
||||
: undefined
|
||||
),
|
||||
...opts,
|
||||
|
||||
@ -61,7 +61,7 @@ span {
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
/* stylelint-disable property-blacklist */
|
||||
/* stylelint-disable property-disallowed-list */
|
||||
@keyframes shimmer {
|
||||
0% {
|
||||
background-position: -400px 0;
|
||||
@ -71,4 +71,4 @@ span {
|
||||
background-position: 400px 0;
|
||||
}
|
||||
}
|
||||
/* stylelint-enable property-blacklist */
|
||||
/* stylelint-enable property-disallowed-list */
|
||||
|
||||
@ -10,6 +10,43 @@ Toasts can be positioned at the top, bottom or middle of the viewport. The posit
|
||||
|
||||
The toast can be dismissed automatically after a specific amount of time by passing the number of milliseconds to display it in the `duration` of the toast options. If a button with a role of `"cancel"` is added, then that button will dismiss the toast. To dismiss the toast after creation, call the `dismiss()` method on the instance.
|
||||
|
||||
## Interfaces
|
||||
|
||||
### ToastButton
|
||||
|
||||
```typescript
|
||||
interface ToastButton {
|
||||
text?: string;
|
||||
icon?: string;
|
||||
side?: 'start' | 'end';
|
||||
role?: 'cancel' | string;
|
||||
cssClass?: string | string[];
|
||||
handler?: () => boolean | void | Promise<boolean | void>;
|
||||
}
|
||||
```
|
||||
|
||||
### ToastOptions
|
||||
|
||||
```typescript
|
||||
interface ToastOptions {
|
||||
header?: string;
|
||||
message?: string | IonicSafeString;
|
||||
cssClass?: string | string[];
|
||||
duration?: number;
|
||||
buttons?: (ToastButton | string)[];
|
||||
position?: 'top' | 'bottom' | 'middle';
|
||||
translucent?: boolean;
|
||||
animated?: boolean;
|
||||
|
||||
color?: Color;
|
||||
mode?: Mode;
|
||||
keyboardClose?: boolean;
|
||||
id?: string;
|
||||
|
||||
enterAnimation?: AnimationBuilder;
|
||||
leaveAnimation?: AnimationBuilder;
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Auto Generated Below -->
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@ ion-virtual-scroll > .virtual-loading {
|
||||
}
|
||||
|
||||
ion-virtual-scroll > .virtual-item {
|
||||
/* stylelint-disable declaration-no-important, property-blacklist */
|
||||
/* stylelint-disable declaration-no-important, property-disallowed-list */
|
||||
position: absolute !important;
|
||||
|
||||
top: 0 !important;
|
||||
|
||||
Reference in New Issue
Block a user