chore(): sync vue tabs changes with master

This commit is contained in:
Liam DeBeasi
2021-03-04 15:31:42 -05:00
71 changed files with 2216 additions and 188 deletions

View File

@@ -155,6 +155,59 @@ async function presentActionSheet() {
### React
```tsx
/* Using with useIonActionSheet Hook */
import React from 'react';
import {
IonButton,
IonContent,
IonPage,
useIonActionSheet,
} from '@ionic/react';
const ActionSheetExample: React.FC = () => {
const [present, dismiss] = useIonActionSheet();
return (
<IonPage>
<IonContent>
<IonButton
expand="block"
onClick={() =>
present({
buttons: [{ text: 'Ok' }, { text: 'Cancel' }],
header: 'Action Sheet'
})
}
>
Show ActionSheet
</IonButton>
<IonButton
expand="block"
onClick={() =>
present([{ text: 'Ok' }, { text: 'Cancel' }], 'Action Sheet')
}
>
Show ActionSheet using params
</IonButton>
<IonButton
expand="block"
onClick={() => {
present([{ text: 'Ok' }, { text: 'Cancel' }], 'Action Sheet');
setTimeout(dismiss, 3000);
}}
>
Show ActionSheet, hide after 3 seconds
</IonButton>
</IonContent>
</IonPage>
);
};
```
```tsx
/* Using with IonActionSheet Component */
import React, { useState } from 'react';
import { IonActionSheet, IonContent, IonButton } from '@ionic/react';
import { trash, share, caretForwardCircle, heart, close } from 'ionicons/icons';

View File

@@ -1,4 +1,57 @@
```tsx
/* Using with useIonActionSheet Hook */
import React from 'react';
import {
IonButton,
IonContent,
IonPage,
useIonActionSheet,
} from '@ionic/react';
const ActionSheetExample: React.FC = () => {
const [present, dismiss] = useIonActionSheet();
return (
<IonPage>
<IonContent>
<IonButton
expand="block"
onClick={() =>
present({
buttons: [{ text: 'Ok' }, { text: 'Cancel' }],
header: 'Action Sheet'
})
}
>
Show ActionSheet
</IonButton>
<IonButton
expand="block"
onClick={() =>
present([{ text: 'Ok' }, { text: 'Cancel' }], 'Action Sheet')
}
>
Show ActionSheet using params
</IonButton>
<IonButton
expand="block"
onClick={() => {
present([{ text: 'Ok' }, { text: 'Cancel' }], 'Action Sheet');
setTimeout(dismiss, 3000);
}}
>
Show ActionSheet, hide after 3 seconds
</IonButton>
</IonContent>
</IonPage>
);
};
```
```tsx
/* Using with IonActionSheet Component */
import React, { useState } from 'react';
import { IonActionSheet, IonContent, IonButton } from '@ionic/react';
import { trash, share, caretForwardCircle, heart, close } from 'ionicons/icons';

View File

@@ -588,6 +588,48 @@ function presentAlertCheckbox() {
### React
```tsx
/* Using with useIonAlert Hook */
import React from 'react';
import { IonButton, IonContent, IonPage, useIonAlert } from '@ionic/react';
const AlertExample: React.FC = () => {
const [present] = useIonAlert();
return (
<IonPage>
<IonContent fullscreen>
<IonButton
expand="block"
onClick={() =>
present({
cssClass: 'my-css',
header: 'Alert',
message: 'alert from hook',
buttons: [
'Cancel',
{ text: 'Ok', handler: (d) => console.log('ok pressed') },
],
onDidDismiss: (e) => console.log('did dismiss'),
})
}
>
Show Alert
</IonButton>
<IonButton
expand="block"
onClick={() => present('hello with params', [{ text: 'Ok' }])}
>
Show Alert using params
</IonButton>
</IonContent>
</IonPage>
);
};
```
```tsx
/* Using with IonAlert Component */
import React, { useState } from 'react';
import { IonAlert, IonButton, IonContent } from '@ionic/react';

View File

@@ -1,4 +1,46 @@
```tsx
/* Using with useIonAlert Hook */
import React from 'react';
import { IonButton, IonContent, IonPage, useIonAlert } from '@ionic/react';
const AlertExample: React.FC = () => {
const [present] = useIonAlert();
return (
<IonPage>
<IonContent fullscreen>
<IonButton
expand="block"
onClick={() =>
present({
cssClass: 'my-css',
header: 'Alert',
message: 'alert from hook',
buttons: [
'Cancel',
{ text: 'Ok', handler: (d) => console.log('ok pressed') },
],
onDidDismiss: (e) => console.log('did dismiss'),
})
}
>
Show Alert
</IonButton>
<IonButton
expand="block"
onClick={() => present('hello with params', [{ text: 'Ok' }])}
>
Show Alert using params
</IonButton>
</IonContent>
</IonPage>
);
};
```
```tsx
/* Using with IonAlert Component */
import React, { useState } from 'react';
import { IonAlert, IonButton, IonContent } from '@ionic/react';

View File

@@ -169,3 +169,18 @@
:host(.has-focus) button {
pointer-events: auto;
}
// Item Floating: Placeholder
// ----------------------------------------------------------------
// When used with a floating item the placeholder should hide
:host-context(.item-label-floating.item-has-placeholder:not(.item-has-value)) {
transition: opacity 0.15s cubic-bezier(0.4, 0, 0.2, 1);
opacity: 0;
}
:host-context(.item-label-floating.item-has-placeholder:not(.item-has-value).item-has-focus) {
opacity: 1;
}

View File

@@ -190,15 +190,6 @@ export class Input implements ComponentInterface {
*/
@Prop({ mutable: true }) value?: string | number | null = '';
/**
* Update the native input element when the value changes
*/
@Watch('value')
protected valueChanged() {
this.emitStyle();
this.ionChange.emit({ value: this.value == null ? this.value : this.value.toString() });
}
/**
* Emitted when a keyboard input occurred.
*/
@@ -225,6 +216,23 @@ export class Input implements ComponentInterface {
*/
@Event() ionStyle!: EventEmitter<StyleEventDetail>;
/**
* Update the item classes when the placeholder changes
*/
@Watch('placeholder')
protected placeholderChanged() {
this.emitStyle();
}
/**
* Update the native input element when the value changes
*/
@Watch('value')
protected valueChanged() {
this.emitStyle();
this.ionChange.emit({ value: this.value == null ? this.value : this.value.toString() });
}
componentWillLoad() {
this.inheritedAttributes = inheritAttributes(this.el, ['tabindex', 'title']);
}

View File

@@ -132,11 +132,11 @@
<ion-label>Right</ion-label>
<ion-input class="ion-text-right" value="Narrow input"></ion-input>
</ion-item>
</ion-content>
<script>
document.querySelector('ion-input').addEventListener('ionBlur', (ev) => { console.log(ev)})
document.querySelector('ion-input').addEventListener('ionBlur', (ev) => { console.log(ev)});
function toggleBoolean(id, prop) {
var el = document.getElementById(id);

View File

@@ -13,27 +13,130 @@
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Input - Spec</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<h1>Floating Inputs</h1>
<div class="grid">
<div class="column">
<h2>Inactive</h2>
<ion-item>
<ion-label position="floating">Label</ion-label>
<ion-input placeholder="Placeholder Text"></ion-input>
</ion-item>
</div>
<div class="column">
<h2>Focused</h2>
<ion-item class="item-has-focus">
<ion-label position="floating">Label</ion-label>
<ion-input placeholder="Placeholder Text"></ion-input>
</ion-item>
</div>
<div class="column">
<h2>Activated</h2>
<ion-item>
<ion-label position="floating">Label</ion-label>
<ion-input placeholder="Placeholder Text" value="Input Text"></ion-input>
</ion-item>
</div>
<div class="column">
<h2>Hover</h2>
<ion-item class="item-hovered">
<ion-label position="floating">Label</ion-label>
<ion-input></ion-input>
</ion-item>
</div>
<div class="column">
<h2>Disabled</h2>
<ion-item>
<ion-label position="floating">Label</ion-label>
<ion-input disabled></ion-input>
</ion-item>
</div>
<div class="column">
<h2>Toggle Placeholder</h2>
<ion-item>
<ion-label position="floating">Label</ion-label>
<ion-input id="floatingToggle" type="password"></ion-input>
<ion-button fill="clear" slot="end" onClick="togglePlaceholder('#floatingToggle')" class="ion-align-self-center">
Toggle
</ion-button>
</ion-item>
</div>
</div>
<h1>Stacked Inputs</h1>
<div class="grid">
<div class="column">
<h2>Inactive</h2>
<ion-item>
<ion-label position="stacked">Label</ion-label>
<ion-input></ion-input>
</ion-item>
</div>
<div class="column">
<h2>Focused</h2>
<ion-item class="item-has-focus">
<ion-label position="stacked">Label</ion-label>
<ion-input placeholder="Placeholder Text"></ion-input>
</ion-item>
</div>
<div class="column">
<h2>Activated</h2>
<ion-item>
<ion-label position="stacked">Label</ion-label>
<ion-input placeholder="Placeholder Text" value="Input Text"></ion-input>
</ion-item>
</div>
<div class="column">
<h2>Hover</h2>
<ion-item class="item-hovered">
<ion-label position="stacked">Label</ion-label>
<ion-input></ion-input>
</ion-item>
</div>
<div class="column">
<h2>Disabled</h2>
<ion-item>
<ion-label position="stacked">Label</ion-label>
<ion-input disabled></ion-input>
</ion-item>
</div>
<div class="column">
<h2>Toggle Placeholder</h2>
<ion-item>
<ion-label position="stacked">Label</ion-label>
<ion-input id="stackedToggle" type="password"></ion-input>
<ion-button fill="clear" slot="end" onClick="togglePlaceholder('#stackedToggle')" class="ion-align-self-center">
Toggle
</ion-button>
</ion-item>
</div>
</div>
<hr>
<h2>Stacked Div</h2>
<ion-item>
<ion-label position="floating">Floating: input</ion-label>
<ion-input></ion-input>
</ion-item>
<ion-item class="item-has-focus">
<ion-label position="floating">Floating: input focused value</ion-label>
<ion-input value="value"></ion-input>
</ion-item>
<ion-item>
<ion-label position="stacked">Stacked: input</ion-label>
<ion-input></ion-input>
</ion-item>
<ion-item>
<ion-label position="stacked">Stacked: input value</ion-label>
<ion-input value="value"></ion-input>
</ion-item>
<ion-item>
<ion-label position="stacked">Stacked: div</ion-label>
<ion-label position="stacked">Label</ion-label>
<div>A div</div>
</ion-item>
<ion-item class="ion-align-items-center">
<ion-icon slot="start" name="planet"></ion-icon>
<ion-label position="stacked">Align items: center</ion-label>
@@ -68,8 +171,45 @@
</ion-content>
<style>
h1 {
font-size: 14px;
color: #54575e;
margin: 25px 0 5px 25px;
}
h2 {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 12px;
font-weight: normal;
color: #a1a7b0;
margin-top: 10px;
margin-left: 5px;
}
hr {
background: #eff1f3;
margin-top: 18px;
margin-bottom: 25px;
}
.grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
row-gap: 20px;
column-gap: 20px;
padding: 0 20px 20px;
}
ion-item {
--background: #f5f5f5;
--background: #e0e0e0;
--background-hover: #d3d3d3;
}
.custom {
@@ -84,6 +224,13 @@
color: purple !important;
}
</style>
<script>
function togglePlaceholder(id) {
const input = document.querySelector(id);
input.placeholder = input.placeholder ? undefined : 'Placeholder Text';
}
</script>
</ion-app>
</body>

View File

@@ -35,12 +35,10 @@
}
:host-context(.item-has-focus).label-floating,
:host-context(.item-has-placeholder).label-floating,
:host-context(.item-has-value).label-floating {
@include transform(translate3d(0, 0, 0), scale(.82));
}
// iOS Typography
// --------------------------------------------------

View File

@@ -39,7 +39,6 @@
}
:host-context(.item-has-focus).label-floating,
:host-context(.item-has-placeholder).label-floating,
:host-context(.item-has-value).label-floating {
@include transform(translateY(50%), scale(.75));
}

View File

@@ -131,6 +131,43 @@ async function presentLoadingWithOptions() {
### React
```tsx
/* Using with useIonLoading Hook */
import React from 'react';
import { IonButton, IonContent, IonPage, useIonLoading } from '@ionic/react';
interface LoadingProps {}
const LoadingExample: React.FC<LoadingProps> = () => {
const [present] = useIonLoading();
return (
<IonPage>
<IonContent>
<IonButton
expand="block"
onClick={() =>
present({
duration: 3000,
})
}
>
Show Loading
</IonButton>
<IonButton
expand="block"
onClick={() => present('Loading', 2000, 'dots')}
>
Show Loading using params
</IonButton>
</IonContent>
</IonPage>
);
};
```
```tsx
/* Using with IonLoading Component */
import React, { useState } from 'react';
import { IonLoading, IonButton, IonContent } from '@ionic/react';

View File

@@ -1,4 +1,41 @@
```tsx
/* Using with useIonLoading Hook */
import React from 'react';
import { IonButton, IonContent, IonPage, useIonLoading } from '@ionic/react';
interface LoadingProps {}
const LoadingExample: React.FC<LoadingProps> = () => {
const [present] = useIonLoading();
return (
<IonPage>
<IonContent>
<IonButton
expand="block"
onClick={() =>
present({
duration: 3000,
})
}
>
Show Loading
</IonButton>
<IonButton
expand="block"
onClick={() => present('Loading', 2000, 'dots')}
>
Show Loading using params
</IonButton>
</IonContent>
</IonPage>
);
};
```
```tsx
/* Using with IonLoading Component */
import React, { useState } from 'react';
import { IonLoading, IonButton, IonContent } from '@ionic/react';

View File

@@ -46,7 +46,11 @@ export class Menu implements ComponentInterface, MenuI {
@State() isEndSide = false;
/**
* The content's id the menu should use.
* The `id` of the main content. When using
* a router this is typically `ion-router-outlet`.
* When not using a router, this is typically
* your main view's `ion-content`. This is not the
* id of the `ion-content` inside of your `ion-menu`.
*/
@Prop({ reflect: true }) contentId?: string;

View File

@@ -477,15 +477,15 @@ export default defineComponent({
## Properties
| Property | Attribute | Description | Type | Default |
| -------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------ | --------------------- | ----------- |
| `contentId` | `content-id` | The content's id the menu should use. | `string \| undefined` | `undefined` |
| `disabled` | `disabled` | If `true`, the menu is disabled. | `boolean` | `false` |
| `maxEdgeStart` | `max-edge-start` | The edge threshold for dragging the menu open. If a drag/swipe happens over this value, the menu is not triggered. | `number` | `50` |
| `menuId` | `menu-id` | An id for the menu. | `string \| undefined` | `undefined` |
| `side` | `side` | Which side of the view the menu should be placed. | `"end" \| "start"` | `'start'` |
| `swipeGesture` | `swipe-gesture` | If `true`, swiping the menu is enabled. | `boolean` | `true` |
| `type` | `type` | The display type of the menu. Available options: `"overlay"`, `"reveal"`, `"push"`. | `string \| undefined` | `undefined` |
| Property | Attribute | Description | Type | Default |
| -------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | ----------- |
| `contentId` | `content-id` | The `id` of the main content. When using a router this is typically `ion-router-outlet`. When not using a router, this is typically your main view's `ion-content`. This is not the id of the `ion-content` inside of your `ion-menu`. | `string \| undefined` | `undefined` |
| `disabled` | `disabled` | If `true`, the menu is disabled. | `boolean` | `false` |
| `maxEdgeStart` | `max-edge-start` | The edge threshold for dragging the menu open. If a drag/swipe happens over this value, the menu is not triggered. | `number` | `50` |
| `menuId` | `menu-id` | An id for the menu. | `string \| undefined` | `undefined` |
| `side` | `side` | Which side of the view the menu should be placed. | `"end" \| "start"` | `'start'` |
| `swipeGesture` | `swipe-gesture` | If `true`, swiping the menu is enabled. | `boolean` | `true` |
| `type` | `type` | The display type of the menu. Available options: `"overlay"`, `"reveal"`, `"push"`. | `string \| undefined` | `undefined` |
## Events

View File

@@ -332,6 +332,70 @@ modalElement.presentingElement = await modalController.getTop(); // Get the top-
### React
```tsx
/* Using with useIonModal Hook */
import React, { useState } from 'react';
import { IonButton, IonContent, IonPage, useIonModal } from '@ionic/react';
const Body: React.FC<{
count: number;
onDismiss: () => void;
onIncrement: () => void;
}> = ({ count, onDismiss, onIncrement }) => (
<div>
count: {count}
<IonButton expand="block" onClick={() => onIncrement()}>
Increment Count
</IonButton>
<IonButton expand="block" onClick={() => onDismiss()}>
Close
</IonButton>
</div>
);
const ModalExample: React.FC = () => {
const [count, setCount] = useState(0);
const handleIncrement = () => {
setCount(count + 1);
};
const handleDismiss = () => {
dismiss();
};
/**
* First parameter is the component to show, second is the props to pass
*/
const [present, dismiss] = useIonModal(Body, {
count,
onDismiss: handleDismiss,
onIncrement: handleIncrement,
});
return (
<IonPage>
<IonContent fullscreen>
<IonButton
expand="block"
onClick={() => {
present({
cssClass: 'my-class',
});
}}
>
Show Modal
</IonButton>
<div>Count: {count}</div>
</IonContent>
</IonPage>
);
};
```
```tsx
/* Using with IonModal Component */
import React, { useState } from 'react';
import { IonModal, IonButton, IonContent } from '@ionic/react';
@@ -350,8 +414,6 @@ export const ModalExample: React.FC = () => {
};
```
> If you need a wrapper element inside of your modal component, we recommend using an `<IonPage>` 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.

View File

@@ -1,4 +1,68 @@
```tsx
/* Using with useIonModal Hook */
import React, { useState } from 'react';
import { IonButton, IonContent, IonPage, useIonModal } from '@ionic/react';
const Body: React.FC<{
count: number;
onDismiss: () => void;
onIncrement: () => void;
}> = ({ count, onDismiss, onIncrement }) => (
<div>
count: {count}
<IonButton expand="block" onClick={() => onIncrement()}>
Increment Count
</IonButton>
<IonButton expand="block" onClick={() => onDismiss()}>
Close
</IonButton>
</div>
);
const ModalExample: React.FC = () => {
const [count, setCount] = useState(0);
const handleIncrement = () => {
setCount(count + 1);
};
const handleDismiss = () => {
dismiss();
};
/**
* First parameter is the component to show, second is the props to pass
*/
const [present, dismiss] = useIonModal(Body, {
count,
onDismiss: handleDismiss,
onIncrement: handleIncrement,
});
return (
<IonPage>
<IonContent fullscreen>
<IonButton
expand="block"
onClick={() => {
present({
cssClass: 'my-class',
});
}}
>
Show Modal
</IonButton>
<div>Count: {count}</div>
</IonContent>
</IonPage>
);
};
```
```tsx
/* Using with IonModal Component */
import React, { useState } from 'react';
import { IonModal, IonButton, IonContent } from '@ionic/react';
@@ -17,8 +81,6 @@ export const ModalExample: React.FC = () => {
};
```
> If you need a wrapper element inside of your modal component, we recommend using an `<IonPage>` 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.

View File

@@ -7,6 +7,95 @@ A Picker is a dialog that displays a row of buttons and columns underneath. It a
<!-- Auto Generated Below -->
## Usage
### React
```tsx
/* Using with useIonPicker Hook */
import React, { useState } from 'react';
import { IonButton, IonContent, IonPage, useIonPicker } from '@ionic/react';
const PickerExample: React.FC = () => {
const [present] = useIonPicker();
const [value, setValue] = useState('');
return (
<IonPage>
<IonContent>
<IonButton
expand="block"
onClick={() =>
present({
buttons: [
{
text: 'Confirm',
handler: (selected) => {
setValue(selected.animal.value)
},
},
],
columns: [
{
name: 'animal',
options: [
{ text: 'Dog', value: 'dog' },
{ text: 'Cat', value: 'cat' },
{ text: 'Bird', value: 'bird' },
],
},
],
})
}
>
Show Picker
</IonButton>
<IonButton
expand="block"
onClick={() =>
present(
[
{
name: 'animal',
options: [
{ text: 'Dog', value: 'dog' },
{ text: 'Cat', value: 'cat' },
{ text: 'Bird', value: 'bird' },
],
},
{
name: 'vehicle',
options: [
{ text: 'Car', value: 'car' },
{ text: 'Truck', value: 'truck' },
{ text: 'Bike', value: 'bike' },
],
},
],
[
{
text: 'Confirm',
handler: (selected) => {
setValue(`${selected.animal.value}, ${selected.vehicle.value}`)
},
},
]
)
}
>
Show Picker using params
</IonButton>
{value && (
<div>Selected Value: {value}</div>
)}
</IonContent>
</IonPage>
);
};
```
## Properties
| Property | Attribute | Description | Type | Default |

View File

@@ -0,0 +1,82 @@
```tsx
/* Using with useIonPicker Hook */
import React, { useState } from 'react';
import { IonButton, IonContent, IonPage, useIonPicker } from '@ionic/react';
const PickerExample: React.FC = () => {
const [present] = useIonPicker();
const [value, setValue] = useState('');
return (
<IonPage>
<IonContent>
<IonButton
expand="block"
onClick={() =>
present({
buttons: [
{
text: 'Confirm',
handler: (selected) => {
setValue(selected.animal.value)
},
},
],
columns: [
{
name: 'animal',
options: [
{ text: 'Dog', value: 'dog' },
{ text: 'Cat', value: 'cat' },
{ text: 'Bird', value: 'bird' },
],
},
],
})
}
>
Show Picker
</IonButton>
<IonButton
expand="block"
onClick={() =>
present(
[
{
name: 'animal',
options: [
{ text: 'Dog', value: 'dog' },
{ text: 'Cat', value: 'cat' },
{ text: 'Bird', value: 'bird' },
],
},
{
name: 'vehicle',
options: [
{ text: 'Car', value: 'car' },
{ text: 'Truck', value: 'truck' },
{ text: 'Bike', value: 'bike' },
],
},
],
[
{
text: 'Confirm',
handler: (selected) => {
setValue(`${selected.animal.value}, ${selected.vehicle.value}`)
},
},
]
)
}
>
Show Picker using params
</IonButton>
{value && (
<div>Selected Value: {value}</div>
)}
</IonContent>
</IonPage>
);
};
```

View File

@@ -114,6 +114,59 @@ function presentPopover(ev) {
### React
```tsx
/* Using with useIonPopover Hook */
import React from 'react';
import {
IonButton,
IonContent,
IonItem,
IonList,
IonListHeader,
IonPage,
useIonPopover,
} from '@ionic/react';
const PopoverList: React.FC<{
onHide: () => void;
}> = ({ onHide }) => (
<IonList>
<IonListHeader>Ionic</IonListHeader>
<IonItem button>Learn Ionic</IonItem>
<IonItem button>Documentation</IonItem>
<IonItem button>Showcase</IonItem>
<IonItem button>GitHub Repo</IonItem>
<IonItem lines="none" detail={false} button onClick={onHide}>
Close
</IonItem>
</IonList>
);
const PopoverExample: React.FC = () => {
const [present, dismiss] = useIonPopover(PopoverList, { onHide: () => dismiss() });
return (
<IonPage>
<IonContent>
<IonButton
expand="block"
onClick={(e) =>
present({
event: e.nativeEvent,
})
}
>
Show Popover
</IonButton>
</IonContent>
</IonPage>
);
};
```
```tsx
/* Using with IonPopover Component */
import React, { useState } from 'react';
import { IonPopover, IonButton } from '@ionic/react';

View File

@@ -1,4 +1,57 @@
```tsx
/* Using with useIonPopover Hook */
import React from 'react';
import {
IonButton,
IonContent,
IonItem,
IonList,
IonListHeader,
IonPage,
useIonPopover,
} from '@ionic/react';
const PopoverList: React.FC<{
onHide: () => void;
}> = ({ onHide }) => (
<IonList>
<IonListHeader>Ionic</IonListHeader>
<IonItem button>Learn Ionic</IonItem>
<IonItem button>Documentation</IonItem>
<IonItem button>Showcase</IonItem>
<IonItem button>GitHub Repo</IonItem>
<IonItem lines="none" detail={false} button onClick={onHide}>
Close
</IonItem>
</IonList>
);
const PopoverExample: React.FC = () => {
const [present, dismiss] = useIonPopover(PopoverList, { onHide: () => dismiss() });
return (
<IonPage>
<IonContent>
<IonButton
expand="block"
onClick={(e) =>
present({
event: e.nativeEvent,
})
}
>
Show Popover
</IonButton>
</IonContent>
</IonPage>
);
};
```
```tsx
/* Using with IonPopover Component */
import React, { useState } from 'react';
import { IonPopover, IonButton } from '@ionic/react';

View File

@@ -1,18 +1,19 @@
@import "../../themes/ionic.globals";
// Progress bar
// --------------------------------------------------
// Host has no background by default - this will be added to the progress-buffer-bar
// ------------------------------------------------------------------------
// Host has no background by default - this will be added to the progress-buffer-bar
:host {
/**
* @prop --background: Same as --buffer-background when using a determinate progress bar, otherwise it styles the background of the ion-progress-bar itself.
* @prop --progress-background: Color of the progress bar
* @prop --buffer-background: Color of the buffer bar
*/
* @prop --background: Background of the progress track, or the buffer bar if `buffer` is set
* @prop --progress-background: Background of the progress bar representing the current value
* @prop --buffer-background: DEPRECATED, use `--background` instead
*/
--background: #{ion-color(primary, base, 0.3)};
--progress-background: #{ion-color(primary, base)};
--buffer-background: var(--background);
display: block;
position: relative;
@@ -25,16 +26,6 @@
overflow: hidden;
}
:host(.ion-color) {
--progress-background: #{current-color(base)};
--buffer-background: #{current-color(base, 0.3)};
}
// indeterminate has no progress-buffer-bar, so it will be added to the host
:host(.progress-bar-indeterminate) {
background: var(--buffer-background);
}
.progress,
.progress-indeterminate,
.indeterminate-bar-primary,
@@ -128,27 +119,33 @@
}
}
// Buffer style
// --------------------------------------------------
// Progress Bar: Buffer Circles
// ------------------------------------------------------------------------
.buffer-circles {
background: radial-gradient(ellipse at center, var(--buffer-background) 0%, var(--buffer-background) 30%, transparent 30%) repeat-x 5px center;
background-image: radial-gradient(ellipse at center, var(--buffer-background) 0%, var(--buffer-background) 30%, transparent 30%);
/* stylelint-disable property-blacklist */
background-repeat: repeat-x;
background-position: 5px center;
background-size: 10px 10px;
/* stylelint-enable property-blacklist */
z-index: 0;
animation: buffering 450ms infinite linear;
}
// Progress Bar: Reversed
// ------------------------------------------------------------------------
// If reversed is set to true, the animation will be reversed
// and the bars starting at the top right
// --------------------------------------------------
// and the bar will start at the top right
:host(.progress-bar-reversed) {
transform: scaleX(-1);
}
// Progress paused
// --------------------------------------------------
// Progress Bar: Paused
// ------------------------------------------------------------------------
:host(.progress-paused) {
.indeterminate-bar-secondary,
@@ -158,8 +155,27 @@
}
}
// Animation Keyframes
// --------------------------------------------------
// Progress Bar: Color
// ------------------------------------------------------------------------
:host(.ion-color) .progress-buffer-bar {
background: #{current-color(base, 0.3)};
}
:host(.ion-color) .buffer-circles {
background-image: radial-gradient(ellipse at center, #{current-color(base, 0.3)} 0%, #{current-color(base, 0.3)} 30%, transparent 30%);
}
:host(.ion-color) {
.progress,
.progress-indeterminate {
background: #{current-color(base)};
}
}
// Progress Bar: Animation Keyframes
// ------------------------------------------------------------------------
// Source: https://github.com/material-components/material-components-web/blob/master/packages/mdc-linear-progress/_keyframes.scss
@keyframes primary-indeterminate-translate {
@@ -259,3 +275,4 @@
transform: translateX(-10px);
}
}

View File

@@ -8,6 +8,11 @@ import { createColorClasses } from '../../utils/theme';
/**
* @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use.
*
* @part progress - The progress bar that shows the current value when `type` is `"determinate"` and slides back and forth when `type` is `"indeterminate"`.
* @part stream - The animated circles that appear while buffering. This only shows when `buffer` is set and `type` is `"determinate"`.
* @part track - The track bar behind the progress bar. If the `buffer` property is set and `type` is `"determinate"` the track will be the
* width of the `buffer` value.
*/
@Component({
tag: 'ion-progress-bar',
@@ -77,10 +82,12 @@ export class ProgressBar implements ComponentInterface {
}
const renderIndeterminate = () => {
return [
<div class="indeterminate-bar-primary"><span class="progress-indeterminate"></span></div>,
<div class="indeterminate-bar-secondary"><span class="progress-indeterminate"></span></div>
];
return (
<div part="track" class="progress-buffer-bar">
<div class="indeterminate-bar-primary"><span part="progress" class="progress-indeterminate"></span></div>
<div class="indeterminate-bar-secondary"><span part="progress" class="progress-indeterminate"></span></div>
</div>
);
};
const renderProgress = (value: number, buffer: number) => {
@@ -88,7 +95,7 @@ const renderProgress = (value: number, buffer: number) => {
const finalBuffer = clamp(0, buffer, 1);
return [
<div class="progress" style={{ transform: `scaleX(${finalValue})` }}></div>,
<div part="progress" class="progress" style={{ transform: `scaleX(${finalValue})` }}></div>,
/**
* Buffer circles with two container to move
* the circles behind the buffer progress
@@ -98,9 +105,9 @@ const renderProgress = (value: number, buffer: number) => {
*/
<div class={{ 'buffer-circles-container': true, 'ion-hide': finalBuffer === 1 }} style={{ transform: `translateX(${finalBuffer * 100}%)` }}>
<div class="buffer-circles-container" style={{ transform: `translateX(-${finalBuffer * 100}%)` }}>
<div class="buffer-circles"></div>
<div part="stream" class="buffer-circles"></div>
</div>
</div>,
<div class="progress-buffer-bar" style={{ transform: `scaleX(${finalBuffer})` }}></div>,
<div part="track" class="progress-buffer-bar" style={{ transform: `scaleX(${finalBuffer})` }}></div>,
];
};

View File

@@ -1,20 +1,18 @@
# ion-progress-bar
ion-progress-bar is a horizontal progress bar to visualize the progression of an operation and activity. You can choose between two types: `determinate` and `indeterminate`.
Progress bars inform users about the status of ongoing processes, such as loading an app, submitting a form, or saving updates. There are two types of progress bars: `determinate` and `indeterminate`.
## Progress Type
### Determinate
If the percentage of an operation is known, you should use the determinate type. This is the default type and the progress is represented by the `value` property.
Determinate is the default type. It should be used when the percentage of an operation is known. The progress is represented by setting the `value` property. This can be used to show the progress increasing from 0 to 100% of the track.
A buffer shows circles as animation to indicate some activity. If the `buffer` property is smaller than 1 you can show the additional buffering progress.
If the `buffer` property is set, a buffer stream will show with animated circles to indicate activity. The value of the `buffer` property will also be represented by how much visible track there is. If the value of `buffer` is less than the `value` property, there will be no visible track. If `buffer` is equal to `1` then the buffer stream will be hidden.
### Indeterminate
If an operation is in progress and it's not necessary to indicate how long it will take.
If you add `reversed="true"`, you receive a query which is used to indicate pre-loading.
The indeterminate type should be used when it is unknown how long the process will take. The progress bar is not tied to the `value`, instead it continually slides along the track until the process is complete.
<!-- Auto Generated Below -->
@@ -144,13 +142,22 @@ export default defineComponent({
| `value` | `value` | The value determines how much of the active bar should display when the `type` is `"determinate"`. The value should be between [0, 1]. | `number` | `0` |
## Shadow Parts
| Part | Description |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `"progress"` | The progress bar that shows the current value when `type` is `"determinate"` and slides back and forth when `type` is `"indeterminate"`. |
| `"stream"` | The animated circles that appear while buffering. This only shows when `buffer` is set and `type` is `"determinate"`. |
| `"track"` | The track bar behind the progress bar. If the `buffer` property is set and `type` is `"determinate"` the track will be the width of the `buffer` value. |
## CSS Custom Properties
| Name | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `--background` | Same as --buffer-background when using a determinate progress bar, otherwise it styles the background of the ion-progress-bar itself. |
| `--buffer-background` | Color of the buffer bar |
| `--progress-background` | Color of the progress bar |
| Name | Description |
| ----------------------- | ---------------------------------------------------------------------- |
| `--background` | Background of the progress track, or the buffer bar if `buffer` is set |
| `--buffer-background` | DEPRECATED, use `--background` instead |
| `--progress-background` | Background of the progress bar representing the current value |
----------------------------------------------

View File

@@ -0,0 +1,10 @@
import { newE2EPage } from '@stencil/core/testing';
test('progress-bar: standalone', async () => {
const page = await newE2EPage({
url: '/src/components/progress-bar/test/standalone?ionic:_testing=true'
});
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();
});

View File

@@ -12,19 +12,171 @@
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script></head>
<body>
<h1>Default Progress Bar</h1>
<h1>Default Progress Bars</h1>
<ion-progress-bar></ion-progress-bar>
<h1>Default Progress Bar with 50% width</h1>
<ion-progress-bar value="0.5"></ion-progress-bar>
<h1>Colorize Progress Bar</h1>
<ion-progress-bar color="primary" value="0.5"></ion-progress-bar>
<ion-progress-bar color="secondary" value="0.5"></ion-progress-bar>
<h1>Other types</h1>
<ion-progress-bar type="indeterminate"></ion-progress-bar>
<hr>
<h1>Progress Bar: Colors</h1>
<ion-progress-bar color="primary" value="0.5"></ion-progress-bar>
<ion-progress-bar color="secondary" value="0.5"></ion-progress-bar>
<ion-progress-bar color="tertiary" value="0.5"></ion-progress-bar>
<ion-progress-bar color="success" value="0.5"></ion-progress-bar>
<ion-progress-bar color="warning" value="0.5"></ion-progress-bar>
<ion-progress-bar color="danger" value="0.5"></ion-progress-bar>
<ion-progress-bar color="dark" value="0.5"></ion-progress-bar>
<ion-progress-bar color="light" value="0.5"></ion-progress-bar>
<hr>
<h1>Default Types</h1>
<ion-progress-bar></ion-progress-bar>
<ion-progress-bar value="0.5"></ion-progress-bar>
<ion-progress-bar value="0.20" buffer="0.4"></ion-progress-bar>
<ion-progress-bar color="warning" reversed="true" value="0.20" buffer="0.4"></ion-progress-bar>
<ion-progress-bar buffer="0"></ion-progress-bar>
<ion-progress-bar color="danger" type="indeterminate"></ion-progress-bar>
<ion-progress-bar type="indeterminate" reversed="true"></ion-progress-bar>
<hr>
<h1>Custom: colors by part</h1>
<ion-progress-bar class="custom-color-parts"></ion-progress-bar>
<ion-progress-bar class="custom-color-parts" buffer="0.9"></ion-progress-bar>
<ion-progress-bar class="custom-color-parts" value="0.5"></ion-progress-bar>
<ion-progress-bar class="custom-color-parts" value="0.20" buffer="0.4"></ion-progress-bar>
<ion-progress-bar class="custom-color-parts" color="warning" reversed="true" value="0.20" buffer="0.4"></ion-progress-bar>
<ion-progress-bar class="custom-color-parts" buffer="0"></ion-progress-bar>
<ion-progress-bar class="custom-color-parts" color="danger" type="indeterminate"></ion-progress-bar>
<ion-progress-bar class="custom-color-parts" type="indeterminate" reversed="true"></ion-progress-bar>
<hr>
<h1>Custom: colors by css variable</h1>
<ion-progress-bar class="custom-color-variables"></ion-progress-bar>
<ion-progress-bar class="custom-color-variables" value="0.5"></ion-progress-bar>
<ion-progress-bar class="custom-color-variables" value="0.20" buffer="0.4"></ion-progress-bar>
<ion-progress-bar class="custom-color-variables" color="warning" reversed="true" value="0.20" buffer="0.4"></ion-progress-bar>
<ion-progress-bar class="custom-color-variables" buffer="0"></ion-progress-bar>
<ion-progress-bar class="custom-color-variables" color="danger" type="indeterminate"></ion-progress-bar>
<ion-progress-bar class="custom-color-variables" type="indeterminate" reversed="true"></ion-progress-bar>
<hr>
<h1>Custom border radius</h1>
<ion-progress-bar class="custom-border-radius"></ion-progress-bar>
<ion-progress-bar class="custom-border-radius" value="0.5"></ion-progress-bar>
<ion-progress-bar class="custom-border-radius" type="indeterminate"></ion-progress-bar>
<h1>Custom transition</h1>
<ion-progress-bar class="random-value" max="100"></ion-progress-bar>
<ion-progress-bar class="random-value custom-transition" max="100"></ion-progress-bar>
</body>
<script>
let randomValues = document.querySelectorAll('.random-value');
setInterval(() => {
let value = Math.random();
for (let i = 0; i < randomValues.length; i++) {
randomValues[i].value = value;
}
}, 100);
</script>
<style>
h1 {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 12px;
font-weight: normal;
color: #a1a7b0;
margin-top: 10px;
margin-left: 5px;
}
hr {
background: #eff1f3;
border: none;
height: 1px;
margin-top: 18px;
margin-bottom: 25px;
}
ion-progress-bar {
margin-bottom: 10px;
}
/*
* Custom progress bar color using parts
* ------------------------------------------------------
* Note: in these examples setting the background on
* each element should override the color prop
*/
/* determinate buffer / track and indeterminate track */
.custom-color-parts::part(track) {
background:rgb(158, 157, 36, 0.2);
}
/* determinate and indeterminate progress background */
.custom-color-parts::part(progress) {
background: #9e9d24;
}
/* buffer stream (animated circles) */
.custom-color-parts::part(stream) {
background-image: radial-gradient(ellipse at center, rgb(158, 157, 36, 0.2) 0%, rgb(158, 157, 36, 0.2) 30%, transparent 30%);
}
/*
* Custom progress bar color using css variables
* ------------------------------------------------------
* Note: in this example setting the background via
* CSS variables should NOT override the color prop
*/
.custom-color-variables {
--background: rgb(158, 157, 36, 0.2);
--progress-background: #9e9d24;
}
/*
* Custom progress bar border radius using parts
* ------------------------------------------------------
*/
.custom-border-radius {
border-radius: 10px;
height: 20px;
}
.custom-border-radius::part(progress) {
border-radius: 0 50% 50% 0;
}
.custom-border-radius[type="indeterminate"]::part(progress) {
border-radius: 8px;
}
/*
* Custom transition for fast value changes
* ------------------------------------------------------
* The first progress bar in the example has the default
* transition, while the second has none. This is
* apparent because they use the same values.
*/
.custom-transition::part(progress) {
transition: none;
}
</style>
</html>

View File

@@ -187,11 +187,11 @@ export default defineComponent({
## Properties
| Property | Attribute | Description | Type | Default |
| ----------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | ------------- |
| `contentId` | `content-id` | The content `id` of the split-pane's main content. | `string \| undefined` | `undefined` |
| `disabled` | `disabled` | If `true`, the split pane will be hidden. | `boolean` | `false` |
| `when` | `when` | When the split-pane should be shown. Can be a CSS media query expression, or a shortcut expression. Can also be a boolean expression. | `boolean \| string` | `QUERY['lg']` |
| Property | Attribute | Description | Type | Default |
| ----------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | ------------- |
| `contentId` | `content-id` | The `id` of the main content. When using a router this is typically `ion-router-outlet`. When not using a router, this is typically your main view's `ion-content`. This is not the id of the `ion-content` inside of your `ion-menu`. | `string \| undefined` | `undefined` |
| `disabled` | `disabled` | If `true`, the split pane will be hidden. | `boolean` | `false` |
| `when` | `when` | When the split-pane should be shown. Can be a CSS media query expression, or a shortcut expression. Can also be a boolean expression. | `boolean \| string` | `QUERY['lg']` |
## Events

View File

@@ -29,7 +29,11 @@ export class SplitPane implements ComponentInterface {
@State() visible = false;
/**
* The content `id` of the split-pane's main content.
* The `id` of the main content. When using
* a router this is typically `ion-router-outlet`.
* When not using a router, this is typically
* your main view's `ion-content`. This is not the
* id of the `ion-content` inside of your `ion-menu`.
*/
@Prop({ reflect: true }) contentId?: string;

View File

@@ -111,6 +111,48 @@ async function presentToastWithOptions() {
### React
```tsx
/* Using the useIonToast Hook */
import React from 'react';
import { IonButton, IonContent, IonPage, useIonToast } from '@ionic/react';
const ToastExample: React.FC = () => {
const [present, dismiss] = useIonToast();
return (
<IonPage>
<IonContent>
<IonButton
expand="block"
onClick={() =>
present({
buttons: [{ text: 'hide', handler: () => dismiss() }],
message: 'toast from hook, click hide to dismiss',
onDidDismiss: () => console.log('dismissed'),
onWillDismiss: () => console.log('will dismiss'),
})
}
>
Show Toast
</IonButton>
<IonButton
expand="block"
onClick={() => present('hello from hook', 3000)}
>
Show Toast using params, closes in 3 secs
</IonButton>
<IonButton expand="block" onClick={dismiss}>
Hide Toast
</IonButton>
</IonContent>
</IonPage>
);
};
```
```tsx
/* Using the IonToast Component */
import React, { useState } from 'react';
import { IonToast, IonContent, IonButton } from '@ionic/react';

View File

@@ -1,4 +1,46 @@
```tsx
/* Using the useIonToast Hook */
import React from 'react';
import { IonButton, IonContent, IonPage, useIonToast } from '@ionic/react';
const ToastExample: React.FC = () => {
const [present, dismiss] = useIonToast();
return (
<IonPage>
<IonContent>
<IonButton
expand="block"
onClick={() =>
present({
buttons: [{ text: 'hide', handler: () => dismiss() }],
message: 'toast from hook, click hide to dismiss',
onDidDismiss: () => console.log('dismissed'),
onWillDismiss: () => console.log('will dismiss'),
})
}
>
Show Toast
</IonButton>
<IonButton
expand="block"
onClick={() => present('hello from hook', 3000)}
>
Show Toast using params, closes in 3 secs
</IonButton>
<IonButton expand="block" onClick={dismiss}>
Hide Toast
</IonButton>
</IonContent>
</IonPage>
);
};
```
```tsx
/* Using the IonToast Component */
import React, { useState } from 'react';
import { IonToast, IonContent, IonButton } from '@ionic/react';