mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f544fb44b0 | ||
|
|
6850853908 | ||
|
|
c6bd7be763 | ||
|
|
b33d940513 |
112
.github/ISSUE_TEMPLATE/bug_report.md
vendored
112
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,55 +1,57 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Create a report to help us improve
|
||||
|
||||
---
|
||||
|
||||
<!-- Before submitting an issue, please consult our docs (https://beta.ionicframework.com/docs/) and API reference (https://beta.ionicframework.com/docs/api/) -->
|
||||
|
||||
<!-- Please make sure you are posting an issue pertaining to the Ionic Framework. If you are having an issue with the Ionic Appflow services (Ionic View, Ionic Deploy, etc.) please consult the Ionic Appflow support portal (https://ionic.zendesk.com/hc/en-us) -->
|
||||
|
||||
<!-- Please do not submit support requests or "How to" questions here. Instead, please use one of these channels: https://forum.ionicframework.com/ or http://ionicworldwide.herokuapp.com/ -->
|
||||
|
||||
<!-- ISSUES MISSING IMPORTANT INFORMATION MAY BE CLOSED WITHOUT INVESTIGATION. -->
|
||||
|
||||
# Bug Report
|
||||
|
||||
**Ionic version:**
|
||||
<!-- (For Ionic 1.x issues, please use https://github.com/ionic-team/ionic-v1) -->
|
||||
<!-- (For Ionic 2.x & 3.x issues, please use https://github.com/ionic-team/ionic-v3) -->
|
||||
[x] **4.x**
|
||||
|
||||
**Current behavior:**
|
||||
<!-- Describe how the bug manifests. -->
|
||||
|
||||
**Expected behavior:**
|
||||
<!-- Describe what the behavior would be without the bug. -->
|
||||
|
||||
**Steps to reproduce:**
|
||||
<!-- Please explain the steps required to duplicate the issue, especially if you are able to provide a sample application. -->
|
||||
|
||||
**Related code:**
|
||||
|
||||
<!-- If you are able to illustrate the bug or feature request with an example, please provide a sample application via one of the following means:
|
||||
|
||||
A sample application via GitHub
|
||||
|
||||
StackBlitz (https://stackblitz.com)
|
||||
|
||||
Plunker (http://plnkr.co/edit/cpeRJs?p=preview)
|
||||
|
||||
-->
|
||||
|
||||
```
|
||||
insert short code snippets here
|
||||
```
|
||||
|
||||
**Other information:**
|
||||
<!-- List any other information that is relevant to your issue. Stack traces, related issues, suggestions on how to fix, Stack Overflow links, forum links, etc. -->
|
||||
|
||||
**Ionic info:**
|
||||
<!-- (run `ionic info` from a terminal/cmd prompt and paste output below): -->
|
||||
|
||||
```
|
||||
insert the output from ionic info here
|
||||
```
|
||||
---
|
||||
name: Bug Report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
<!-- Before submitting an issue, please consult our docs (https://ionicframework.com/docs/). -->
|
||||
|
||||
<!-- Please make sure you are posting an issue pertaining to the Ionic Framework. If you are having an issue with the Ionic Appflow services (Ionic View, Ionic Deploy, etc.) please consult the Ionic Appflow support portal (https://ionic.zendesk.com/hc/en-us) -->
|
||||
|
||||
<!-- Please do not submit support requests or "How to" questions here. Instead, please use one of these channels: https://forum.ionicframework.com/ or http://ionicworldwide.herokuapp.com/ -->
|
||||
|
||||
<!-- ISSUES MISSING IMPORTANT INFORMATION MAY BE CLOSED WITHOUT INVESTIGATION. -->
|
||||
|
||||
# Bug Report
|
||||
|
||||
**Ionic version:**
|
||||
<!-- (For Ionic 1.x issues, please use https://github.com/ionic-team/ionic-v1) -->
|
||||
<!-- (For Ionic 2.x & 3.x issues, please use https://github.com/ionic-team/ionic-v3) -->
|
||||
[x] **4.x**
|
||||
|
||||
**Current behavior:**
|
||||
<!-- Describe how the bug manifests. -->
|
||||
|
||||
**Expected behavior:**
|
||||
<!-- Describe what the behavior would be without the bug. -->
|
||||
|
||||
**Steps to reproduce:**
|
||||
<!-- Please explain the steps required to duplicate the issue, especially if you are able to provide a sample application. -->
|
||||
|
||||
**Related code:**
|
||||
|
||||
<!-- If you are able to illustrate the bug or feature request with an example, please provide a sample application via one of the following means:
|
||||
|
||||
A sample application via GitHub
|
||||
|
||||
StackBlitz (https://stackblitz.com)
|
||||
|
||||
Plunker (http://plnkr.co/edit/cpeRJs?p=preview)
|
||||
|
||||
-->
|
||||
|
||||
```
|
||||
insert short code snippets here
|
||||
```
|
||||
|
||||
**Other information:**
|
||||
<!-- List any other information that is relevant to your issue. Stack traces, related issues, suggestions on how to fix, Stack Overflow links, forum links, etc. -->
|
||||
|
||||
**Ionic info:**
|
||||
<!-- (run `ionic info` from a terminal/cmd prompt and paste output below): -->
|
||||
|
||||
```
|
||||
insert the output from ionic info here
|
||||
```
|
||||
|
||||
11
.github/ISSUE_TEMPLATE/cli.md
vendored
Normal file
11
.github/ISSUE_TEMPLATE/cli.md
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
name: CLI
|
||||
about: Suggest an improvement for the CLI
|
||||
title: ''
|
||||
labels: 'ionitron: cli'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
# CLI
|
||||
|
||||
Please do not submit bug reports or feature requests related to the Ionic CLI. Instead, please submit an issue to the [Ionic CLI Repository](https://github.com/ionic-team/ionic-cli/issues/new/choose).
|
||||
11
.github/ISSUE_TEMPLATE/documentation.md
vendored
Normal file
11
.github/ISSUE_TEMPLATE/documentation.md
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
name: Documentation
|
||||
about: Suggest an improvement for the documentation of this project
|
||||
title: ''
|
||||
labels: 'ionitron: docs'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
# Documentation
|
||||
|
||||
Please do not submit issues on how to improve or fix the documentation. Instead, please submit an issue to the [Ionic Docs Repository](https://github.com/ionic-team/ionic-docs/issues/new/choose).
|
||||
74
.github/ISSUE_TEMPLATE/feature_request.md
vendored
74
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,37 +1,37 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest an idea for this project
|
||||
|
||||
---
|
||||
|
||||
<!-- Please make sure you are posting an feature request pertaining to the Ionic Framework. -->
|
||||
|
||||
<!-- Before submitting an issue, please consult our docs (https://beta.ionicframework.com/docs/) and API reference (https://beta.ionicframework.com/docs/api/) -->
|
||||
|
||||
<!-- Please make sure you are posting an issue pertaining to the Ionic Framework. If you are having an issue with the Ionic Appflow services (Ionic View, Ionic Deploy, etc.) please consult the Ionic Appflow support portal (https://ionic.zendesk.com/hc/en-us) -->
|
||||
|
||||
<!-- Please do not submit support requests or "How to" questions here. Instead, please use one of these channels: https://forum.ionicframework.com/ or http://ionicworldwide.herokuapp.com/ -->
|
||||
|
||||
<!-- ISSUES MISSING IMPORTANT INFORMATION MAY BE CLOSED WITHOUT INVESTIGATION. -->
|
||||
|
||||
# Feature Request
|
||||
|
||||
**Ionic version:**
|
||||
<!-- (For Ionic 1.x issues, please use https://github.com/ionic-team/ionic-v1) -->
|
||||
<!-- (For Ionic 2.x & 3.x issues, please use https://github.com/ionic-team/ionic-v3) -->
|
||||
[x] **4.x**
|
||||
|
||||
**Describe the Feature Request**
|
||||
<!-- A clear and concise description of what the feature request is. Please include if your feature request is related to a problem. -->
|
||||
|
||||
**Describe Preferred Solution**
|
||||
<!-- A clear and concise description of what you want to happen. -->
|
||||
|
||||
**Describe Alternatives**
|
||||
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||
|
||||
**Related Code**
|
||||
<!-- If you are able to illustrate the feature request with an example, please provide a sample application via an online code collaborator such as [StackBlitz](https://stackblitz.com), or [GitHub](https://github.com). -->
|
||||
|
||||
**Additional Context**
|
||||
<!-- List any other information that is relevant to your issue. Stack traces, related issues, suggestions on how to add, use case, Stack Overflow links, forum links, screenshots, OS if applicable, etc. -->
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
<!-- Before submitting an issue, please consult our docs (https://ionicframework.com/docs/). -->
|
||||
|
||||
<!-- Please make sure you are posting an issue pertaining to the Ionic Framework. If you are having an issue with the Ionic Appflow services (Ionic View, Ionic Deploy, etc.) please consult the Ionic Appflow support portal (https://ionic.zendesk.com/hc/en-us) -->
|
||||
|
||||
<!-- Please do not submit support requests or "How to" questions here. Instead, please use one of these channels: https://forum.ionicframework.com/ or http://ionicworldwide.herokuapp.com/ -->
|
||||
|
||||
<!-- ISSUES MISSING IMPORTANT INFORMATION MAY BE CLOSED WITHOUT INVESTIGATION. -->
|
||||
|
||||
# Feature Request
|
||||
|
||||
**Ionic version:**
|
||||
<!-- (For Ionic 1.x issues, please use https://github.com/ionic-team/ionic-v1) -->
|
||||
<!-- (For Ionic 2.x & 3.x issues, please use https://github.com/ionic-team/ionic-v3) -->
|
||||
[x] **4.x**
|
||||
|
||||
**Describe the Feature Request**
|
||||
<!-- A clear and concise description of what the feature request is. Please include if your feature request is related to a problem. -->
|
||||
|
||||
**Describe Preferred Solution**
|
||||
<!-- A clear and concise description of what you want to happen. -->
|
||||
|
||||
**Describe Alternatives**
|
||||
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||
|
||||
**Related Code**
|
||||
<!-- If you are able to illustrate the feature request with an example, please provide a sample application via an online code collaborator such as [StackBlitz](https://stackblitz.com), or [GitHub](https://github.com). -->
|
||||
|
||||
**Additional Context**
|
||||
<!-- List any other information that is relevant to your issue. Stack traces, related issues, suggestions on how to add, use case, Stack Overflow links, forum links, screenshots, OS if applicable, etc. -->
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
---
|
||||
name: Support Question
|
||||
about: Question on how to use this project
|
||||
|
||||
---
|
||||
|
||||
# Support Question
|
||||
|
||||
Please do not submit support requests or "How to" questions here. Instead, please use one of these channels: https://forum.ionicframework.com/ or http://ionicworldwide.herokuapp.com/
|
||||
---
|
||||
name: Support Question
|
||||
about: Question on how to use this project
|
||||
title: ''
|
||||
labels: 'ionitron: support'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
# Support Question
|
||||
|
||||
Please do not submit support requests or "How to" questions here. Instead, please use one of these channels: https://forum.ionicframework.com/ or http://ionicworldwide.herokuapp.com/
|
||||
17
.github/PROCESS.md
vendored
17
.github/PROCESS.md
vendored
@@ -35,26 +35,27 @@ Issues and pull requests that need review. Pull requests will automatically move
|
||||
|
||||
Issues and pull requests that are completed. Issues will automatically move here when they are closed. Pull requests will automatically moved here when they are merged or closed with unmerged commits.
|
||||
|
||||
|
||||
## Managing Issues
|
||||
|
||||
### Issues to Triage
|
||||
|
||||
The issues that need to be triaged all have the `triage` label. In many cases the issue can be automatically processed by the Ionic Issue Bot by applying a specific label.
|
||||
|
||||
Once another label is applied to the issue, the `triage` label is automatically be removed by the bot.
|
||||
Once another label is applied to the issue, the `triage` label is automatically removed by the bot.
|
||||
|
||||
### Wrong Repo
|
||||
### Wrong Repository
|
||||
|
||||
If an issue does not pertain to the Ionic Framework but does pertain to another repo, it should be moved to that repo. The bot has been set up to automatically create the issue in other repositories while closing and locking the issue in this repository. Use one of the following labels to perform that action:
|
||||
If an issue does not pertain to the Ionic Framework but does pertain to another repository, it should be moved to that repository. The bot has been set up to automatically create the issue in other repositories while closing and locking the issue in this repository. Use one of the following labels to perform that action:
|
||||
|
||||
- ionitron: cli
|
||||
- ionitron: docs
|
||||
- ionitron: stencil
|
||||
- ionitron: native
|
||||
|
||||
### Ionic Pro Issues
|
||||
### Ionic Appflow Issues
|
||||
|
||||
If the issue is associated with Ionic Pro the submitter should be told to use the [Ionic Pro Support Forum](https://ionic.zendesk.com/hc/en-us/requests/new). The issue should be closed and locked. Use the `ionitron: ionic pro` label to accomplish this.
|
||||
If the issue is associated with Ionic Appflow the submitter should be told to use the [Ionic Appflow Support Forum](https://ionic.zendesk.com/hc/en-us/requests/new). The issue should be closed and locked. Use the `ionitron: ionic appflow` label to accomplish this.
|
||||
|
||||
### Support Questions
|
||||
|
||||
@@ -68,12 +69,12 @@ If the issue template has not been filled out completely, the issue should be cl
|
||||
|
||||
In many cases, the template is mostly filled out but just missing a thing or two or you may have a question or need clarification. In such a case, the submitter should be asked to supply that information.
|
||||
|
||||
1. create a comment requesting the additional information or clarification
|
||||
1. add the `needs reply` label to the task
|
||||
1. add a comment requesting the additional information or clarification
|
||||
1. add the `needs: reply` label to the task
|
||||
|
||||
NOTE: be sure to perform those actions in the order stated. If you add the comment second it will trigger the removal of the label.
|
||||
|
||||
If there is a response to the question, the bot will remove the `needs reply` and apply the `triage` label. The issue will then go through the triage handling again.
|
||||
If there is a response to the question, the bot will remove the `needs: reply` and apply the `triage` label. The issue will then go through the triage handling again.
|
||||
|
||||
if there is no response within 30 days, the issue will be closed and locked.
|
||||
|
||||
|
||||
@@ -37,8 +37,8 @@ function checkGit(tasks) {
|
||||
{
|
||||
title: 'Check current branch',
|
||||
task: () => execa.stdout('git', ['symbolic-ref', '--short', 'HEAD']).then(branch => {
|
||||
if (branch !== 'master') {
|
||||
throw new Error(`Not on "master" branch`);
|
||||
if (branch.indexOf('release') === -1 && branch.indexOf('hotfix') === -1) {
|
||||
throw new Error(`Must be on a "release" or "hotfix" branch.`);
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
43
CHANGELOG.md
43
CHANGELOG.md
@@ -1,3 +1,46 @@
|
||||
## [4.0.3](https://github.com/ionic-team/ionic/compare/v4.0.2...v4.0.3) (2019-04-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **security:** sanitize components using innerHTML ([#18143](https://github.com/ionic-team/ionic/issues/18143)) ([b33d940](https://github.com/ionic-team/ionic/commit/b33d940))
|
||||
|
||||
|
||||
|
||||
## [4.0.2](https://github.com/ionic-team/ionic/compare/v4.0.1...v4.0.2) (2019-02-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **button:** show proper shade for activated button on ios ([#17508](https://github.com/ionic-team/ionic/issues/17508)) ([3a9b679](https://github.com/ionic-team/ionic/commit/3a9b679)), closes [#17436](https://github.com/ionic-team/ionic/issues/17436)
|
||||
* **config:** update types for scrollPadding, inputBlurring and hideCaretOnScroll to boolean ([#17302](https://github.com/ionic-team/ionic/issues/17302)) ([39fbc32](https://github.com/ionic-team/ionic/commit/39fbc32))
|
||||
* **datetime:** default to current date when no value given ([#17443](https://github.com/ionic-team/ionic/issues/17443)) ([644f9f4](https://github.com/ionic-team/ionic/commit/644f9f4))
|
||||
* **item-sliding:** Sliding no longer breaks after removing an item ([#17492](https://github.com/ionic-team/ionic/issues/17492)) ([e27bb2e](https://github.com/ionic-team/ionic/commit/e27bb2e))
|
||||
* **range:** implement RTL (from PR 17157) ([#17384](https://github.com/ionic-team/ionic/issues/17384)) ([4f203bc](https://github.com/ionic-team/ionic/commit/4f203bc)), closes [#17012](https://github.com/ionic-team/ionic/issues/17012)
|
||||
* **searchbar:** allow setting of toolbar color and searchbar color ([#17474](https://github.com/ionic-team/ionic/issues/17474)) ([ba4e117](https://github.com/ionic-team/ionic/commit/ba4e117))
|
||||
* **select:** Account for when options are not loaded immediately ([#17405](https://github.com/ionic-team/ionic/issues/17405)) ([f9f1775](https://github.com/ionic-team/ionic/commit/f9f1775))
|
||||
* **tab-bar:** add translucent tab-bar styles back ([#17376](https://github.com/ionic-team/ionic/issues/17376)) ([374bd77](https://github.com/ionic-team/ionic/commit/374bd77))
|
||||
|
||||
|
||||
|
||||
## [4.0.1](https://github.com/ionic-team/ionic/compare/v4.0.0...v4.0.1) (2019-02-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **build:** modify rollup.config.js to work with Windows ([#17231](https://github.com/ionic-team/ionic/issues/17231)) ([d26d43d](https://github.com/ionic-team/ionic/commit/d26d43d))
|
||||
* **grid:** add flex to ion-grid to allow it to properly render in an ion-item ([#17258](https://github.com/ionic-team/ionic/issues/17258)) ([40c6955](https://github.com/ionic-team/ionic/commit/40c6955)), closes [#17075](https://github.com/ionic-team/ionic/issues/17075)
|
||||
* **menu:** fix content shadow when revealed in iOS ([#17383](https://github.com/ionic-team/ionic/issues/17383)) ([fc43faa](https://github.com/ionic-team/ionic/commit/fc43faa))
|
||||
* **platform:** add additional check for safari PWA ([a584f6e](https://github.com/ionic-team/ionic/commit/a584f6e))
|
||||
* **platform:** add mobileweb platform back ([cf2b2b3](https://github.com/ionic-team/ionic/commit/cf2b2b3))
|
||||
* **popover:** apply fixed position to keep backdrop in viewport ([#17352](https://github.com/ionic-team/ionic/issues/17352)) ([ee3b04a](https://github.com/ionic-team/ionic/commit/ee3b04a)), closes [#17337](https://github.com/ionic-team/ionic/issues/17337)
|
||||
* **popover:** originate animation from right in RTL/MD ([#17381](https://github.com/ionic-team/ionic/issues/17381)) ([bc3aa21](https://github.com/ionic-team/ionic/commit/bc3aa21))
|
||||
* **range:** chrome bug with will-change ([74ce34f](https://github.com/ionic-team/ionic/commit/74ce34f))
|
||||
* **react:** duplicate events being fired in ionic/react ([#17321](https://github.com/ionic-team/ionic/issues/17321)) ([a415001](https://github.com/ionic-team/ionic/commit/a415001))
|
||||
* **reorder:** capture click event ([#17244](https://github.com/ionic-team/ionic/issues/17244)) ([986e67b](https://github.com/ionic-team/ionic/commit/986e67b)), closes [#17241](https://github.com/ionic-team/ionic/issues/17241)
|
||||
* **searchbar:** hide search icon when focused with cancel button ([#17260](https://github.com/ionic-team/ionic/issues/17260)) ([c87867c](https://github.com/ionic-team/ionic/commit/c87867c)), closes [#17252](https://github.com/ionic-team/ionic/issues/17252)
|
||||
|
||||
|
||||
# [4.0.0 Neutronium](https://github.com/ionic-team/ionic/compare/v4.0.0-rc.3...v4.0.0) (2019-01-23)
|
||||
|
||||
Enjoy! 🎈
|
||||
|
||||
@@ -21,6 +21,7 @@ A list of the breaking changes introduced to each component in Ionic Angular v4.
|
||||
- [Back Button](#back-button)
|
||||
- [Button](#button)
|
||||
- [Colors](#colors)
|
||||
- [Component Imports](#component-imports)
|
||||
- [Content](#content)
|
||||
- [Datetime](#datetime)
|
||||
- [Dynamic Mode](#dynamic-mode)
|
||||
@@ -43,6 +44,7 @@ A list of the breaking changes introduced to each component in Ionic Angular v4.
|
||||
- [Navbar](#navbar)
|
||||
- [Option](#option)
|
||||
- [Overlays](#overlays)
|
||||
- [Popover](#popover)
|
||||
- [Radio](#radio)
|
||||
- [Range](#range)
|
||||
- [Refresher](#refresher)
|
||||
@@ -54,6 +56,7 @@ A list of the breaking changes introduced to each component in Ionic Angular v4.
|
||||
- [Tabs](#tabs)
|
||||
- [Text / Typography](#text--typography)
|
||||
- [Theming](#theming)
|
||||
- [Toast](#toast)
|
||||
- [Toolbar](#toolbar)
|
||||
|
||||
|
||||
@@ -86,7 +89,7 @@ await actionSheet.present();
|
||||
|
||||
## Alert
|
||||
|
||||
The `title`, `subTitle` and `enableBackdropDismiss` properties has been renamed to `header`, `subHeader` and `backdropDismiss` respectivelly.
|
||||
The `title`, `subTitle` and `enableBackdropDismiss` properties has been renamed to `header`, `subHeader` and `backdropDismiss` respectively.
|
||||
|
||||
**Old Usage Example:**
|
||||
|
||||
@@ -263,7 +266,7 @@ light: #f4f4f4
|
||||
dark: #222
|
||||
```
|
||||
|
||||
Some of their values have changed and we now include more colors by default:
|
||||
Some of their values have changed, and we now include more colors by default:
|
||||
|
||||
```
|
||||
primary: #3880ff
|
||||
@@ -280,13 +283,24 @@ dark: #222428
|
||||
The `secondary` color saw the largest change. If you were previously using our `secondary` color we recommend switching to `success` instead.
|
||||
|
||||
|
||||
## Component Imports
|
||||
|
||||
For consistency with other frameworks and the rest of APIs and tooling, the exported
|
||||
Ionic components are now prefixed with `Ion`:
|
||||
|
||||
```diff
|
||||
- import { Input, List, Slides } from 'ionic-angular';
|
||||
+ import { IonInput, IonList, IonSlides } from '@ionic/angular';
|
||||
```
|
||||
|
||||
|
||||
## Content
|
||||
|
||||
Content is now a drop-in replacement for `ion-scroll`, that means `ion-content` is much more flexible today, they can be used anywhere, even in a nested fashion.
|
||||
Content is now a drop-in replacement for `ion-scroll`. This makes `ion-content` much more flexible. It can be used anywhere, even nested.
|
||||
|
||||
### resize() was removed
|
||||
### Method Removed
|
||||
|
||||
In Ionic 4, `ion-content` layout is based in flex, that means their size will automatically adjust without requiring to call resize() programmatically.
|
||||
The `resize` method has been removed from Content. In Ionic 4, the `ion-content` is based on a flex layout. This means the content size will automatically adjust without requiring a call to `resize()`.
|
||||
|
||||
|
||||
### Attributes Renamed
|
||||
@@ -319,6 +333,35 @@ import { Datetime } from '@ionic/angular';
|
||||
Components are no longer able to have their mode changed dynamically. You can change the mode before the first render, but after that it will not style properly because only the initial mode's styles are included.
|
||||
|
||||
|
||||
## Events
|
||||
|
||||
Events now emit as a [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) interface that extends the [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event) interface. This interface includes a `detail` property that holds any data passed when the event is triggered.
|
||||
|
||||
This allows you to still get the details of the event. For example, to get the target where the event was dispatched, such as a button that was clicked, you can read in the value of `event.target`.
|
||||
|
||||
**Old Usage Example:**
|
||||
|
||||
```html
|
||||
<ion-select (ionChange)="onSelectChange($event)">
|
||||
```
|
||||
|
||||
```typescript
|
||||
onSelectChange(event) {
|
||||
const value = event.value;
|
||||
console.log('Select value is', value);
|
||||
}
|
||||
```
|
||||
|
||||
**New Usage Example:**
|
||||
|
||||
```typescript
|
||||
onSelectChange(event: CustomEvent) {
|
||||
const value = event.detail.value;
|
||||
console.log('Select value is', value);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## FAB
|
||||
|
||||
### Markup Changed
|
||||
@@ -381,8 +424,8 @@ The attributes to align the fab horizontally are now combined under the `horizon
|
||||
|
||||
| Old Property | New Property | Property Behavior |
|
||||
|--------------|----------------------|-------------------------------------------------------------------------|
|
||||
| left | Removed | |
|
||||
| right | Removed | |
|
||||
| left | Removed, see `start` | |
|
||||
| right | Removed, see `end` | |
|
||||
| center | `horizontal="center"`| Positions to the center of the viewport. |
|
||||
| start | `horizontal="start"` | Positions to the left of the viewport in LTR, and to the right in RTL. |
|
||||
| end | `horizontal="end"` | Positions to the right of the viewport in LTR, and to the left in RTL. |
|
||||
@@ -448,7 +491,7 @@ The `<ion-fab>` container was previously placed inside of the fixed content by d
|
||||
|
||||
The Grid has been refactored in order to support css variables and a dynamic number of columns. The following column attributes have been changed.
|
||||
|
||||
_In the following examples, `{breakpoint}` refers to the optional screen breakpoint (xs, sm, md, lg, xl) and `{value}` refers to the number of columns._
|
||||
_In the following examples, `{breakpoint}` refers to the optional screen breakpoint (xs, sm, md, lg, xl) and `{value}` refers to the number of columns (`auto` or a number between `1` and `12`)._
|
||||
|
||||
- `col-{breakpoint}-{value}` attributes have been renamed to `size-{breakpoint}=“{value}”`
|
||||
- `offset-{breakpoint}-{value}` attributes have been renamed to `offset-{breakpoint}=“{value}”`
|
||||
@@ -585,7 +628,7 @@ Item should now be written as an `<ion-item>` element. Ionic will determine when
|
||||
|
||||
### Label Required
|
||||
|
||||
Previously an `ion-label` would automatically get added to an `ion-item` if one wasn't provided. Now an `ion-label` should always be added if the component is used to display text.
|
||||
Previously an `ion-label` would automatically get added to an `ion-item` if one wasn't provided. Now an `ion-label` should always be added in the item component.
|
||||
|
||||
```html
|
||||
<ion-item>
|
||||
@@ -667,7 +710,7 @@ By default, items that render buttons or anchor tags will show the arrow in `ios
|
||||
|
||||
### Label Required
|
||||
|
||||
Previously an `ion-label` would automatically get added to an `ion-item-divider` if one wasn't provided. Now an `ion-label` should always be added if the component is used to display text.
|
||||
Previously an `ion-label` would automatically get added to an `ion-item-divider` if one wasn't provided. Now an `ion-label` should always be added around the text.
|
||||
|
||||
```html
|
||||
<ion-item-divider>
|
||||
@@ -695,7 +738,7 @@ These values have been renamed to `"start"` and `"end"` to better align with our
|
||||
|
||||
### Markup Changed
|
||||
|
||||
The option component should not be written as a `button` with an `ion-button` directive anymore. It should be written as an `ion-item-option`. This will render a native button element inside of it.
|
||||
The option component should now be written as an `ion-item-option`. Previously it was written as a `button` with an `ion-button` directive. The `ion-item-option` element will render a native button element inside of it.
|
||||
|
||||
**Old Usage Example:**
|
||||
|
||||
@@ -736,7 +779,7 @@ The `getSlidingPercent` method has been renamed to `getSlidingRatio` since the f
|
||||
|
||||
### Attributes Renamed
|
||||
|
||||
The attributes to set label position for input are now combined under the `position` attribute:
|
||||
The attributes to set label position in an item are now combined under the `position` attribute:
|
||||
|
||||
| Old Property | New Property | Property Behavior |
|
||||
|--------------|----------------------|------------------------------------------------------------------------------|
|
||||
@@ -777,7 +820,7 @@ The attributes to set label position for input are now combined under the `posit
|
||||
|
||||
### Label Required
|
||||
|
||||
Previously an `ion-label` would automatically get added to an `ion-list-header` if one wasn't provided. Now an `ion-label` should always be added if the component is used to display text.
|
||||
Previously an `ion-label` would automatically get added to an `ion-list-header` if one wasn't provided. Now an `ion-label` should always be added around the text.
|
||||
|
||||
```html
|
||||
<ion-list-header>
|
||||
@@ -787,38 +830,39 @@ Previously an `ion-label` would automatically get added to an `ion-list-header`
|
||||
|
||||
## Loading
|
||||
|
||||
`dismissOnPageChange` was removed. Fortunately all the navigation API is promise based and there are global events (`ionNavWillChange`) you can listen in order to detect when navigation occurs.
|
||||
|
||||
You should take advantage of these APIs in order to dismiss your loading overlay explicitally.
|
||||
See [Overlays](#overlays).
|
||||
|
||||
|
||||
## Menu
|
||||
|
||||
### Prop renamed
|
||||
### Properties Renamed
|
||||
|
||||
The `swipeEnabled` prop has been renamed to `swipeGesture`.
|
||||
The `content` prop has been renamed to `contentId` and it points to the DOM id of the content:
|
||||
- The `swipeEnabled` property has been renamed to `swipeGesture`.
|
||||
- The `content` prop has been renamed to `contentId` and it points to the DOM id of the content.
|
||||
|
||||
**Old Usage Example:**
|
||||
|
||||
```html
|
||||
<ion-menu swipeEnabled="false" content="nav"> </ion-menu>
|
||||
|
||||
<ion-nav #nav></ion-nav>
|
||||
```
|
||||
|
||||
### Events renamed
|
||||
|
||||
- `ionClose` was renamed to `ionDidClose`
|
||||
- `ionOpen` was renamed to `ionDidOpen`
|
||||
|
||||
|
||||
**New Usage Example:**
|
||||
|
||||
```html
|
||||
<ion-menu swipeGesture="false" contentId="nav"> </ion-menu>
|
||||
|
||||
<ion-nav id="nav"></ion-nav>
|
||||
```
|
||||
|
||||
|
||||
### Events Renamed
|
||||
|
||||
- `ionClose` was renamed to `ionDidClose`
|
||||
- `ionOpen` was renamed to `ionDidOpen`
|
||||
|
||||
|
||||
## Menu Toggle
|
||||
|
||||
### Markup Changed
|
||||
@@ -897,19 +941,19 @@ export class MyPage {
|
||||
|
||||
## Nav
|
||||
|
||||
### Method renamed
|
||||
### Method Renamed
|
||||
|
||||
The `remove` method has been renamed to `removeIndex` to avoid conflicts with HTML and be more descriptive as to what it does.
|
||||
The `getActiveChildNavs` method has been renamed to `getChildNavs`.
|
||||
- The `remove` method has been renamed to `removeIndex` to avoid conflicts with HTML and be more descriptive as to what it does.
|
||||
- The `getActiveChildNavs` method has been renamed to `getChildNavs`.
|
||||
|
||||
### Prop renamed
|
||||
### Prop Renamed
|
||||
|
||||
The `swipeBackEnabled` prop has been renamed to `swipeGesture`.
|
||||
- The `swipeBackEnabled` prop has been renamed to `swipeGesture`.
|
||||
|
||||
|
||||
## Navbar
|
||||
|
||||
The `<ion-navbar>` component has been removed in favor of always using an `<ion-toolbar>` with an added back button:
|
||||
The `<ion-navbar>` component has been removed in favor of always using an `<ion-toolbar>` with an explicit back button:
|
||||
|
||||
**Old Usage Example:**
|
||||
|
||||
@@ -932,6 +976,7 @@ The `<ion-navbar>` component has been removed in favor of always using an `<ion-
|
||||
|
||||
See the [back button](#back-button) changes for more information.
|
||||
|
||||
|
||||
## Option
|
||||
|
||||
### Markup Changed
|
||||
@@ -958,19 +1003,16 @@ Select's option element should now be written as `<ion-select-option>`. This mak
|
||||
</ion-select>
|
||||
```
|
||||
|
||||
### Class Changed
|
||||
### Class Renamed
|
||||
|
||||
The class has been renamed from `Option` to `SelectOption` to keep it consistent with the element tag name.
|
||||
|
||||
|
||||
## Overlays
|
||||
|
||||
### Markup Changed
|
||||
|
||||
Action Sheet, Alert, Loading, Modal, Popover, and Toast:
|
||||
- Should now use `async`/`await`
|
||||
- `enableBackdropDismiss` has been renamed to `backdropDismiss`.
|
||||
|
||||
|
||||
All overlays should now use `async`/`await`. This includes Action Sheet, Alert, Loading, Modal, Popover, and Toast. In addition, the `enableBackdropDismiss` property has been renamed to `backdropDismiss`.
|
||||
|
||||
**Old Usage Example:**
|
||||
|
||||
@@ -1001,12 +1043,48 @@ async presentPopover(ev: any) {
|
||||
```
|
||||
|
||||
|
||||
### Property Removed
|
||||
|
||||
The `dismissOnPageChange` property of the create was removed from Loading & Toast. All of the navigation API is promise based and there are global events (`ionNavWillChange`, `ionNavDidChange`) that you can listen to in order to detect when navigation occurs.
|
||||
|
||||
**Old Usage Example:**
|
||||
|
||||
```javascript
|
||||
openLoading() {
|
||||
let loading = this.loadingCtrl.create({
|
||||
content: 'Loading...',
|
||||
dismissOnPageChange: true
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**New Usage Example:**
|
||||
|
||||
```javascript
|
||||
async openLoading() {
|
||||
let loading = this.loadingCtrl.create({
|
||||
content: 'Loading...'
|
||||
});
|
||||
|
||||
await loading.present();
|
||||
|
||||
const { role, data } = await loading.onDidDismiss();
|
||||
|
||||
console.log('Loading dismissed!');
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Popover
|
||||
|
||||
See [Overlays](#overlays).
|
||||
|
||||
|
||||
## Radio
|
||||
|
||||
### Slot Required
|
||||
|
||||
Previously radio was positioned inside of an item automatically or by using `item-left`/`item-right`. It is now required to have a `slot` to be positioned properly.
|
||||
Previously radio was positioned inside of an item automatically or by using `item-left`/`item-right`. It is now required to have a `slot` to be positioned properly in an item.
|
||||
|
||||
**Old Usage Example:**
|
||||
|
||||
@@ -1155,7 +1233,7 @@ The `enabled` property (with a default value of `true`) has been renamed to `dis
|
||||
|
||||
## Scroll
|
||||
|
||||
`ion-scroll` has been removed, fortunately `ion-content` can work as a drop-in replacement:
|
||||
`ion-scroll` has been removed in favor of using `ion-content`:
|
||||
|
||||
```diff
|
||||
- <ion-scroll scrollX="true">
|
||||
@@ -1440,11 +1518,18 @@ The `ios` and `ios-small` spinner's have been renamed to `lines` and `lines-smal
|
||||
|
||||
## Tabs
|
||||
|
||||
### Breaking changes
|
||||
Tabs has been completely refactored for Ionic 4. In Ionic 3 there was a lot of magic going on behind the scenes to style and generate the tab bar and buttons. While this made it easy to get up and running using tabs in Ionic, it made it more difficult to customize the tabs.
|
||||
|
||||
#### `ion-tabs`
|
||||
We decided to rethink the tabs implementation to make it more flexible and easier to theme for your application. In order to accomplish this, there had to be some changes to the markup.
|
||||
|
||||
The attributes to position the tabs, change the tab layout, enable the tab highlight and hide the tabs have been removed. Instead use [ion-tab-button](#ion-tab-button)
|
||||
|
||||
### `ion-tabs`
|
||||
|
||||
The general usage of the `ion-tabs` element hasn't changed too drastically. Its purpose is still mostly the same - it wraps the entire layout.
|
||||
|
||||
#### Properties Removed
|
||||
|
||||
The attributes to position the tabs, change the tab layout, enable the tab highlight and hide the tabs have been removed. Instead customize this content in the [ion-tab-button](#ion-tab-button).
|
||||
|
||||
**Old Usage Example:**
|
||||
|
||||
@@ -1463,21 +1548,99 @@ The attributes to position the tabs, change the tab layout, enable the tab highl
|
||||
```
|
||||
|
||||
|
||||
#### `ion-tab`
|
||||
### `ion-tab`
|
||||
|
||||
`ion-tab` was removed. Instead you have to use [ion-tab-button](#ion-tab-button).
|
||||
The `ion-tab` has been removed in the Angular version of Ionic 4. You should use the Angular router with an [ion-tab-button](#ion-tab-button) that has a `tab` property.
|
||||
|
||||
#### `ion-tab-button`
|
||||
```typescript
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
You can add `<ion-label>` and `<ion-icon>` inside `ion-tab-button`. `ion-tab-button` has to be wrapped by `<ion-tab-bar>`.
|
||||
import { TabsPage } from './tabs.page';
|
||||
|
||||
The tab attribute defines the route (which should be shown on switching to this tab).
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'tabs',
|
||||
component: TabsPage,
|
||||
children: [
|
||||
{
|
||||
path: 'tab1',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
loadChildren: '../tab1/tab1.module#Tab1PageModule'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'tab2',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
loadChildren: '../tab2/tab2.module#Tab2PageModule'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'tab3',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
loadChildren: '../tab3/tab3.module#Tab3PageModule'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
redirectTo: '/tabs/tab1',
|
||||
pathMatch: 'full'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
redirectTo: '/tabs/tab1',
|
||||
pathMatch: 'full'
|
||||
}
|
||||
];
|
||||
```
|
||||
|
||||
```html
|
||||
<ion-tabs>
|
||||
<ion-tab-bar slot="bottom">
|
||||
<ion-tab-button tab="tab1">
|
||||
<ion-icon name="flash"></ion-icon>
|
||||
<ion-label>Tab One</ion-label>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="tab2">
|
||||
<ion-icon name="apps"></ion-icon>
|
||||
<ion-label>Tab Two</ion-label>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="tab3">
|
||||
<ion-icon name="send"></ion-icon>
|
||||
<ion-label>Tab Three</ion-label>
|
||||
</ion-tab-button>
|
||||
</ion-tab-bar>
|
||||
</ion-tabs>
|
||||
```
|
||||
|
||||
### `ion-tab-bar`
|
||||
|
||||
A new element, `ion-tab-bar`, creates the tab bar that will contain the tab buttons and allow for full customization. It requires `slot` to be placed properly above or below the content.
|
||||
|
||||
### `ion-tab-button`
|
||||
|
||||
A new element, `ion-tab-button`, is used to create each button in the tab bar. These could be static links to different routes, buttons with click handlers on them, or link to whole tab views.
|
||||
|
||||
You can add `<ion-label>` and `<ion-icon>` inside of an `<ion-tab-button>`. An `<ion-tab-button>` should be wrapped by an `<ion-tab-bar>`.
|
||||
|
||||
The tab attribute defines the route to be shown upon clicking on this tab.
|
||||
|
||||
**Old Usage Example:**
|
||||
|
||||
```html
|
||||
<ion-tabs>
|
||||
<ion-tab tabTitle="Schedule" tabIcon="add"></ion-tab>
|
||||
<ion-tab tabTitle="Map" tabIcon="map" tabBadge="2" tabBadgeStyle="danger" enabled="false"></ion-tab>
|
||||
</ion-tabs>
|
||||
```
|
||||
@@ -1493,16 +1656,12 @@ The tab attribute defines the route (which should be shown on switching to this
|
||||
<ion-label>Map</ion-label>
|
||||
<ion-badge color="danger">2</ion-badge>
|
||||
</ion-tab-button>
|
||||
|
||||
<!-- No ion-tab, just a button that looks like a tab -->
|
||||
<ion-tab-button (click)="schedule()">
|
||||
<ion-icon name="add"></ion-icon>
|
||||
<ion-label>Schedule</ion-label>
|
||||
</ion-tab-button>
|
||||
</ion-tab-bar>
|
||||
</ion-tabs>
|
||||
```
|
||||
|
||||
See more usage examples in the [Tabs](https://github.com/ionic-team/ionic/blob/master/core/src/components/tabs) documentation.
|
||||
|
||||
|
||||
## Text / Typography
|
||||
|
||||
@@ -1675,6 +1834,11 @@ Sass variables should no longer be used to change Ionic components. We have buil
|
||||
For more information on theming, check out the [theming documentation](https://beta.ionicframework.com/docs/theming/basics).
|
||||
|
||||
|
||||
## Toast
|
||||
|
||||
See [Overlays](#overlays).
|
||||
|
||||
|
||||
## Toolbar
|
||||
|
||||
### Menu Toggle
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/angular",
|
||||
"version": "4.0.0",
|
||||
"version": "4.0.3",
|
||||
"description": "Angular specific wrappers for @ionic/core",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -45,7 +45,7 @@
|
||||
"css/"
|
||||
],
|
||||
"dependencies": {
|
||||
"@ionic/core": "4.0.0",
|
||||
"@ionic/core": "4.0.3",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -12,7 +12,9 @@ export default {
|
||||
return false;
|
||||
}
|
||||
// anything else is external
|
||||
return !(id.startsWith('.') || id.startsWith('/'));
|
||||
// Windows: C:\xxxxxx\xxx
|
||||
const colonPosition = 1;
|
||||
return !(id.startsWith('.') || id.startsWith('/') || id.charAt(colonPosition) === ':');
|
||||
},
|
||||
plugins: [
|
||||
resolve({
|
||||
|
||||
@@ -98,8 +98,9 @@ export class Platform {
|
||||
* | phablet | on a phablet device. |
|
||||
* | tablet | on a tablet device. |
|
||||
* | electron | in Electron on a desktop device. |
|
||||
* | pwa | as a PWA app. |
|
||||
* | pwa | as a PWA app. |
|
||||
* | mobile | on a mobile device. |
|
||||
* | mobileweb | on a mobile device in a browser. |
|
||||
* | desktop | on a desktop device. |
|
||||
* | hybrid | is a cordova or capacitor app. |
|
||||
*
|
||||
|
||||
@@ -1,23 +1,7 @@
|
||||
# TestApp
|
||||
# Ionic Angular Test App
|
||||
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.1.0.
|
||||
|
||||
## Development server
|
||||
|
||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
||||
```
|
||||
npm install
|
||||
npm run sync:build
|
||||
npm start
|
||||
```
|
||||
|
||||
@@ -23,8 +23,8 @@ The Ionic Core package contains the Web Components that make up the reusable UI
|
||||
Easiest way to start using Ionic Core is by adding a script tag to the CDN:
|
||||
|
||||
```html
|
||||
<link href="https://unpkg.com/@ionic/core@4.0.0/css/ionic.bundle.css" rel="stylesheet">
|
||||
<script src="https://unpkg.com/@ionic/core@4.0.0/dist/ionic.js"></script>
|
||||
<link href="https://unpkg.com/@ionic/core@4.0.3/css/ionic.bundle.css" rel="stylesheet">
|
||||
<script src="https://unpkg.com/@ionic/core@4.0.3/dist/ionic.js"></script>
|
||||
```
|
||||
|
||||
Any Ionic component added to the webpage will automatically load. This includes writing the component tag directly in HTML, or using JavaScript such as `document.createElement('ion-toggle')`.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/core",
|
||||
"version": "4.0.0",
|
||||
"version": "4.0.3",
|
||||
"description": "Base components for Ionic",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -33,7 +33,7 @@
|
||||
"ionicons": "4.5.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@stencil/core": "0.17.0",
|
||||
"@stencil/core": "0.17.3-0",
|
||||
"@stencil/sass": "0.1.1",
|
||||
"@stencil/utils": "latest",
|
||||
"@types/jest": "^23.3.13",
|
||||
|
||||
24
core/src/components.d.ts
vendored
24
core/src/components.d.ts
vendored
@@ -281,7 +281,7 @@ export namespace Components {
|
||||
*/
|
||||
'leaveAnimation'?: AnimationBuilder;
|
||||
/**
|
||||
* The main message to be displayed in the alert.
|
||||
* The main message to be displayed in the alert. `message` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
|
||||
*/
|
||||
'message'?: string;
|
||||
/**
|
||||
@@ -348,7 +348,7 @@ export namespace Components {
|
||||
*/
|
||||
'leaveAnimation'?: AnimationBuilder;
|
||||
/**
|
||||
* The main message to be displayed in the alert.
|
||||
* The main message to be displayed in the alert. `message` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
|
||||
*/
|
||||
'message'?: string;
|
||||
/**
|
||||
@@ -1550,7 +1550,7 @@ export namespace Components {
|
||||
*/
|
||||
'loadingSpinner'?: SpinnerTypes | null;
|
||||
/**
|
||||
* Optional text to display while loading.
|
||||
* Optional text to display while loading. `loadingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
|
||||
*/
|
||||
'loadingText'?: string;
|
||||
}
|
||||
@@ -1560,7 +1560,7 @@ export namespace Components {
|
||||
*/
|
||||
'loadingSpinner'?: SpinnerTypes | null;
|
||||
/**
|
||||
* Optional text to display while loading.
|
||||
* Optional text to display while loading. `loadingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
|
||||
*/
|
||||
'loadingText'?: string;
|
||||
}
|
||||
@@ -2776,7 +2776,7 @@ export namespace Components {
|
||||
*/
|
||||
'onIonNavDidChange'?: (event: CustomEvent<void>) => void;
|
||||
/**
|
||||
* Event fired when the nav will components
|
||||
* Event fired when the nav will change components
|
||||
*/
|
||||
'onIonNavWillChange'?: (event: CustomEvent<void>) => void;
|
||||
/**
|
||||
@@ -3394,7 +3394,7 @@ export namespace Components {
|
||||
*/
|
||||
'pullingIcon'?: string | null;
|
||||
/**
|
||||
* The text you want to display when you begin to pull down
|
||||
* The text you want to display when you begin to pull down. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
|
||||
*/
|
||||
'pullingText'?: string;
|
||||
/**
|
||||
@@ -3402,7 +3402,7 @@ export namespace Components {
|
||||
*/
|
||||
'refreshingSpinner'?: SpinnerTypes | null;
|
||||
/**
|
||||
* The text you want to display when performing a refresh
|
||||
* The text you want to display when performing a refresh. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
|
||||
*/
|
||||
'refreshingText'?: string;
|
||||
}
|
||||
@@ -3412,7 +3412,7 @@ export namespace Components {
|
||||
*/
|
||||
'pullingIcon'?: string | null;
|
||||
/**
|
||||
* The text you want to display when you begin to pull down
|
||||
* The text you want to display when you begin to pull down. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
|
||||
*/
|
||||
'pullingText'?: string;
|
||||
/**
|
||||
@@ -3420,7 +3420,7 @@ export namespace Components {
|
||||
*/
|
||||
'refreshingSpinner'?: SpinnerTypes | null;
|
||||
/**
|
||||
* The text you want to display when performing a refresh
|
||||
* The text you want to display when performing a refresh. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
|
||||
*/
|
||||
'refreshingText'?: string;
|
||||
}
|
||||
@@ -3510,7 +3510,7 @@ export namespace Components {
|
||||
*/
|
||||
'disabled'?: boolean;
|
||||
/**
|
||||
* Event that needs to be listen to in order to respond to reorder action. `ion-reorder-group` uses this event to delegate to the user the reordering of data array. The complete() method exposed as
|
||||
* Event that needs to be listened to in order to complete the reorder action. Once the event has been emitted, the `complete()` method then needs to be called in order to finalize the reorder action.
|
||||
*/
|
||||
'onIonItemReorder'?: (event: CustomEvent<ItemReorderEventDetail>) => void;
|
||||
}
|
||||
@@ -3704,7 +3704,7 @@ export namespace Components {
|
||||
*/
|
||||
'mode': Mode;
|
||||
/**
|
||||
* Set the input's placeholder.
|
||||
* Set the input's placeholder. `placeholder` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
|
||||
*/
|
||||
'placeholder': string;
|
||||
/**
|
||||
@@ -3794,7 +3794,7 @@ export namespace Components {
|
||||
*/
|
||||
'onIonInput'?: (event: CustomEvent<KeyboardEvent>) => void;
|
||||
/**
|
||||
* Set the input's placeholder.
|
||||
* Set the input's placeholder. `placeholder` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
|
||||
*/
|
||||
'placeholder'?: string;
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,7 @@ An Action Sheet is a dialog that displays a set of options. It appears on top of
|
||||
|
||||
### Creating
|
||||
|
||||
An action sheet can be created by the [Action Sheet Controller](../../action-sheet-controller/ActionSheetController) from an array of `buttons`, with each button including properties for its `text`, and optionally a `handler` and `role`. If a handler returns `false` then the action sheet will not be dismissed. An action sheet can also optionally have a `title`, `subTitle` and an `icon`.
|
||||
An action sheet can be created by the [Action Sheet Controller](../action-sheet-controller) from an array of `buttons`, with each button including properties for its `text`, and optionally a `handler` and `role`. If a handler returns `false` then the action sheet will not be dismissed. An action sheet can also optionally have a `title`, `subTitle` and an `icon`.
|
||||
|
||||
### Buttons
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Listen, Me
|
||||
|
||||
import { AlertButton, AlertInput, Animation, AnimationBuilder, Config, CssClassMap, Mode, OverlayEventDetail, OverlayInterface } from '../../interface';
|
||||
import { BACKDROP, dismiss, eventMethod, isCancel, present } from '../../utils/overlays';
|
||||
import { sanitizeDOMString } from '../../utils/sanitization';
|
||||
import { getClassMap } from '../../utils/theme';
|
||||
|
||||
import { iosEnterAnimation } from './animations/ios.enter';
|
||||
@@ -72,6 +73,12 @@ export class Alert implements ComponentInterface, OverlayInterface {
|
||||
|
||||
/**
|
||||
* The main message to be displayed in the alert.
|
||||
* `message` can accept either plaintext or HTML as a string.
|
||||
* To display characters normally reserved for HTML, they
|
||||
* must be escaped. For example `<Ionic>` would become
|
||||
* `<Ionic>`
|
||||
*
|
||||
* For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
|
||||
*/
|
||||
@Prop() message?: string;
|
||||
|
||||
@@ -439,7 +446,7 @@ export class Alert implements ComponentInterface, OverlayInterface {
|
||||
{this.subHeader && <h2 id={subHdrId} class="alert-sub-title">{this.subHeader}</h2>}
|
||||
</div>
|
||||
|
||||
<div id={msgId} class="alert-message" innerHTML={this.message}></div>
|
||||
<div id={msgId} class="alert-message" innerHTML={sanitizeDOMString(this.message)}></div>
|
||||
|
||||
{this.renderAlertInputs(labelledById)}
|
||||
{this.renderAlertButtons()}
|
||||
|
||||
@@ -5,7 +5,7 @@ An Alert is a dialog that presents users with information or collects informatio
|
||||
|
||||
### Creating
|
||||
|
||||
Alerts can be created using a [Alert Controller](../../alert-controller/AlertController). They can be customized by passing alert options in the alert controller's create method.
|
||||
Alerts can be created using an [Alert Controller](../alert-controller). They can be customized by passing alert options in the alert controller's create method.
|
||||
|
||||
|
||||
### Buttons
|
||||
@@ -529,21 +529,21 @@ async function presentAlertCheckbox() {
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Attribute | Description | Type | Default |
|
||||
| ----------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ----------- |
|
||||
| `animated` | `animated` | If `true`, the alert will animate. | `boolean` | `true` |
|
||||
| `backdropDismiss` | `backdrop-dismiss` | If `true`, the alert will be dismissed when the backdrop is clicked. | `boolean` | `true` |
|
||||
| `buttons` | -- | Array of buttons to be added to the alert. | `(string \| AlertButton)[]` | `[]` |
|
||||
| `cssClass` | `css-class` | Additional classes to apply for custom CSS. If multiple classes are provided they should be separated by spaces. | `string \| string[] \| undefined` | `undefined` |
|
||||
| `enterAnimation` | -- | Animation to use when the alert is presented. | `((Animation: Animation, baseEl: any, opts?: any) => Promise<Animation>) \| undefined` | `undefined` |
|
||||
| `header` | `header` | The main title in the heading of the alert. | `string \| undefined` | `undefined` |
|
||||
| `inputs` | -- | Array of input to show in the alert. | `AlertInput[]` | `[]` |
|
||||
| `keyboardClose` | `keyboard-close` | If `true`, the keyboard will be automatically dismissed when the overlay is presented. | `boolean` | `true` |
|
||||
| `leaveAnimation` | -- | Animation to use when the alert is dismissed. | `((Animation: Animation, baseEl: any, opts?: any) => Promise<Animation>) \| undefined` | `undefined` |
|
||||
| `message` | `message` | The main message to be displayed in the alert. | `string \| undefined` | `undefined` |
|
||||
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` |
|
||||
| `subHeader` | `sub-header` | The subtitle in the heading of the alert. Displayed under the title. | `string \| undefined` | `undefined` |
|
||||
| `translucent` | `translucent` | If `true`, the alert will be translucent. | `boolean` | `false` |
|
||||
| Property | Attribute | Description | Type | Default |
|
||||
| ----------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ----------- |
|
||||
| `animated` | `animated` | If `true`, the alert will animate. | `boolean` | `true` |
|
||||
| `backdropDismiss` | `backdrop-dismiss` | If `true`, the alert will be dismissed when the backdrop is clicked. | `boolean` | `true` |
|
||||
| `buttons` | -- | Array of buttons to be added to the alert. | `(string \| AlertButton)[]` | `[]` |
|
||||
| `cssClass` | `css-class` | Additional classes to apply for custom CSS. If multiple classes are provided they should be separated by spaces. | `string \| string[] \| undefined` | `undefined` |
|
||||
| `enterAnimation` | -- | Animation to use when the alert is presented. | `((Animation: Animation, baseEl: any, opts?: any) => Promise<Animation>) \| undefined` | `undefined` |
|
||||
| `header` | `header` | The main title in the heading of the alert. | `string \| undefined` | `undefined` |
|
||||
| `inputs` | -- | Array of input to show in the alert. | `AlertInput[]` | `[]` |
|
||||
| `keyboardClose` | `keyboard-close` | If `true`, the keyboard will be automatically dismissed when the overlay is presented. | `boolean` | `true` |
|
||||
| `leaveAnimation` | -- | Animation to use when the alert is dismissed. | `((Animation: Animation, baseEl: any, opts?: any) => Promise<Animation>) \| undefined` | `undefined` |
|
||||
| `message` | `message` | The main message to be displayed in the alert. `message` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) | `string \| undefined` | `undefined` |
|
||||
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` |
|
||||
| `subHeader` | `sub-header` | The subtitle in the heading of the alert. Displayed under the title. | `string \| undefined` | `undefined` |
|
||||
| `translucent` | `translucent` | If `true`, the alert will be translucent. | `boolean` | `false` |
|
||||
|
||||
|
||||
## Events
|
||||
|
||||
@@ -10,30 +10,7 @@ Avatars can be used by themselves or inside of any element. If placed inside of
|
||||
|
||||
## Usage
|
||||
|
||||
### Angular
|
||||
|
||||
```html
|
||||
<ion-avatar>
|
||||
<img [src]="user.img">
|
||||
</ion-avatar>
|
||||
|
||||
<ion-chip>
|
||||
<ion-avatar>
|
||||
<img [src]="user.img">
|
||||
</ion-avatar>
|
||||
<ion-label>Chip Avatar</ion-label>
|
||||
</ion-chip>
|
||||
|
||||
<ion-item>
|
||||
<ion-avatar slot="start">
|
||||
<img [src]="user.img">
|
||||
</ion-avatar>
|
||||
<ion-label>Item Avatar</ion-label>
|
||||
</ion-item>
|
||||
```
|
||||
|
||||
|
||||
### Javascript
|
||||
### Angular / javascript
|
||||
|
||||
```html
|
||||
<ion-avatar>
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
```html
|
||||
<ion-avatar>
|
||||
<img [src]="user.img">
|
||||
<img src="https://gravatar.com/avatar/dba6bae8c566f9d4041fb9cd9ada7741?d=identicon&f=y">
|
||||
</ion-avatar>
|
||||
|
||||
<ion-chip>
|
||||
<ion-avatar>
|
||||
<img [src]="user.img">
|
||||
<img src="https://gravatar.com/avatar/dba6bae8c566f9d4041fb9cd9ada7741?d=identicon&f=y">
|
||||
</ion-avatar>
|
||||
<ion-label>Chip Avatar</ion-label>
|
||||
</ion-chip>
|
||||
|
||||
<ion-item>
|
||||
<ion-avatar slot="start">
|
||||
<img [src]="user.img">
|
||||
<img src="https://gravatar.com/avatar/dba6bae8c566f9d4041fb9cd9ada7741?d=identicon&f=y">
|
||||
</ion-avatar>
|
||||
<ion-label>Item Avatar</ion-label>
|
||||
</ion-item>
|
||||
|
||||
@@ -34,6 +34,10 @@
|
||||
--opacity: #{$button-ios-opacity-activated};
|
||||
}
|
||||
|
||||
:host(.button-solid.activated.ion-color) .button-native {
|
||||
background: current-color(shade);
|
||||
}
|
||||
|
||||
// iOS Outline Button
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
@@ -4,6 +4,12 @@ import { Color, Mode, RouterDirection } from '../../interface';
|
||||
import { hasShadowDom } from '../../utils/helpers';
|
||||
import { createColorClasses, openURL } from '../../utils/theme';
|
||||
|
||||
/**
|
||||
* @slot - Content is placed between the named slots if provided without a slot.
|
||||
* @slot icon-only - Should be used on an icon in a button that has no text.
|
||||
* @slot start - Content is placed to the left of the button text in LTR, and to the right in RTL.
|
||||
* @slot end - Content is placed to the right of the button text in LTR, and to the left in RTL.
|
||||
*/
|
||||
@Component({
|
||||
tag: 'ion-button',
|
||||
styleUrls: {
|
||||
|
||||
@@ -118,6 +118,16 @@ This attribute specifies the size of the button. Setting this attribute will cha
|
||||
| `ionFocus` | Emitted when the button has focus. | `CustomEvent<void>` |
|
||||
|
||||
|
||||
## Slots
|
||||
|
||||
| Slot | Description |
|
||||
| ------------- | --------------------------------------------------------------------------------- |
|
||||
| | Content is placed between the named slots if provided without a slot. |
|
||||
| `"end"` | Content is placed to the right of the button text in LTR, and to the left in RTL. |
|
||||
| `"icon-only"` | Should be used on an icon in a button that has no text. |
|
||||
| `"start"` | Content is placed to the left of the button text in LTR, and to the right in RTL. |
|
||||
|
||||
|
||||
## CSS Custom Properties
|
||||
|
||||
| Name | Description |
|
||||
|
||||
@@ -4,6 +4,10 @@ import { Color, Config, Mode, ScrollBaseDetail, ScrollDetail } from '../../inter
|
||||
import { isPlatform } from '../../utils/platform';
|
||||
import { createColorClasses, hostContext } from '../../utils/theme';
|
||||
|
||||
/**
|
||||
* @slot - Content is placed in the scrollable area if provided without a slot.
|
||||
* @slot fixed - Should be used for fixed content that should not scroll.
|
||||
*/
|
||||
@Component({
|
||||
tag: 'ion-content',
|
||||
styleUrl: 'content.scss',
|
||||
|
||||
@@ -144,6 +144,14 @@ Type: `Promise<void>`
|
||||
|
||||
|
||||
|
||||
## Slots
|
||||
|
||||
| Slot | Description |
|
||||
| --------- | -------------------------------------------------------------------- |
|
||||
| | Content is placed in the scrollable area if provided without a slot. |
|
||||
| `"fixed"` | Should be used for fixed content that should not scroll. |
|
||||
|
||||
|
||||
## CSS Custom Properties
|
||||
|
||||
| Name | Description |
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
/**
|
||||
* Gets a date value given a format
|
||||
* Defaults to the current date if
|
||||
* no date given
|
||||
*/
|
||||
export function getDateValue(date: DatetimeData, format: string): number {
|
||||
const getValue = getValueFromFormat(date, format);
|
||||
|
||||
if (getValue) { return getValue; }
|
||||
|
||||
const defaultDate = parseDate(new Date().toISOString());
|
||||
return getValueFromFormat((defaultDate as DatetimeData), format);
|
||||
}
|
||||
|
||||
export function renderDatetime(template: string, value: DatetimeData | undefined, locale: LocaleData): string | undefined {
|
||||
if (value === undefined) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { DatetimeChangeEventDetail, DatetimeOptions, Mode, PickerColumn, PickerC
|
||||
import { clamp, findItemLabel, renderHiddenInput } from '../../utils/helpers';
|
||||
import { hostContext } from '../../utils/theme';
|
||||
|
||||
import { DatetimeData, LocaleData, convertDataToISO, convertFormatToKey, convertToArrayOfNumbers, convertToArrayOfStrings, dateDataSortValue, dateSortValue, dateValueRange, daysInMonth, getValueFromFormat, parseDate, parseTemplate, renderDatetime, renderTextFormat, updateDate } from './datetime-util';
|
||||
import { DatetimeData, LocaleData, convertDataToISO, convertFormatToKey, convertToArrayOfNumbers, convertToArrayOfStrings, dateDataSortValue, dateSortValue, dateValueRange, daysInMonth, getDateValue, parseDate, parseTemplate, renderDatetime, renderTextFormat, updateDate } from './datetime-util';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-datetime',
|
||||
@@ -359,7 +359,7 @@ export class Datetime implements ComponentInterface {
|
||||
|
||||
// cool, we've loaded up the columns with options
|
||||
// preselect the option for this column
|
||||
const optValue = getValueFromFormat(this.datetimeValue, format);
|
||||
const optValue = getDateValue(this.datetimeValue, format);
|
||||
const selectedIndex = colOptions.findIndex(opt => opt.value === optValue);
|
||||
|
||||
return {
|
||||
|
||||
@@ -151,18 +151,20 @@ selection between the beginning of 2016, and October 31st of 2020:
|
||||
|
||||
At this time, there is no one-size-fits-all standard to automatically choose the
|
||||
correct language/spelling for a month name, or day of the week name, depending
|
||||
on the language or locale. Good news is that there is an
|
||||
[Intl.DatetimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DatetimeFormat)
|
||||
standard whichmost* browsers have adopted. However, at this time the standard
|
||||
has not been fully implemented by all popular browsers so Ionic is unavailable
|
||||
to take advantage of ityet*. Additionally, Angular also provides an
|
||||
internationalization service, but it is still under heavy development so Ionic
|
||||
does not depend on it at this time.
|
||||
on the language or locale.
|
||||
|
||||
All things considered, the by far easiest solution is to just provide an array
|
||||
of names if the app needs to use names other than the default English version of
|
||||
month and day names. The month names and day names can be either configured at
|
||||
the app level, or individual `ion-datetime` level.
|
||||
The good news is that there is an [Intl.DatetimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DatetimeFormat)
|
||||
standard which [most browsers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DatetimeFormat#Browser_compatibility) have adopted.
|
||||
|
||||
However, at this time the standard has not been fully implemented by all popular browsers
|
||||
so Ionic is unavailable to take advantage of it yet.
|
||||
|
||||
Additionally, Angular also provides an internationalization service, but it is still
|
||||
under heavy development so Ionic does not depend on it at this time.
|
||||
|
||||
The current best practice is to provide an array of names if the app needs to use names other
|
||||
than the default English version of month and day names. The month names and day names can be
|
||||
either configured at the app level, or individual `ion-datetime` level.
|
||||
|
||||
### Component Input Level
|
||||
|
||||
@@ -194,8 +196,7 @@ dates in JavaScript.
|
||||
```html
|
||||
<ion-item>
|
||||
<ion-label>Date</ion-label>
|
||||
<ion-datetime display-format="MM/DD/YYYY" >
|
||||
</ion-datetime>
|
||||
<ion-datetime display-format="MM/DD/YYYY"></ion-datetime>
|
||||
</ion-item>
|
||||
```
|
||||
|
||||
|
||||
35
core/src/components/datetime/test/datetime.spec.ts
Normal file
35
core/src/components/datetime/test/datetime.spec.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { DatetimeOptions } from '../datetime-interface';
|
||||
import { DatetimeData, getDateValue } from '../datetime-util';
|
||||
|
||||
describe('Datetime', () => {
|
||||
describe('getDateValue()', () => {
|
||||
it('it should return the date value for the current day', () => {
|
||||
const today = new Date();
|
||||
|
||||
const dayValue = getDateValue({}, 'DD');
|
||||
const monthvalue = getDateValue({}, 'MM');
|
||||
const yearValue = getDateValue({}, 'YYYY');
|
||||
|
||||
expect(dayValue).toEqual(today.getDate());
|
||||
expect(monthvalue).toEqual(today.getMonth() + 1);
|
||||
expect(yearValue).toEqual(today.getFullYear());
|
||||
});
|
||||
|
||||
it('it should return the date value for a given day', () => {
|
||||
const date = new Date('15 October 1995');
|
||||
const dateTimeData: DatetimeData = {
|
||||
year: date.getFullYear(),
|
||||
month: date.getMonth() + 1,
|
||||
day: date.getDate()
|
||||
}
|
||||
|
||||
const dayValue = getDateValue(dateTimeData, 'DD');
|
||||
const monthvalue = getDateValue(dateTimeData, 'MM');
|
||||
const yearValue = getDateValue(dateTimeData, 'YYYY');
|
||||
|
||||
expect(dayValue).toEqual(date.getDate());
|
||||
expect(monthvalue).toEqual(date.getMonth() + 1);
|
||||
expect(yearValue).toEqual(date.getFullYear());
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
# ion-fab
|
||||
|
||||
Fabs are container elements that contain one or more fab buttons. They should be placed in a fixed position that does not scroll with the content. Fab should have one main fab-button. Fabs can also contain fab-lists which contain related buttons that show when the main fab button is clicked. The same fab container can contain several [fab-list](../../fab-list/FabList) elements with different side values.
|
||||
Fabs are container elements that contain one or more fab buttons. They should be placed in a fixed position that does not scroll with the content. Fab should have one main fab-button. Fabs can also contain fab-lists which contain related buttons that show when the main fab button is clicked. The same fab container can contain several [fab-list](../fab-list) elements with different side values.
|
||||
|
||||
<!-- Auto Generated Below -->
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
@include margin-horizontal(auto);
|
||||
|
||||
display: block;
|
||||
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
// Fixed Grid
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Component, ComponentInterface, Prop } from '@stencil/core';
|
||||
|
||||
import { Config, Mode, SpinnerTypes } from '../../interface';
|
||||
import { sanitizeDOMString } from '../../utils/sanitization';
|
||||
import { createThemedClasses } from '../../utils/theme';
|
||||
|
||||
@Component({
|
||||
@@ -23,6 +24,12 @@ export class InfiniteScrollContent implements ComponentInterface {
|
||||
|
||||
/**
|
||||
* Optional text to display while loading.
|
||||
* `loadingText` can accept either plaintext or HTML as a string.
|
||||
* To display characters normally reserved for HTML, they
|
||||
* must be escaped. For example `<Ionic>` would become
|
||||
* `<Ionic>`
|
||||
*
|
||||
* For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
|
||||
*/
|
||||
@Prop() loadingText?: string;
|
||||
|
||||
@@ -50,7 +57,7 @@ export class InfiniteScrollContent implements ComponentInterface {
|
||||
</div>
|
||||
)}
|
||||
{this.loadingText && (
|
||||
<div class="infinite-loading-text" innerHTML={this.loadingText} />
|
||||
<div class="infinite-loading-text" innerHTML={sanitizeDOMString(this.loadingText)} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -38,10 +38,10 @@ The `ion-infinite-scroll-content` component is the default child used by the `io
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Attribute | Description | Type | Default |
|
||||
| ---------------- | ----------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------- |
|
||||
| `loadingSpinner` | `loading-spinner` | An animated SVG spinner that shows while loading. | `"bubbles" \| "circles" \| "crescent" \| "dots" \| "lines" \| "lines-small" \| null \| undefined` | `undefined` |
|
||||
| `loadingText` | `loading-text` | Optional text to display while loading. | `string \| undefined` | `undefined` |
|
||||
| Property | Attribute | Description | Type | Default |
|
||||
| ---------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------- |
|
||||
| `loadingSpinner` | `loading-spinner` | An animated SVG spinner that shows while loading. | `"bubbles" \| "circles" \| "crescent" \| "dots" \| "lines" \| "lines-small" \| null \| undefined` | `undefined` |
|
||||
| `loadingText` | `loading-text` | Optional text to display while loading. `loadingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) | `string \| undefined` | `undefined` |
|
||||
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
@@ -3,6 +3,11 @@ import { Component, ComponentInterface, Element, Prop } from '@stencil/core';
|
||||
import { Color, Mode } from '../../interface';
|
||||
import { createColorClasses } from '../../utils/theme';
|
||||
|
||||
/**
|
||||
* @slot - Content is placed between the named slots if provided without a slot.
|
||||
* @slot start - Content is placed to the left of the divider text in LTR, and to the right in RTL.
|
||||
* @slot end - Content is placed to the right of the divider text in LTR, and to the left in RTL.
|
||||
*/
|
||||
@Component({
|
||||
tag: 'ion-item-divider',
|
||||
styleUrls: {
|
||||
|
||||
@@ -61,6 +61,15 @@ Item Dividers are block elements that can be used to separate items in a list. T
|
||||
| `sticky` | `sticky` | When it's set to `true`, the item-divider will stay visible when it reaches the top of the viewport until the next `ion-item-divider` replaces it. This feature relies in `position:sticky`: https://caniuse.com/#feat=css-sticky | `boolean` | `false` |
|
||||
|
||||
|
||||
## Slots
|
||||
|
||||
| Slot | Description |
|
||||
| --------- | ---------------------------------------------------------------------------------- |
|
||||
| | Content is placed between the named slots if provided without a slot. |
|
||||
| `"end"` | Content is placed to the right of the divider text in LTR, and to the left in RTL. |
|
||||
| `"start"` | Content is placed to the left of the divider text in LTR, and to the right in RTL. |
|
||||
|
||||
|
||||
## CSS Custom Properties
|
||||
|
||||
| Name | Description |
|
||||
|
||||
@@ -3,6 +3,14 @@ import { Component, ComponentInterface, Element, Listen, Prop } from '@stencil/c
|
||||
import { Color, Mode } from '../../interface';
|
||||
import { createColorClasses } from '../../utils/theme';
|
||||
|
||||
/**
|
||||
* @slot - Content is placed between the named slots if provided without a slot.
|
||||
* @slot start - Content is placed to the left of the option text in LTR, and to the right in RTL.
|
||||
* @slot top - Content is placed above the option text.
|
||||
* @slot icon-only - Should be used on an icon in an option that has no text.
|
||||
* @slot bottom - Content is placed below the option text.
|
||||
* @slot end - Content is placed to the right of the option text in LTR, and to the left in RTL.
|
||||
*/
|
||||
@Component({
|
||||
tag: 'ion-item-option',
|
||||
styleUrls: {
|
||||
|
||||
@@ -18,6 +18,18 @@ action for the item.
|
||||
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` |
|
||||
|
||||
|
||||
## Slots
|
||||
|
||||
| Slot | Description |
|
||||
| ------------- | --------------------------------------------------------------------------------- |
|
||||
| | Content is placed between the named slots if provided without a slot. |
|
||||
| `"bottom"` | Content is placed below the option text. |
|
||||
| `"end"` | Content is placed to the right of the option text in LTR, and to the left in RTL. |
|
||||
| `"icon-only"` | Should be used on an icon in an option that has no text. |
|
||||
| `"start"` | Content is placed to the left of the option text in LTR, and to the right in RTL. |
|
||||
| `"top"` | Content is placed above the option text. |
|
||||
|
||||
|
||||
## CSS Custom Properties
|
||||
|
||||
| Name | Description |
|
||||
|
||||
@@ -66,7 +66,6 @@ export class ItemSliding implements ComponentInterface {
|
||||
|
||||
async componentDidLoad() {
|
||||
this.item = this.el.querySelector('ion-item');
|
||||
|
||||
await this.updateOptions();
|
||||
|
||||
this.gesture = (await import('../../utils/gesture')).createGesture({
|
||||
@@ -91,6 +90,7 @@ export class ItemSliding implements ComponentInterface {
|
||||
|
||||
this.item = null;
|
||||
this.leftOptions = this.rightOptions = undefined;
|
||||
this.closeOpened();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,6 +128,7 @@ export class ItemSliding implements ComponentInterface {
|
||||
async closeOpened(): Promise<boolean> {
|
||||
if (openSlidingItem !== undefined) {
|
||||
openSlidingItem.close();
|
||||
openSlidingItem = undefined;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -162,6 +163,7 @@ export class ItemSliding implements ComponentInterface {
|
||||
this.closeOpened();
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!(this.rightOptions || this.leftOptions);
|
||||
}
|
||||
|
||||
|
||||
49
core/src/components/item-sliding/test/interactive/e2e.ts
Normal file
49
core/src/components/item-sliding/test/interactive/e2e.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
test('item-sliding: interactive', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/item-sliding/test/interactive?ionic:_testing=true'
|
||||
});
|
||||
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
|
||||
const items = await page.$$('ion-item-sliding');
|
||||
expect(items.length).toEqual(3);
|
||||
|
||||
await slideAndDelete(items[0], page);
|
||||
|
||||
const itemsAfterFirstSlide = await page.$$('ion-item-sliding');
|
||||
expect(itemsAfterFirstSlide.length).toEqual(2);
|
||||
|
||||
await slideAndDelete(items[1], page);
|
||||
|
||||
const itemsAfterSecondSlide = await page.$$('ion-item-sliding');
|
||||
expect(itemsAfterSecondSlide.length).toEqual(1);
|
||||
});
|
||||
|
||||
async function slideAndDelete(item: any, page: any) {
|
||||
try {
|
||||
// Get the element's ID
|
||||
const id = await(await item.getProperty('id')).jsonValue();
|
||||
|
||||
// Simulate a drag
|
||||
const boundingBox = await item.boundingBox();
|
||||
const centerX = parseFloat(boundingBox.x + boundingBox.width / 2);
|
||||
const centerY = parseFloat(boundingBox.y + boundingBox.height / 2);
|
||||
|
||||
await page.mouse.move(centerX, centerY);
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(0, centerY);
|
||||
await page.mouse.up();
|
||||
|
||||
// Click the "delete" option
|
||||
const options = await item.$$('ion-item-option');
|
||||
await options[0].click();
|
||||
|
||||
// Wait for element to be removed from DOM
|
||||
await page.waitForSelector(id, { hidden: true });
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
54
core/src/components/item-sliding/test/interactive/index.html
Normal file
54
core/src/components/item-sliding/test/interactive/index.html
Normal file
@@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<html dir="ltr">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Item Sliding - Standalone</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/core.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-list></ion-list>
|
||||
|
||||
<script>
|
||||
let lists = document.getElementsByTagName('ion-list');
|
||||
for (var i = 0; i < lists.length; i++) {
|
||||
for (var j = 0; j < 3; j++) {
|
||||
addSlidingItem(lists[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function addSlidingItem(list) {
|
||||
const slidingItem = document.createElement('ion-item-sliding');
|
||||
slidingItem.innerHTML = `
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
<h2>Item Options Both Sides</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item-options side="end">
|
||||
<ion-item-option color="danger" expandable onclick="remove(this)">
|
||||
<ion-icon name="trash"></ion-icon>
|
||||
Delete
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
`;
|
||||
|
||||
|
||||
slidingItem.id = `item-${list.childElementCount}`;
|
||||
|
||||
list.appendChild(slidingItem);
|
||||
}
|
||||
|
||||
function remove(optionElement) {
|
||||
const slidingElement = optionElement.parentNode.parentNode;
|
||||
lists[0].removeChild(slidingElement);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,6 +3,11 @@ import { Component, ComponentInterface, Element, Listen, Prop, State } from '@st
|
||||
import { Color, CssClassMap, Mode, RouterDirection, StyleEventDetail } from '../../interface';
|
||||
import { createColorClasses, hostContext, openURL } from '../../utils/theme';
|
||||
|
||||
/**
|
||||
* @slot - Content is placed between the named slots if provided without a slot.
|
||||
* @slot start - Content is placed to the left of the item text in LTR, and to the right in RTL.
|
||||
* @slot end - Content is placed to the right of the item text in LTR, and to the left in RTL.
|
||||
*/
|
||||
@Component({
|
||||
tag: 'ion-item',
|
||||
styleUrls: {
|
||||
|
||||
@@ -31,7 +31,7 @@ The below chart details the item slots and where it will place the element insid
|
||||
| Slot | Description |
|
||||
|---------|-----------------------------------------------------------------------------|
|
||||
| `start` | Placed to the left of all other content in LTR, and to the `right` in RTL. |
|
||||
| `end` | Placed to the right of all other content in LTR, and to the `right` in RTL. |
|
||||
| `end` | Placed to the right of all other content in LTR, and to the `left` in RTL. |
|
||||
| none | Placed inside of the input wrapper. |
|
||||
|
||||
|
||||
@@ -721,6 +721,15 @@ Item Inputs
|
||||
| `type` | `type` | The type of the button. Only used when an `onclick` or `button` property is present. | `"button" \| "reset" \| "submit"` | `'button'` |
|
||||
|
||||
|
||||
## Slots
|
||||
|
||||
| Slot | Description |
|
||||
| --------- | ------------------------------------------------------------------------------- |
|
||||
| | Content is placed between the named slots if provided without a slot. |
|
||||
| `"end"` | Content is placed to the right of the item text in LTR, and to the left in RTL. |
|
||||
| `"start"` | Content is placed to the left of the item text in LTR, and to the right in RTL. |
|
||||
|
||||
|
||||
## CSS Custom Properties
|
||||
|
||||
| Name | Description |
|
||||
|
||||
@@ -5,12 +5,12 @@ An overlay that can be used to indicate activity while blocking user interaction
|
||||
|
||||
### Creating
|
||||
|
||||
Loading indicators can be created using a [Loading Controller](../../loading-controller/LoadingController). They can be customized by passing loading options in the loading controller's create method. The spinner name should be passed in the `spinner` property, and any optional HTML can be passed in the `content` property. If a value is not passed to `spinner` the loading indicator will use the spinner specified by the platform.
|
||||
Loading indicators can be created using a [Loading Controller](../loading-controller). They can be customized by passing loading options in the loading controller's create method. The spinner name should be passed in the `spinner` property, and any optional HTML can be passed in the `content` property. If a value is not passed to `spinner` the loading indicator will use the spinner specified by the platform.
|
||||
|
||||
|
||||
### Dismissing
|
||||
|
||||
The loading indicator can be dismissed automatically after a specific amount of time by passing the number of milliseconds to display it in the `duration` of the loading options. By default the loading indicator will show even during page changes, but this can be disabled by setting `dismissOnPageChange` to `true`. To dismiss the loading indicator after creation, call the `dismiss()` method on the loading instance. The `onDidDismiss` function can be called to perform an action after the loading indicator is dismissed.
|
||||
The loading indicator can be dismissed automatically after a specific amount of time by passing the number of milliseconds to display it in the `duration` of the loading options. To dismiss the loading indicator after creation, call the `dismiss()` method on the loading instance. The `onDidDismiss` function can be called to perform an action after the loading indicator is dismissed.
|
||||
|
||||
|
||||
<!-- Auto Generated Below -->
|
||||
@@ -37,7 +37,11 @@ export class LoadingExample {
|
||||
message: 'Hellooo',
|
||||
duration: 2000
|
||||
});
|
||||
return await loading.present();
|
||||
await loading.present();
|
||||
|
||||
const { role, data } = await loading.onDidDismiss();
|
||||
|
||||
console.log('Loading dismissed!');
|
||||
}
|
||||
|
||||
async presentLoadingWithOptions() {
|
||||
@@ -65,7 +69,12 @@ async function presentLoading() {
|
||||
message: 'Hellooo',
|
||||
duration: 2000
|
||||
});
|
||||
return await loading.present();
|
||||
|
||||
await loading.present();
|
||||
|
||||
const { role, data } = await loading.onDidDismiss();
|
||||
|
||||
console.log('Loading dismissed!');
|
||||
}
|
||||
|
||||
async function presentLoadingWithOptions() {
|
||||
|
||||
@@ -15,7 +15,11 @@ export class LoadingExample {
|
||||
message: 'Hellooo',
|
||||
duration: 2000
|
||||
});
|
||||
return await loading.present();
|
||||
await loading.present();
|
||||
|
||||
const { role, data } = await loading.onDidDismiss();
|
||||
|
||||
console.log('Loading dismissed!');
|
||||
}
|
||||
|
||||
async presentLoadingWithOptions() {
|
||||
|
||||
@@ -7,7 +7,12 @@ async function presentLoading() {
|
||||
message: 'Hellooo',
|
||||
duration: 2000
|
||||
});
|
||||
return await loading.present();
|
||||
|
||||
await loading.present();
|
||||
|
||||
const { role, data } = await loading.onDidDismiss();
|
||||
|
||||
console.log('Loading dismissed!');
|
||||
}
|
||||
|
||||
async function presentLoadingWithOptions() {
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
box-shadow: $menu-ios-box-shadow-reveal;
|
||||
}
|
||||
|
||||
[dir=rtl].ios .menu-content-reveal {
|
||||
box-shadow: $menu-ios-box-shadow-reveal-rtl;
|
||||
}
|
||||
|
||||
.ios .menu-content-push {
|
||||
box-shadow: $menu-ios-box-shadow-push;
|
||||
}
|
||||
|
||||
@@ -12,9 +12,15 @@ $menu-ios-box-shadow-color: rgba(0, 0, 0, .08) !default;
|
||||
/// @prop - Box shadow of the menu
|
||||
$menu-ios-box-shadow: -8px 0 42px $menu-ios-box-shadow-color !default;
|
||||
|
||||
/// @prop - Box shadow of the menu in rtl mode
|
||||
$menu-ios-box-shadow-rtl: 8px 0 42px $menu-ios-box-shadow-color !default;
|
||||
|
||||
/// @prop - Box shadow of the reveal menu
|
||||
$menu-ios-box-shadow-reveal: $menu-ios-box-shadow !default;
|
||||
|
||||
/// @prop - Box shadow of the reveal menu
|
||||
$menu-ios-box-shadow-reveal-rtl: $menu-ios-box-shadow-rtl !default;
|
||||
|
||||
/// @prop - Box shadow of the push menu
|
||||
$menu-ios-box-shadow-push: null !default;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ A Modal is a dialog that appears on top of the app's content, and must be dismis
|
||||
|
||||
### Creating
|
||||
|
||||
Modals can be created using a [Modal Controller](../../modal-controller/ModalController). They can be customized by passing modal options in the modal controller's create method.
|
||||
Modals can be created using a [Modal Controller](../modal-controller). They can be customized by passing modal options in the modal controller's create method.
|
||||
|
||||
|
||||
### Passing paramaters
|
||||
@@ -36,7 +36,7 @@ instance.prop2 = value2;
|
||||
This way, your component can access the passed params, check the "Usage" section for further code example for each frameworks.
|
||||
|
||||
|
||||
### Retuning data
|
||||
### Returning data
|
||||
|
||||
Modals can also return data back to the controller when they are dismissed.
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ export class Nav implements NavOutlet {
|
||||
*/
|
||||
@Event() ionNavWillLoad!: EventEmitter<void>;
|
||||
/**
|
||||
* Event fired when the nav will components
|
||||
* Event fired when the nav will change components
|
||||
*/
|
||||
@Event({ bubbles: false }) ionNavWillChange!: EventEmitter<void>;
|
||||
/**
|
||||
|
||||
@@ -23,7 +23,7 @@ Unlike RouterOutlet, Nav is not tied to a particular router. Meaning that if we
|
||||
| Event | Description | Type |
|
||||
| ------------------ | ----------------------------------------------- | ------------------- |
|
||||
| `ionNavDidChange` | Event fired when the nav has changed components | `CustomEvent<void>` |
|
||||
| `ionNavWillChange` | Event fired when the nav will components | `CustomEvent<void>` |
|
||||
| `ionNavWillChange` | Event fired when the nav will change components | `CustomEvent<void>` |
|
||||
|
||||
|
||||
## Methods
|
||||
|
||||
@@ -4,8 +4,10 @@ import { Animation } from '../../../interface';
|
||||
* Md Popover Enter Animation
|
||||
*/
|
||||
export function mdEnterAnimation(AnimationC: Animation, baseEl: HTMLElement, ev?: Event): Promise<Animation> {
|
||||
const isRTL = document.dir === 'rtl';
|
||||
|
||||
let originY = 'top';
|
||||
let originX = 'left';
|
||||
let originX = isRTL ? 'right' : 'left';
|
||||
|
||||
const contentEl = baseEl.querySelector('.popover-content') as HTMLElement;
|
||||
const contentDimentions = contentEl.getBoundingClientRect();
|
||||
@@ -24,7 +26,6 @@ export function mdEnterAnimation(AnimationC: Animation, baseEl: HTMLElement, ev?
|
||||
? targetDim.top
|
||||
: bodyHeight / 2 - contentHeight / 2;
|
||||
|
||||
const isRTL = document.dir === 'rtl';
|
||||
const targetLeft =
|
||||
targetDim != null && 'left' in targetDim
|
||||
? isRTL
|
||||
@@ -49,7 +50,7 @@ export function mdEnterAnimation(AnimationC: Animation, baseEl: HTMLElement, ev?
|
||||
bodyWidth
|
||||
) {
|
||||
popoverCSS.left = bodyWidth - contentWidth - POPOVER_MD_BODY_PADDING;
|
||||
originX = 'right';
|
||||
originX = isRTL ? 'left' : 'right';
|
||||
}
|
||||
|
||||
// If the popover when popped down stretches past bottom of screen,
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
@include position(0, 0, 0, 0);
|
||||
|
||||
display: flex;
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
@@ -4,7 +4,7 @@ A Popover is a dialog that appears on top of the current page. It can be used fo
|
||||
|
||||
### Creating
|
||||
|
||||
Popovers can be created using a [Popover Controller](https://beta.ionicframework.com/docs/api/popover-controller). They can be customized by passing popover options in the popover controller's create method.
|
||||
Popovers can be created using a [Popover Controller](../popover-controller). They can be customized by passing popover options in the popover controller's create method.
|
||||
|
||||
### Presenting
|
||||
|
||||
@@ -52,8 +52,8 @@ async function presentPopover(ev) {
|
||||
|
||||
const popover = await popoverController.create({
|
||||
component: 'popover-example-page',
|
||||
translucent: true
|
||||
event: ev,
|
||||
translucent: true
|
||||
});
|
||||
return await popover.present();
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ async function presentPopover(ev) {
|
||||
|
||||
const popover = await popoverController.create({
|
||||
component: 'popover-example-page',
|
||||
translucent: true
|
||||
event: ev,
|
||||
translucent: true
|
||||
});
|
||||
return await popover.present();
|
||||
}
|
||||
|
||||
@@ -105,6 +105,11 @@
|
||||
@include border-radius(50%, 50%, 50%, 0);
|
||||
@include margin-horizontal(-13px, null);
|
||||
|
||||
@include rtl() {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
left: unset;
|
||||
}
|
||||
|
||||
position: absolute;
|
||||
|
||||
width: 26px;
|
||||
|
||||
@@ -79,6 +79,11 @@
|
||||
null
|
||||
);
|
||||
|
||||
@include rtl() {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
left: unset;
|
||||
}
|
||||
|
||||
position: absolute;
|
||||
|
||||
width: var(--knob-handle-size);
|
||||
@@ -95,6 +100,12 @@
|
||||
.range-bar {
|
||||
@include border-radius(var(--bar-border-radius));
|
||||
@include position(calc((var(--height) - var(--bar-height)) / 2), null, null, 0);
|
||||
|
||||
@include rtl() {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
left: unset;
|
||||
}
|
||||
|
||||
position: absolute;
|
||||
|
||||
width: 100%;
|
||||
@@ -113,6 +124,11 @@
|
||||
calc(50% - var(--knob-size) / 2)
|
||||
);
|
||||
|
||||
@include rtl() {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
left: unset;
|
||||
}
|
||||
|
||||
position: absolute;
|
||||
|
||||
width: var(--knob-size);
|
||||
@@ -129,9 +145,6 @@
|
||||
will-change: left, right;
|
||||
}
|
||||
|
||||
:host(.range-pressed) .range-knob-handle {
|
||||
will-change: left;
|
||||
}
|
||||
|
||||
// Range in Item
|
||||
// ----------------------------
|
||||
|
||||
@@ -4,6 +4,10 @@ import { Color, Gesture, GestureDetail, KnobName, Mode, RangeChangeEventDetail,
|
||||
import { clamp, debounceEvent } from '../../utils/helpers';
|
||||
import { createColorClasses, hostContext } from '../../utils/theme';
|
||||
|
||||
/**
|
||||
* @slot start - Content is placed to the left of the range slider in LTR, and to the right in RTL.
|
||||
* @slot end - Content is placed to the right of the range slider in LTR, and to the left in RTL.
|
||||
*/
|
||||
@Component({
|
||||
tag: 'ion-range',
|
||||
styleUrls: {
|
||||
@@ -235,7 +239,11 @@ export class Range implements ComponentInterface {
|
||||
const currentX = detail.currentX;
|
||||
|
||||
// figure out which knob they started closer to
|
||||
const ratio = clamp(0, (currentX - rect.left) / rect.width, 1);
|
||||
let ratio = clamp(0, (currentX - rect.left) / rect.width, 1);
|
||||
if (document.dir === 'rtl') {
|
||||
ratio = 1 - ratio;
|
||||
}
|
||||
|
||||
this.pressedKnob =
|
||||
!this.dualKnobs ||
|
||||
Math.abs(this.ratioA - ratio) < Math.abs(this.ratioB - ratio)
|
||||
@@ -262,6 +270,10 @@ export class Range implements ComponentInterface {
|
||||
// update the knob being interacted with
|
||||
const rect = this.rect;
|
||||
let ratio = clamp(0, (currentX - rect.left) / rect.width, 1);
|
||||
if (document.dir === 'rtl') {
|
||||
ratio = 1 - ratio;
|
||||
}
|
||||
|
||||
if (this.snaps) {
|
||||
// snaps the ratio to the current value
|
||||
ratio = valueToRatio(
|
||||
@@ -353,31 +365,56 @@ export class Range implements ComponentInterface {
|
||||
render() {
|
||||
const { min, max, step, ratioLower, ratioUpper } = this;
|
||||
|
||||
const barL = `${ratioLower * 100}%`;
|
||||
const barR = `${100 - ratioUpper * 100}%`;
|
||||
const barStart = `${ratioLower * 100}%`;
|
||||
const barEnd = `${100 - ratioUpper * 100}%`;
|
||||
|
||||
const isRTL = document.dir === 'rtl';
|
||||
const start = isRTL ? 'right' : 'left';
|
||||
const end = isRTL ? 'left' : 'right';
|
||||
|
||||
const ticks = [];
|
||||
if (this.snaps) {
|
||||
for (let value = min; value <= max; value += step) {
|
||||
const ratio = valueToRatio(value, min, max);
|
||||
ticks.push({
|
||||
|
||||
const tick: any = {
|
||||
ratio,
|
||||
active: ratio >= ratioLower && ratio <= ratioUpper,
|
||||
left: `${ratio * 100}%`
|
||||
});
|
||||
};
|
||||
|
||||
tick[start] = `${ratio * 100}%`;
|
||||
|
||||
ticks.push(tick);
|
||||
}
|
||||
}
|
||||
|
||||
const tickStyle = (tick: any) => {
|
||||
const style: any = {};
|
||||
|
||||
style[start] = tick[start];
|
||||
|
||||
return style;
|
||||
};
|
||||
|
||||
const barStyle = () => {
|
||||
const style: any = {};
|
||||
|
||||
style[start] = barStart;
|
||||
style[end] = barEnd;
|
||||
|
||||
return style;
|
||||
};
|
||||
|
||||
return [
|
||||
<slot name="start"></slot>,
|
||||
<div class="range-slider" ref={el => this.rangeSlider = el}>
|
||||
{ticks.map(t => (
|
||||
{ticks.map(tick => (
|
||||
<div
|
||||
style={{ left: t.left }}
|
||||
style={tickStyle(tick)}
|
||||
role="presentation"
|
||||
class={{
|
||||
'range-tick': true,
|
||||
'range-tick-active': t.active
|
||||
'range-tick-active': tick.active
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
@@ -386,10 +423,7 @@ export class Range implements ComponentInterface {
|
||||
<div
|
||||
class="range-bar range-bar-active"
|
||||
role="presentation"
|
||||
style={{
|
||||
left: barL,
|
||||
right: barR
|
||||
}}
|
||||
style={barStyle()}
|
||||
/>
|
||||
|
||||
{ renderKnob({
|
||||
@@ -435,6 +469,17 @@ interface RangeKnob {
|
||||
}
|
||||
|
||||
function renderKnob({ knob, value, ratio, min, max, disabled, pressed, pin, handleKeyboard }: RangeKnob) {
|
||||
const isRTL = document.dir === 'rtl';
|
||||
const start = isRTL ? 'right' : 'left';
|
||||
|
||||
const knobStyle = () => {
|
||||
const style: any = {};
|
||||
|
||||
style[start] = `${ratio * 100}%`;
|
||||
|
||||
return style;
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
onKeyDown={(ev: KeyboardEvent) => {
|
||||
@@ -458,9 +503,7 @@ function renderKnob({ knob, value, ratio, min, max, disabled, pressed, pin, hand
|
||||
'range-knob-min': value === min,
|
||||
'range-knob-max': value === max
|
||||
}}
|
||||
style={{
|
||||
'left': `${ratio * 100}%`
|
||||
}}
|
||||
style={knobStyle()}
|
||||
role="slider"
|
||||
tabindex={disabled ? -1 : 0}
|
||||
aria-valuemin={min}
|
||||
|
||||
@@ -111,6 +111,14 @@ left or right of the range.
|
||||
| `ionFocus` | Emitted when the range has focus. | `CustomEvent<void>` |
|
||||
|
||||
|
||||
## Slots
|
||||
|
||||
| Slot | Description |
|
||||
| --------- | ---------------------------------------------------------------------------------- |
|
||||
| `"end"` | Content is placed to the right of the range slider in LTR, and to the left in RTL. |
|
||||
| `"start"` | Content is placed to the left of the range slider in LTR, and to the right in RTL. |
|
||||
|
||||
|
||||
## CSS Custom Properties
|
||||
|
||||
| Name | Description |
|
||||
|
||||
@@ -9,12 +9,12 @@ The refresher content contains the text, icon and spinner to display during a pu
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Attribute | Description | Type | Default |
|
||||
| ------------------- | -------------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------- |
|
||||
| `pullingIcon` | `pulling-icon` | A static icon to display when you begin to pull down | `null \| string \| undefined` | `undefined` |
|
||||
| `pullingText` | `pulling-text` | The text you want to display when you begin to pull down | `string \| undefined` | `undefined` |
|
||||
| `refreshingSpinner` | `refreshing-spinner` | An animated SVG spinner that shows when refreshing begins | `"bubbles" \| "circles" \| "crescent" \| "dots" \| "lines" \| "lines-small" \| null \| undefined` | `undefined` |
|
||||
| `refreshingText` | `refreshing-text` | The text you want to display when performing a refresh | `string \| undefined` | `undefined` |
|
||||
| Property | Attribute | Description | Type | Default |
|
||||
| ------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------- |
|
||||
| `pullingIcon` | `pulling-icon` | A static icon to display when you begin to pull down | `null \| string \| undefined` | `undefined` |
|
||||
| `pullingText` | `pulling-text` | The text you want to display when you begin to pull down. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) | `string \| undefined` | `undefined` |
|
||||
| `refreshingSpinner` | `refreshing-spinner` | An animated SVG spinner that shows when refreshing begins | `"bubbles" \| "circles" \| "crescent" \| "dots" \| "lines" \| "lines-small" \| null \| undefined` | `undefined` |
|
||||
| `refreshingText` | `refreshing-text` | The text you want to display when performing a refresh. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) | `string \| undefined` | `undefined` |
|
||||
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Component, ComponentInterface, Prop } from '@stencil/core';
|
||||
|
||||
import { Config, SpinnerTypes } from '../../interface';
|
||||
import { sanitizeDOMString } from '../../utils/sanitization';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-refresher-content'
|
||||
@@ -15,7 +16,13 @@ export class RefresherContent implements ComponentInterface {
|
||||
@Prop({ mutable: true }) pullingIcon?: string | null;
|
||||
|
||||
/**
|
||||
* The text you want to display when you begin to pull down
|
||||
* The text you want to display when you begin to pull down.
|
||||
* `pullingText` can accept either plaintext or HTML as a string.
|
||||
* To display characters normally reserved for HTML, they
|
||||
* must be escaped. For example `<Ionic>` would become
|
||||
* `<Ionic>`
|
||||
*
|
||||
* For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
|
||||
*/
|
||||
@Prop() pullingText?: string;
|
||||
|
||||
@@ -25,7 +32,13 @@ export class RefresherContent implements ComponentInterface {
|
||||
@Prop({ mutable: true }) refreshingSpinner?: SpinnerTypes | null;
|
||||
|
||||
/**
|
||||
* The text you want to display when performing a refresh
|
||||
* The text you want to display when performing a refresh.
|
||||
* `refreshingText` can accept either plaintext or HTML as a string.
|
||||
* To display characters normally reserved for HTML, they
|
||||
* must be escaped. For example `<Ionic>` would become
|
||||
* `<Ionic>`
|
||||
*
|
||||
* For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
|
||||
*/
|
||||
@Prop() refreshingText?: string;
|
||||
|
||||
@@ -47,7 +60,7 @@ export class RefresherContent implements ComponentInterface {
|
||||
</div>
|
||||
}
|
||||
{this.pullingText &&
|
||||
<div class="refresher-pulling-text" innerHTML={this.pullingText}></div>
|
||||
<div class="refresher-pulling-text" innerHTML={sanitizeDOMString(this.pullingText)}></div>
|
||||
}
|
||||
</div>,
|
||||
<div class="refresher-refreshing">
|
||||
@@ -57,7 +70,7 @@ export class RefresherContent implements ComponentInterface {
|
||||
</div>
|
||||
}
|
||||
{this.refreshingText &&
|
||||
<div class="refresher-refreshing-text" innerHTML={this.refreshingText}></div>
|
||||
<div class="refresher-refreshing-text" innerHTML={sanitizeDOMString(this.refreshingText)}></div>
|
||||
}
|
||||
</div>
|
||||
];
|
||||
|
||||
@@ -13,7 +13,7 @@ reorderGroup.addEventListener('ionItemReorder', (ev) => {
|
||||
});
|
||||
```
|
||||
|
||||
The event's detail includes all the relevant information about the reorder operation, including the `from` and `to` indexes. The meaning of this indexes are pretty self-explanatory, the item **from** index X, moved **to** the index Y.
|
||||
The event's detail includes all the relevant information about the reorder operation, including the `from` and `to` indexes. In the context of reordering, an item moves `from` index X `to` index Y.
|
||||
|
||||
For example, in this list we move the item at index `0` to the index `3`:
|
||||
|
||||
@@ -42,10 +42,6 @@ Fortunately this `complete()` method can optionally accept an array as input and
|
||||
this.dataList = reorderGroup.complete(this.dataList);
|
||||
```
|
||||
|
||||
This utility is really handy when
|
||||
|
||||
|
||||
|
||||
<!-- Auto Generated Below -->
|
||||
|
||||
|
||||
@@ -178,9 +174,9 @@ reorderGroup.addEventListener('ionItemReorder', ({detail}) => {
|
||||
|
||||
## Events
|
||||
|
||||
| Event | Description | Type |
|
||||
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- |
|
||||
| `ionItemReorder` | Event that needs to be listen to in order to respond to reorder action. `ion-reorder-group` uses this event to delegate to the user the reordering of data array. The complete() method exposed as | `CustomEvent<ItemReorderEventDetail>` |
|
||||
| Event | Description | Type |
|
||||
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- |
|
||||
| `ionItemReorder` | Event that needs to be listened to in order to complete the reorder action. Once the event has been emitted, the `complete()` method then needs to be called in order to finalize the reorder action. | `CustomEvent<ItemReorderEventDetail>` |
|
||||
|
||||
|
||||
## Methods
|
||||
|
||||
@@ -50,11 +50,9 @@ export class ReorderGroup implements ComponentInterface {
|
||||
}
|
||||
|
||||
/**
|
||||
* Event that needs to be listen to in order to respond to reorder action.
|
||||
* `ion-reorder-group` uses this event to delegate to the user the reordering of data array.
|
||||
*
|
||||
*
|
||||
* The complete() method exposed as
|
||||
* Event that needs to be listened to in order to complete the reorder action.
|
||||
* Once the event has been emitted, the `complete()` method then needs
|
||||
* to be called in order to finalize the reorder action.
|
||||
*/
|
||||
@Event() ionItemReorder!: EventEmitter<ItemReorderEventDetail>;
|
||||
|
||||
|
||||
@@ -27,35 +27,35 @@
|
||||
<ion-list>
|
||||
<ion-reorder-group id="reorder">
|
||||
|
||||
<ion-item>
|
||||
<ion-item button onclick="openAlert()">
|
||||
<ion-label>
|
||||
Item 1 (default ion-reorder)
|
||||
</ion-label>
|
||||
<ion-reorder slot="end"></ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-item button onclick="openAlert()">
|
||||
<ion-label>
|
||||
Item 2 (default ion-reorder)
|
||||
</ion-label>
|
||||
<ion-reorder slot="end"></ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-item button onclick="openAlert()">
|
||||
<ion-label>
|
||||
Item 3 (default ion-reorder slot="start")
|
||||
</ion-label>
|
||||
<ion-reorder slot="start"></ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item color="secondary">
|
||||
<ion-item button color="secondary">
|
||||
<ion-label>
|
||||
Item 4 (default ion-reorder slot="start")
|
||||
</ion-label>
|
||||
<ion-reorder slot="start"></ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-item button onclick="openAlert()">
|
||||
<ion-label>
|
||||
Item 5 (custom ion-reorder)
|
||||
</ion-label>
|
||||
@@ -64,7 +64,7 @@
|
||||
</ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-item button onclick="openAlert()">
|
||||
<ion-label>
|
||||
Item 6 (custom ion-reorder)
|
||||
</ion-label>
|
||||
@@ -73,7 +73,7 @@
|
||||
</ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-item button onclick="openAlert()">
|
||||
<ion-label>
|
||||
Item 7 (custom ion-reorder slot="start")
|
||||
</ion-label>
|
||||
@@ -83,7 +83,7 @@
|
||||
</ion-item>
|
||||
|
||||
<ion-reorder>
|
||||
<ion-item>
|
||||
<ion-item button onclick="openAlert()">
|
||||
<ion-label>
|
||||
Item 8 (the whole item can be dragged)
|
||||
</ion-label>
|
||||
@@ -91,7 +91,7 @@
|
||||
</ion-reorder>
|
||||
|
||||
<ion-reorder>
|
||||
<ion-item>
|
||||
<ion-item button onclick="openAlert()">
|
||||
<ion-label>
|
||||
Item 9 (the whole item can be dragged)
|
||||
</ion-label>
|
||||
@@ -99,7 +99,7 @@
|
||||
</ion-reorder>
|
||||
|
||||
<ion-reorder>
|
||||
<ion-item>
|
||||
<ion-item button onclick="openAlert()">
|
||||
<ion-label>
|
||||
Item 10 (the whole item can be dragged)
|
||||
</ion-label>
|
||||
@@ -113,6 +113,9 @@
|
||||
</ion-app>
|
||||
|
||||
<script>
|
||||
function openAlert() {
|
||||
alert('click');
|
||||
}
|
||||
function toggleEdit() {
|
||||
const reorderGroup = document.getElementById('reorder');
|
||||
reorderGroup.disabled = !reorderGroup.disabled;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Reorder is a component that allows an item to be dragged to change its order. It must be used within an `ion-reorder-group` to provide a visual drag and drop interface.
|
||||
|
||||
`ion-reorder` is the anchor users will use to drag and drop items inside the `ion-reorder-group`. It must be added to `ion-item` in order for them to be draggable.
|
||||
`ion-reorder` is the anchor users will use to drag and drop items inside the `ion-reorder-group`.
|
||||
|
||||
```html
|
||||
<ion-item>
|
||||
@@ -13,9 +13,6 @@ Reorder is a component that allows an item to be dragged to change its order. It
|
||||
</ion-item>
|
||||
```
|
||||
|
||||
The position of the
|
||||
|
||||
|
||||
<!-- Auto Generated Below -->
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, ComponentInterface } from '@stencil/core';
|
||||
import { Component, ComponentInterface, Listen } from '@stencil/core';
|
||||
|
||||
import { Mode } from '../../interface';
|
||||
|
||||
@@ -14,6 +14,12 @@ export class Reorder implements ComponentInterface {
|
||||
|
||||
mode!: Mode;
|
||||
|
||||
@Listen('click', { capture: true })
|
||||
onClick(ev: Event) {
|
||||
ev.preventDefault();
|
||||
ev.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<slot>
|
||||
|
||||
@@ -82,23 +82,23 @@ A Searchbar should be used instead of an input to search lists. A clear button i
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Attribute | Description | Type | Default |
|
||||
| ------------------ | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | ----------------- |
|
||||
| `animated` | `animated` | If `true`, enable searchbar animation. | `boolean` | `false` |
|
||||
| `autocomplete` | `autocomplete` | Set the input's autocomplete property. | `"off" \| "on"` | `'off'` |
|
||||
| `autocorrect` | `autocorrect` | Set the input's autocorrect property. | `"off" \| "on"` | `'off'` |
|
||||
| `cancelButtonIcon` | `cancel-button-icon` | Set the cancel button icon. Only applies to `md` mode. | `string` | `'md-arrow-back'` |
|
||||
| `cancelButtonText` | `cancel-button-text` | Set the the cancel button text. Only applies to `ios` mode. | `string` | `'Cancel'` |
|
||||
| `clearIcon` | `clear-icon` | Set the clear icon. Defaults to `"close-circle"` for `ios` and `"close"` for `md`. | `string \| undefined` | `undefined` |
|
||||
| `color` | `color` | The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). | `string \| undefined` | `undefined` |
|
||||
| `debounce` | `debounce` | Set the amount of time, in milliseconds, to wait to trigger the `ionChange` event after each keystroke. | `number` | `250` |
|
||||
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` |
|
||||
| `placeholder` | `placeholder` | Set the input's placeholder. | `string` | `'Search'` |
|
||||
| `searchIcon` | `search-icon` | The icon to use as the search icon. | `string` | `'search'` |
|
||||
| `showCancelButton` | `show-cancel-button` | If `true`, show the cancel button. | `boolean` | `false` |
|
||||
| `spellcheck` | `spellcheck` | If `true`, enable spellcheck on the input. | `boolean` | `false` |
|
||||
| `type` | `type` | Set the type of the input. | `"email" \| "number" \| "password" \| "search" \| "tel" \| "text" \| "url"` | `'search'` |
|
||||
| `value` | `value` | the value of the searchbar. | `null \| string \| undefined` | `''` |
|
||||
| Property | Attribute | Description | Type | Default |
|
||||
| ------------------ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | ----------------- |
|
||||
| `animated` | `animated` | If `true`, enable searchbar animation. | `boolean` | `false` |
|
||||
| `autocomplete` | `autocomplete` | Set the input's autocomplete property. | `"off" \| "on"` | `'off'` |
|
||||
| `autocorrect` | `autocorrect` | Set the input's autocorrect property. | `"off" \| "on"` | `'off'` |
|
||||
| `cancelButtonIcon` | `cancel-button-icon` | Set the cancel button icon. Only applies to `md` mode. | `string` | `'md-arrow-back'` |
|
||||
| `cancelButtonText` | `cancel-button-text` | Set the the cancel button text. Only applies to `ios` mode. | `string` | `'Cancel'` |
|
||||
| `clearIcon` | `clear-icon` | Set the clear icon. Defaults to `"close-circle"` for `ios` and `"close"` for `md`. | `string \| undefined` | `undefined` |
|
||||
| `color` | `color` | The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). | `string \| undefined` | `undefined` |
|
||||
| `debounce` | `debounce` | Set the amount of time, in milliseconds, to wait to trigger the `ionChange` event after each keystroke. | `number` | `250` |
|
||||
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` |
|
||||
| `placeholder` | `placeholder` | Set the input's placeholder. `placeholder` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) | `string` | `'Search'` |
|
||||
| `searchIcon` | `search-icon` | The icon to use as the search icon. | `string` | `'search'` |
|
||||
| `showCancelButton` | `show-cancel-button` | If `true`, show the cancel button. | `boolean` | `false` |
|
||||
| `spellcheck` | `spellcheck` | If `true`, enable spellcheck on the input. | `boolean` | `false` |
|
||||
| `type` | `type` | Set the type of the input. | `"email" \| "number" \| "password" \| "search" \| "tel" \| "text" \| "url"` | `'search'` |
|
||||
| `value` | `value` | the value of the searchbar. | `null \| string \| undefined` | `''` |
|
||||
|
||||
|
||||
## Events
|
||||
|
||||
@@ -156,11 +156,11 @@
|
||||
// Searchbar in Toolbar Color
|
||||
// -----------------------------------------
|
||||
|
||||
:host-context(ion-toolbar.ion-color) {
|
||||
:host-context(ion-toolbar.ion-color):not(.ion-color) {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
:host-context(ion-toolbar.ion-color) .searchbar-cancel-button {
|
||||
:host-context(ion-toolbar.ion-color):not(.ion-color) .searchbar-cancel-button {
|
||||
color: currentColor;
|
||||
}
|
||||
|
||||
@@ -170,13 +170,13 @@
|
||||
opacity: $searchbar-ios-input-icon-opacity;
|
||||
}
|
||||
|
||||
:host-context(ion-toolbar.ion-color) .searchbar-input {
|
||||
:host-context(ion-toolbar.ion-color):not(.ion-color) .searchbar-input {
|
||||
background: rgba(var(--ion-color-contrast-rgb), $searchbar-ios-input-background-color-alpha);
|
||||
color: currentColor;
|
||||
}
|
||||
|
||||
:host-context(ion-toolbar.ion-color) .searchbar-clear-button {
|
||||
:host-context(ion-toolbar.ion-color):not(.ion-color) .searchbar-clear-button {
|
||||
color: currentColor;
|
||||
|
||||
opacity: $searchbar-ios-input-icon-opacity;
|
||||
}
|
||||
}
|
||||
@@ -109,6 +109,10 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
:host(.searchbar-has-focus) .searchbar-cancel-button + .searchbar-search-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
// Searchbar in Toolbar
|
||||
// -----------------------------------------
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Method, Pr
|
||||
|
||||
import { Color, Config, Mode, SearchbarChangeEventDetail } from '../../interface';
|
||||
import { debounceEvent } from '../../utils/helpers';
|
||||
import { sanitizeDOMString } from '../../utils/sanitization';
|
||||
import { createColorClasses } from '../../utils/theme';
|
||||
|
||||
@Component({
|
||||
@@ -80,6 +81,12 @@ export class Searchbar implements ComponentInterface {
|
||||
|
||||
/**
|
||||
* Set the input's placeholder.
|
||||
* `placeholder` can accept either plaintext or HTML as a string.
|
||||
* To display characters normally reserved for HTML, they
|
||||
* must be escaped. For example `<Ionic>` would become
|
||||
* `<Ionic>`
|
||||
*
|
||||
* For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
|
||||
*/
|
||||
@Prop() placeholder = 'Search';
|
||||
|
||||
@@ -288,7 +295,7 @@ export class Searchbar implements ComponentInterface {
|
||||
// Create a dummy span to get the placeholder width
|
||||
const doc = this.doc;
|
||||
const tempSpan = doc.createElement('span');
|
||||
tempSpan.innerHTML = this.placeholder;
|
||||
tempSpan.innerHTML = sanitizeDOMString(this.placeholder) || '';
|
||||
doc.body.appendChild(tempSpan);
|
||||
|
||||
// Get the width of the span then remove it
|
||||
|
||||
@@ -7,10 +7,29 @@ test('searchbar: basic', async () => {
|
||||
|
||||
await page.waitFor(250);
|
||||
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
const compares = [];
|
||||
compares.push(await page.compareScreenshot());
|
||||
|
||||
const searchbar = await page.find('ion-searchbar');
|
||||
let searchbar = await page.find('#basic');
|
||||
await searchbar.callMethod('setFocus');
|
||||
expect(await page.compareScreenshot('focused')).toMatchScreenshot();
|
||||
|
||||
await page.waitFor(250);
|
||||
searchbar = await page.find('#basic');
|
||||
expect(searchbar).toHaveClass('searchbar-has-focus');
|
||||
|
||||
compares.push(await page.compareScreenshot('focused'));
|
||||
|
||||
// No Cancel Button Searchbar
|
||||
searchbar = await page.find('#noCancel');
|
||||
await searchbar.callMethod('setFocus');
|
||||
|
||||
await page.waitFor(250);
|
||||
searchbar = await page.find('#noCancel');
|
||||
expect(searchbar).toHaveClass('searchbar-has-focus');
|
||||
|
||||
compares.push(await page.compareScreenshot('no cancel button, focused'));
|
||||
|
||||
for (const compare of compares) {
|
||||
expect(compare).toMatchScreenshot();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -22,7 +22,11 @@
|
||||
|
||||
<ion-content id="content">
|
||||
<h5 padding-start padding-top> Search - Default </h5>
|
||||
<ion-searchbar value="test" type="tel" show-cancel-button debounce="500">
|
||||
<ion-searchbar id="basic" value="test" type="tel" show-cancel-button debounce="500">
|
||||
</ion-searchbar>
|
||||
|
||||
<h5 padding-start padding-top> Search - No Cancel Button </h5>
|
||||
<ion-searchbar id="noCancel" value="after view" autocorrect="off" autocomplete="off" spellcheck="true" type="text" show-cancel-button="false">
|
||||
</ion-searchbar>
|
||||
|
||||
<h5 padding-start padding-top> Search - Danger </h5>
|
||||
@@ -42,10 +46,6 @@
|
||||
type="number" placeholder="Filter Schedules">
|
||||
</ion-searchbar>
|
||||
|
||||
<h5 padding-start padding-top> Search - No Cancel Button </h5>
|
||||
<ion-searchbar value="after view" autocorrect="off" autocomplete="off" spellcheck="true" type="text" show-cancel-button="false">
|
||||
</ion-searchbar>
|
||||
|
||||
<h5 padding-start padding-top> Search - Custom Cancel Button Danger </h5>
|
||||
<ion-searchbar show-cancel-button cancel-button-text="Really Long Cancel" color="danger">
|
||||
</ion-searchbar>
|
||||
|
||||
@@ -78,6 +78,11 @@
|
||||
<ion-toolbar color="dark">
|
||||
<ion-searchbar show-cancel-button placeholder="Filter Schedules"></ion-searchbar>
|
||||
</ion-toolbar>
|
||||
|
||||
<h5 padding-start padding-top> Search - Dark Toolbar, Primary Search </h5>
|
||||
<ion-toolbar color="dark">
|
||||
<ion-searchbar show-cancel-button color="primary" placeholder="Filter Schedules"></ion-searchbar>
|
||||
</ion-toolbar>
|
||||
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
@@ -138,4 +143,4 @@
|
||||
</style>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
@@ -1,6 +1,6 @@
|
||||
# ion-segment-button
|
||||
|
||||
Segment buttons are groups of related buttons inside of a [Segment](../../segment/Segment). They are displayed in a horizontal row. A segment button can be checked by default by adding the `checked` attribute or by setting the `value` of the segment to the `value` of the segment button. Only one segment button should be selected at a time.
|
||||
Segment buttons are groups of related buttons inside of a [Segment](../segment). They are displayed in a horizontal row. A segment button can be checked by default by adding the `checked` attribute or by setting the `value` of the segment to the `value` of the segment button. Only one segment button should be selected at a time.
|
||||
|
||||
|
||||
<!-- Auto Generated Below -->
|
||||
|
||||
@@ -92,6 +92,16 @@ Their functionality is similar to tabs, where selecting one will deselect all ot
|
||||
</ion-segment-button>
|
||||
</ion-segment>
|
||||
</ion-toolbar>
|
||||
|
||||
<!-- Segment with default selection -->
|
||||
<ion-segment (ionChange)="segmentChanged($event)" value="javascript">
|
||||
<ion-segment-button value="python">
|
||||
<ion-label>Python</ion-label>
|
||||
</ion-segment-button>
|
||||
<ion-segment-button value="javascript">
|
||||
<ion-label>Javascript</ion-label>
|
||||
</ion-segment-button>
|
||||
</ion-segment>
|
||||
```
|
||||
|
||||
```typescript
|
||||
@@ -192,6 +202,16 @@ export class SegmentExample {
|
||||
</ion-segment-button>
|
||||
</ion-segment>
|
||||
</ion-toolbar>
|
||||
|
||||
<!-- Segment with default selection -->
|
||||
<ion-segment value="javascript">
|
||||
<ion-segment-button value="python">
|
||||
<ion-label>Python</ion-label>
|
||||
</ion-segment-button>
|
||||
<ion-segment-button value="javascript">
|
||||
<ion-label>Javascript</ion-label>
|
||||
</ion-segment-button>
|
||||
</ion-segment>
|
||||
```
|
||||
|
||||
```javascript
|
||||
|
||||
@@ -78,6 +78,16 @@
|
||||
</ion-segment-button>
|
||||
</ion-segment>
|
||||
</ion-toolbar>
|
||||
|
||||
<!-- Segment with default selection -->
|
||||
<ion-segment (ionChange)="segmentChanged($event)" value="javascript">
|
||||
<ion-segment-button value="python">
|
||||
<ion-label>Python</ion-label>
|
||||
</ion-segment-button>
|
||||
<ion-segment-button value="javascript">
|
||||
<ion-label>Javascript</ion-label>
|
||||
</ion-segment-button>
|
||||
</ion-segment>
|
||||
```
|
||||
|
||||
```typescript
|
||||
|
||||
@@ -78,6 +78,16 @@
|
||||
</ion-segment-button>
|
||||
</ion-segment>
|
||||
</ion-toolbar>
|
||||
|
||||
<!-- Segment with default selection -->
|
||||
<ion-segment value="javascript">
|
||||
<ion-segment-button value="python">
|
||||
<ion-label>Python</ion-label>
|
||||
</ion-segment-button>
|
||||
<ion-segment-button value="javascript">
|
||||
<ion-label>Javascript</ion-label>
|
||||
</ion-segment-button>
|
||||
</ion-segment>
|
||||
```
|
||||
|
||||
```javascript
|
||||
|
||||
@@ -9,7 +9,7 @@ If `value` is set on the `<ion-select>`, the selected option will be chosen base
|
||||
|
||||
## Interfaces
|
||||
|
||||
By default, select uses the [AlertController API](../../alert/AlertController) to open up the overlay of options in an alert. The interface can be changed to use the [ActionSheetController API](../../action-sheet/ActionSheetController) or [PopoverController API](../../popover/PopoverController) by passing `action-sheet` or `popover`, respectively, to the `interface` property. Read on to the other sections for the limitations of the different interfaces.
|
||||
By default, select uses the [AlertController API](../alert-controller) to open up the overlay of options in an alert. The interface can be changed to use the [ActionSheetController API](../action-sheet-controller) or [PopoverController API](../popover-controller) by passing `action-sheet` or `popover`, respectively, to the `interface` property. Read on to the other sections for the limitations of the different interfaces.
|
||||
|
||||
|
||||
## Single Selection
|
||||
@@ -33,7 +33,7 @@ The `action-sheet` and `popover` interfaces do not have an `OK` button, clicking
|
||||
|
||||
## Interface Options
|
||||
|
||||
Since select uses the alert, action sheet and popover interfaces, options can be passed to these components through the `interfaceOptions` property. This can be used to pass a custom header, subheader, css class, and more. See the [AlertController API docs](../../alert/AlertController/#create), [ActionSheetController API docs](../../action-sheet/ActionSheetController/#create), and [PopoverController API docs](../../popover/PopoverController/#create) for the properties that each interface accepts.
|
||||
Since select uses the alert, action sheet and popover interfaces, options can be passed to these components through the `interfaceOptions` property. This can be used to pass a custom header, subheader, css class, and more. See the [AlertController API docs](../alert-controller), [ActionSheetController API docs](../action-sheet-controller), and [PopoverController API docs](../popover-controller) for the properties that each interface accepts.
|
||||
|
||||
|
||||
<!-- Auto Generated Below -->
|
||||
|
||||
@@ -133,8 +133,21 @@ export class Select implements ComponentInterface {
|
||||
@Listen('ionSelectOptionDidUnload')
|
||||
async selectOptionChanged() {
|
||||
await this.loadOptions();
|
||||
|
||||
if (this.didInit) {
|
||||
this.updateOptions();
|
||||
|
||||
/**
|
||||
* In the event that options
|
||||
* are not loaded at component load
|
||||
* this ensures that any value that is
|
||||
* set is properly rendered once
|
||||
* options have been loaded
|
||||
*/
|
||||
if (this.value !== undefined) {
|
||||
this.el.forceUpdate();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
10
core/src/components/select/test/async/e2e.ts
Normal file
10
core/src/components/select/test/async/e2e.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
test('select: async', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/select/test/async?ionic:_testing=true'
|
||||
});
|
||||
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
});
|
||||
37
core/src/components/select/test/async/index.html
Normal file
37
core/src/components/select/test/async/index.html
Normal file
@@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<html dir="ltr">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Select - Async</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/core.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-select id="animals" placeholder="Select One"></ion-select>
|
||||
|
||||
<script>
|
||||
let select = document.getElementById('animals');
|
||||
const options = ['bird', 'dog', 'shark', 'lizard'];
|
||||
|
||||
setTimeout(() => {
|
||||
|
||||
options.forEach(option => {
|
||||
let o = document.createElement('ion-select-option');
|
||||
o.value = option;
|
||||
o.textContent = option;
|
||||
|
||||
select.appendChild(o);
|
||||
});
|
||||
|
||||
select.value = options[0];
|
||||
|
||||
}, 500);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -12,6 +12,11 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique ipsum ipsum, eget hendrerit erat gravida vitae. Cras condimentum aliquam diam faucibus facilisis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Suspendisse potenti. Duis libero quam, varius ac quam et, pulvinar pretium libero. Suspendisse mollis eros a vehicula pellentesque. Sed viverra eros vel lorem lacinia, lobortis congue dolor dignissim. In congue tincidunt massa, sit amet elementum turpis eleifend in. Nullam laoreet condimentum pharetra. Donec cursus viverra eros nec lacinia. Sed dapibus nulla vel augue efficitur tincidunt. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Fusce suscipit auctor sem et eleifend. Nullam vitae magna ex. Quisque dolor arcu, congue in nisi eu, ullamcorper tincidunt sapien. Praesent sed molestie ante, vitae tincidunt odio.</p>
|
||||
<p>Duis quis rhoncus libero, vitae mollis tortor. Phasellus mollis tellus in auctor ullamcorper. Vivamus eget sagittis arcu, sed egestas dolor. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Integer mi sapien, pharetra ut sapien vulputate, congue dictum libero. Integer dapibus, arcu at ullamcorper tempus, nulla dolor vestibulum arcu, sit amet laoreet libero leo eu turpis. Nulla varius maximus molestie.</p>
|
||||
<p>Phasellus interdum sem ut viverra bibendum. Vivamus vel turpis arcu. Maecenas ultricies pretium dolor id blandit. In nibh sapien, tristique quis purus vitae, placerat sollicitudin ante. Suspendisse venenatis libero rutrum odio ultricies blandit. Proin a fringilla sem. Etiam pretium metus sem, eu lacinia tellus blandit id.</p>
|
||||
<p>Vestibulum aliquam sapien elementum hendrerit viverra. Vivamus vulputate ligula id dui accumsan, at pulvinar mauris lobortis. Integer a ultrices nulla. Cras ultricies sollicitudin erat. Nam nec dolor a enim vestibulum egestas id non dui. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Integer sodales libero congue magna dictum auctor. Duis vitae tellus tempor, tempus dolor semper, dictum eros. Etiam at suscipit lacus. Proin magna ipsum, pulvinar rhoncus vulputate vel, finibus nec nisi. Praesent felis neque, porttitor ac pellentesque et, mattis non lorem. Etiam et ex ultrices, posuere purus id, vestibulum massa.</p>
|
||||
|
||||
<ion-select id="gender" placeholder="Select One">
|
||||
<ion-select-option value="f">Female</ion-select-option>
|
||||
<ion-select-option value="m">Male</ion-select-option>
|
||||
@@ -24,7 +29,8 @@
|
||||
<ion-select-option value="red">Red</ion-select-option>
|
||||
</ion-select>
|
||||
|
||||
<ion-select id="gaming" ok-text="Okay" cancel-text="Dismiss">
|
||||
<ion-select id="gaming" interface="popover" value="popover" ok-text="Okay" cancel-text="Dismiss">
|
||||
<ion-select-option value="popover">Popover</ion-select-option>
|
||||
<ion-select-option value="nes">NES</ion-select-option>
|
||||
<ion-select-option value="n64">Nintendo64</ion-select-option>
|
||||
<ion-select-option value="ps">PlayStation</ion-select-option>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ion-slides
|
||||
|
||||
The Slides component is a multi-section container. Each section can be swiped
|
||||
or dragged between. It contains any number of [Slide](../Slide) components.
|
||||
or dragged between. It contains any number of [Slide](../slide) components.
|
||||
|
||||
|
||||
Adopted from Swiper.js:
|
||||
|
||||
@@ -72,17 +72,17 @@ var win = (typeof window === 'undefined') ? {
|
||||
} : window; // eslint-disable-line
|
||||
|
||||
/**
|
||||
* Dom7 2.1.2
|
||||
* Dom7 2.1.3
|
||||
* Minimalistic JavaScript library for DOM manipulation, with a jQuery-compatible API
|
||||
* http://framework7.io/docs/dom.html
|
||||
*
|
||||
* Copyright 2018, Vladimir Kharlampidi
|
||||
* Copyright 2019, Vladimir Kharlampidi
|
||||
* The iDangero.us
|
||||
* http://www.idangero.us/
|
||||
*
|
||||
* Licensed under MIT
|
||||
*
|
||||
* Released on: September 13, 2018
|
||||
* Released on: February 11, 2019
|
||||
*/
|
||||
|
||||
class Dom7 {
|
||||
@@ -361,6 +361,9 @@ function off(...args) {
|
||||
if (listener && handler.listener === listener) {
|
||||
el.removeEventListener(event, handler.proxyListener, capture);
|
||||
handlers.splice(k, 1);
|
||||
} else if (listener && handler.listener && handler.listener.dom7proxy && handler.listener.dom7proxy === listener) {
|
||||
el.removeEventListener(event, handler.proxyListener, capture);
|
||||
handlers.splice(k, 1);
|
||||
} else if (!listener) {
|
||||
el.removeEventListener(event, handler.proxyListener, capture);
|
||||
handlers.splice(k, 1);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
@import "./tab-bar";
|
||||
@import "../../themes/ionic.globals.ios";
|
||||
@import "../tab-button/tab-button.ios.vars";
|
||||
@import "./tab-bar.ios.vars";
|
||||
|
||||
// iOS Tabs
|
||||
// --------------------------------------------------
|
||||
@@ -20,6 +19,16 @@
|
||||
// --------------------------------------------------
|
||||
|
||||
:host(.tab-bar-translucent) {
|
||||
background-color: #{current-color(base, .8)};
|
||||
backdrop-filter: saturate(210%) blur(20px);
|
||||
--background: #{$tab-bar-ios-translucent-background-color};
|
||||
backdrop-filter: $tab-bar-ios-translucent-filter;
|
||||
}
|
||||
|
||||
// iOS Translucent Tab bar with Color
|
||||
:host(.ion-color.tab-bar-translucent) {
|
||||
background: #{current-color(base, $tab-bar-ios-translucent-background-color-alpha)};
|
||||
}
|
||||
|
||||
// iOS Translucent Tab bar button
|
||||
:host(.tab-bar-translucent) ::slotted(ion-tab-button) {
|
||||
background: transparent;
|
||||
}
|
||||
14
core/src/components/tab-bar/tab-bar.ios.vars.scss
Normal file
14
core/src/components/tab-bar/tab-bar.ios.vars.scss
Normal file
@@ -0,0 +1,14 @@
|
||||
@import "../../themes/ionic.globals.ios";
|
||||
@import "../tab-button/tab-button.ios.vars";
|
||||
|
||||
// iOS Tab Bar
|
||||
// --------------------------------------------------
|
||||
|
||||
/// @prop - Alpha of translucent tab bar background color
|
||||
$tab-bar-ios-translucent-background-color-alpha: .8 !default;
|
||||
|
||||
/// @prop - Translucent tab bar background color
|
||||
$tab-bar-ios-translucent-background-color: rgba($background-color-rgb, $tab-bar-ios-translucent-background-color-alpha) !default;
|
||||
|
||||
/// @prop - Filter of the translucent tab bar background color
|
||||
$tab-bar-ios-translucent-filter: saturate(210%) blur(20px) !default;
|
||||
@@ -12,3 +12,7 @@
|
||||
|
||||
height: 56px;
|
||||
}
|
||||
|
||||
:host(.tab-bar-translucent) ::slotted(ion-tab-button) {
|
||||
background: transparent;
|
||||
}
|
||||
@@ -146,6 +146,44 @@
|
||||
<ion-icon name="calendar"></ion-icon>
|
||||
</ion-tab-button>
|
||||
</ion-tab-bar>
|
||||
|
||||
<!-- Translucent -->
|
||||
<ion-tab-bar translucent>
|
||||
<ion-tab-button tab="1">
|
||||
<ion-icon name="heart"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="2">
|
||||
<ion-icon name="musical-note"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="3">
|
||||
<ion-icon name="today"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="4">
|
||||
<ion-icon name="calendar"></ion-icon>
|
||||
</ion-tab-button>
|
||||
</ion-tab-bar>
|
||||
|
||||
<!-- Translucent / Custom Color -->
|
||||
<ion-tab-bar translucent color="dark">
|
||||
<ion-tab-button tab="1">
|
||||
<ion-icon name="heart"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="2">
|
||||
<ion-icon name="musical-note"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="3">
|
||||
<ion-icon name="today"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="4">
|
||||
<ion-icon name="calendar"></ion-icon>
|
||||
</ion-tab-button>
|
||||
</ion-tab-bar>
|
||||
|
||||
<style>
|
||||
body {
|
||||
|
||||
10
core/src/components/tab-bar/test/translucent/e2e.ts
Normal file
10
core/src/components/tab-bar/test/translucent/e2e.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
test('tab-bar: translucent', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/tab-bar/test/translucent?ionic:_testing=true'
|
||||
});
|
||||
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
});
|
||||
213
core/src/components/tab-bar/test/translucent/index.html
Normal file
213
core/src/components/tab-bar/test/translucent/index.html
Normal file
@@ -0,0 +1,213 @@
|
||||
<!DOCTYPE html>
|
||||
<html dir="ltr">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Tab Bar - Spec</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script src="../../../../../dist/ionic.js"></script>
|
||||
<link rel="stylesheet" href="../../../../../css/ionic.bundle.css">
|
||||
<link rel="stylesheet" href="../../../../../scripts/testing/styles.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Default -->
|
||||
<ion-tab-bar translucent selected-tab="1">
|
||||
|
||||
<ion-tab-button tab="1">
|
||||
<ion-label>Recents</ion-label>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="2">
|
||||
<ion-label>Favorites</ion-label>
|
||||
<ion-badge>6</ion-badge>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="3">
|
||||
<ion-label>Settings</ion-label>
|
||||
</ion-tab-button>
|
||||
|
||||
</ion-tab-bar>
|
||||
|
||||
<!-- Badges -->
|
||||
<ion-tab-bar translucent selected-tab="1">
|
||||
<ion-tab-button tab="1">
|
||||
<ion-icon name="heart"></ion-icon>
|
||||
<ion-label>Item One Max</ion-label>
|
||||
<ion-badge color="danger"></ion-badge>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="2">
|
||||
<ion-icon name="musical-note"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="3">
|
||||
<ion-icon name="today"></ion-icon>
|
||||
<ion-badge color="danger">88</ion-badge>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="4">
|
||||
<ion-icon name="calendar"></ion-icon>
|
||||
<ion-badge color="danger">888+</ion-badge>
|
||||
</ion-tab-button>
|
||||
</ion-tab-bar>
|
||||
|
||||
<ion-tab-bar translucent selected-tab="1">
|
||||
<ion-tab-button tab="1">
|
||||
<ion-icon name="musical-note"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="2" layout="icon-bottom">
|
||||
<ion-icon name="heart"></ion-icon>
|
||||
<ion-label>Item One Max</ion-label>
|
||||
<ion-badge color="danger"></ion-badge>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="3">
|
||||
<ion-icon name="today"></ion-icon>
|
||||
<ion-badge color="danger">88</ion-badge>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="4">
|
||||
<ion-icon name="calendar"></ion-icon>
|
||||
<ion-badge color="danger">888+</ion-badge>
|
||||
</ion-tab-button>
|
||||
</ion-tab-bar>
|
||||
|
||||
<ion-tab-bar translucent selected-tab="1">
|
||||
<ion-tab-button tab="1">
|
||||
<ion-icon name="heart"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="2">
|
||||
<ion-icon name="musical-note"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="3">
|
||||
<ion-icon name="today"></ion-icon>
|
||||
</ion-tab-button>
|
||||
</ion-tab-bar>
|
||||
|
||||
<ion-tab-bar translucent selected-tab="1">
|
||||
<ion-tab-button tab="1">
|
||||
<ion-icon name="musical-note"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="2" layout="icon-bottom">
|
||||
<ion-icon name="heart"></ion-icon>
|
||||
<ion-label>Item One Max</ion-label>
|
||||
<ion-badge color="danger">88</ion-badge>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="3">
|
||||
<ion-icon name="today"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="4">
|
||||
<ion-icon name="calendar"></ion-icon>
|
||||
</ion-tab-button>
|
||||
</ion-tab-bar>
|
||||
|
||||
<!-- Custom Height -->
|
||||
<ion-tab-bar translucent color="danger" selected-tab="3" class="custom-height">
|
||||
<ion-tab-button tab="1">
|
||||
<ion-icon name="heart"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="2">
|
||||
<ion-icon name="musical-note"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="3">
|
||||
<ion-icon name="today"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="4">
|
||||
<ion-icon name="calendar"></ion-icon>
|
||||
</ion-tab-button>
|
||||
</ion-tab-bar>
|
||||
|
||||
<!-- Custom Height / Alignment -->
|
||||
<ion-tab-bar translucent color="tertiary" selected-tab="3" class="custom-height custom-top">
|
||||
<ion-tab-button tab="1">
|
||||
<ion-icon name="heart"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="2">
|
||||
<ion-icon name="musical-note"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="3">
|
||||
<ion-icon name="today"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="4">
|
||||
<ion-icon name="calendar"></ion-icon>
|
||||
</ion-tab-button>
|
||||
</ion-tab-bar>
|
||||
|
||||
<!-- Translucent -->
|
||||
<ion-tab-bar translucent>
|
||||
<ion-tab-button tab="1">
|
||||
<ion-icon name="heart"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="2">
|
||||
<ion-icon name="musical-note"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="3">
|
||||
<ion-icon name="today"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="4">
|
||||
<ion-icon name="calendar"></ion-icon>
|
||||
</ion-tab-button>
|
||||
</ion-tab-bar>
|
||||
|
||||
<!-- Translucent / Custom Color -->
|
||||
<ion-tab-bar translucent class="custom-background">
|
||||
<ion-tab-button tab="1">
|
||||
<ion-icon name="heart"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="2">
|
||||
<ion-icon name="musical-note"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="3">
|
||||
<ion-icon name="today"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="4">
|
||||
<ion-icon name="calendar"></ion-icon>
|
||||
</ion-tab-button>
|
||||
</ion-tab-bar>
|
||||
|
||||
<style>
|
||||
body {
|
||||
background: linear-gradient(
|
||||
to right, orange , yellow, green, cyan, blue, violet
|
||||
);
|
||||
}
|
||||
|
||||
ion-tab-bar {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.custom-height {
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.custom-top ion-tab-button {
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.custom-background {
|
||||
--background: rgba(124, 243, 217, 0.6);
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -217,6 +217,15 @@ Type: `Promise<boolean>`
|
||||
|
||||
|
||||
|
||||
## Slots
|
||||
|
||||
| Slot | Description |
|
||||
| ---------- | --------------------------------------------------------------------- |
|
||||
| | Content is placed between the named slots if provided without a slot. |
|
||||
| `"bottom"` | Content is placed at the bottom of the screen. |
|
||||
| `"top"` | Content is placed at the top of the screen. |
|
||||
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
*Built with [StencilJS](https://stenciljs.com/)*
|
||||
|
||||
@@ -2,6 +2,11 @@ import { Component, Element, Event, EventEmitter, Listen, Method, Prop, State }
|
||||
|
||||
import { Config, NavOutlet, RouteID, RouteWrite, TabButtonClickEventDetail } from '../../interface';
|
||||
|
||||
/**
|
||||
* @slot - Content is placed between the named slots if provided without a slot.
|
||||
* @slot top - Content is placed at the top of the screen.
|
||||
* @slot bottom - Content is placed at the bottom of the screen.
|
||||
*/
|
||||
@Component({
|
||||
tag: 'ion-tabs',
|
||||
styleUrl: 'tabs.scss',
|
||||
|
||||
@@ -351,6 +351,17 @@ In `md` mode, the `<ion-header>` will receive a box-shadow on the bottom, and th
|
||||
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` |
|
||||
|
||||
|
||||
## Slots
|
||||
|
||||
| Slot | Description |
|
||||
| ------------- | -------------------------------------------------------------------------------------------------------- |
|
||||
| | Content is placed between the named slots if provided without a slot. |
|
||||
| `"end"` | Content is placed to the left of the toolbar text in LTR, and to the right in RTL. |
|
||||
| `"primary"` | Content is placed to the right of the toolbar text in `ios` mode, and to the far right in `md` mode. |
|
||||
| `"secondary"` | Content is placed to the left of the toolbar text in `ios` mode, and directly to the right in `md` mode. |
|
||||
| `"start"` | Content is placed to the left of the toolbar text in LTR, and to the right in RTL. |
|
||||
|
||||
|
||||
## CSS Custom Properties
|
||||
|
||||
| Name | Description |
|
||||
|
||||
@@ -3,6 +3,13 @@ import { Component, ComponentInterface, Element, Listen, Prop } from '@stencil/c
|
||||
import { Color, Config, CssClassMap, Mode, StyleEventDetail } from '../../interface';
|
||||
import { createColorClasses } from '../../utils/theme';
|
||||
|
||||
/**
|
||||
* @slot - Content is placed between the named slots if provided without a slot.
|
||||
* @slot start - Content is placed to the left of the toolbar text in LTR, and to the right in RTL.
|
||||
* @slot secondary - Content is placed to the left of the toolbar text in `ios` mode, and directly to the right in `md` mode.
|
||||
* @slot primary - Content is placed to the right of the toolbar text in `ios` mode, and to the far right in `md` mode.
|
||||
* @slot end - Content is placed to the left of the toolbar text in LTR, and to the right in RTL.
|
||||
*/
|
||||
@Component({
|
||||
tag: 'ion-toolbar',
|
||||
styleUrls: {
|
||||
|
||||
@@ -83,7 +83,7 @@ scrolling. In order to better control images, Ionic provides `<ion-img>`
|
||||
to manage HTTP requests and image rendering. While scrolling through items
|
||||
quickly, `<ion-img>` knows when and when not to make requests, when and
|
||||
when not to render images, and only loads the images that are viewable
|
||||
after scrolling. [Read more about `ion-img`.](../../img/Img/)
|
||||
after scrolling. [Read more about `ion-img`.](../img)
|
||||
|
||||
It's also important for app developers to ensure image sizes are locked in,
|
||||
and after images have fully loaded they do not change size and affect any
|
||||
@@ -132,7 +132,7 @@ dimensions are measured correctly.
|
||||
#### iOS Cordova WKWebView
|
||||
|
||||
When deploying to iOS with Cordova, it's highly recommended to use the
|
||||
[WKWebView plugin](http://blog.ionic.io/cordova-ios-performance-improvements-drop-in-speed-with-wkwebview/)
|
||||
[WKWebView plugin](https://blog.ionicframework.com/cordova-ios-performance-improvements-drop-in-speed-with-wkwebview/)
|
||||
in order to take advantage of iOS's higher performing webview. Additionally,
|
||||
WKWebView is superior at scrolling efficiently in comparison to the older
|
||||
UIWebView.
|
||||
|
||||
@@ -168,10 +168,10 @@ export interface IonicConfig {
|
||||
// PRIVATE configs
|
||||
keyboardHeight?: number;
|
||||
inputShims?: boolean;
|
||||
scrollPadding?: string;
|
||||
inputBlurring?: string;
|
||||
scrollPadding?: boolean;
|
||||
inputBlurring?: boolean;
|
||||
scrollAssist?: boolean;
|
||||
hideCaretOnScroll?: string;
|
||||
hideCaretOnScroll?: boolean;
|
||||
|
||||
// INTERNAL configs
|
||||
persistConfig?: boolean;
|
||||
|
||||
@@ -11,6 +11,7 @@ export const PLATFORMS_MAP = {
|
||||
'electron': isElectron,
|
||||
'pwa': isPWA,
|
||||
'mobile': isMobile,
|
||||
'mobileweb': isMobileWeb,
|
||||
'desktop': isDesktop,
|
||||
'hybrid': isHybrid
|
||||
};
|
||||
@@ -37,6 +38,10 @@ export function setupPlatforms(win: any) {
|
||||
return platforms;
|
||||
}
|
||||
|
||||
function isMobileWeb(win: Window): boolean {
|
||||
return isMobile(win) && !isHybrid(win);
|
||||
}
|
||||
|
||||
function detectPlatforms(win: Window): string[] {
|
||||
return Object.keys(PLATFORMS_MAP).filter(p => (PLATFORMS_MAP as any)[p](win));
|
||||
}
|
||||
@@ -104,10 +109,10 @@ function isElectron(win: Window): boolean {
|
||||
}
|
||||
|
||||
function isPWA(win: Window): boolean {
|
||||
return win.matchMedia('(display-mode: standalone)').matches;
|
||||
return win.matchMedia('(display-mode: standalone)').matches || (win.navigator as any).standalone;
|
||||
}
|
||||
|
||||
function testUserAgent(win: Window, expr: RegExp) {
|
||||
export function testUserAgent(win: Window, expr: RegExp) {
|
||||
return expr.test(win.navigator.userAgent);
|
||||
}
|
||||
|
||||
|
||||
126
core/src/utils/sanitization/index.ts
Normal file
126
core/src/utils/sanitization/index.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* Does a simple sanitization of all elements
|
||||
* in an untrusted string
|
||||
*/
|
||||
|
||||
export const sanitizeDOMString = (untrustedString: string | undefined): string | undefined => {
|
||||
try {
|
||||
if (typeof untrustedString !== 'string' || untrustedString === '') { return untrustedString; }
|
||||
|
||||
/**
|
||||
* Create a document fragment
|
||||
* separate from the main DOM,
|
||||
* create a div to do our work in
|
||||
*/
|
||||
const documentFragment = document.createDocumentFragment();
|
||||
const workingDiv = document.createElement('div');
|
||||
documentFragment.appendChild(workingDiv);
|
||||
workingDiv.innerHTML = untrustedString;
|
||||
|
||||
/**
|
||||
* Remove any elements
|
||||
* that are blocked
|
||||
*/
|
||||
blockedTags.forEach(blockedTag => {
|
||||
|
||||
const getElementsToRemove = documentFragment.querySelectorAll(blockedTag);
|
||||
for (let elementIndex = getElementsToRemove.length - 1; elementIndex >= 0; elementIndex--) {
|
||||
const element = getElementsToRemove[elementIndex];
|
||||
if (element.parentNode) {
|
||||
element.parentNode.removeChild(element);
|
||||
} else {
|
||||
documentFragment.removeChild(element);
|
||||
}
|
||||
|
||||
/**
|
||||
* We still need to sanitize
|
||||
* the children of this element
|
||||
* as they are left behind
|
||||
*/
|
||||
const childElements = getElementChildren(element);
|
||||
|
||||
/* tslint:disable-next-line */
|
||||
for (let childIndex = 0; childIndex < childElements.length; childIndex++) {
|
||||
sanitizeElement(childElements[childIndex]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Go through remaining elements and remove
|
||||
* non-allowed attribs
|
||||
*/
|
||||
|
||||
// IE does not support .children on document fragments, only .childNodes
|
||||
const documentFragmentChildren = getElementChildren(documentFragment);
|
||||
|
||||
/* tslint:disable-next-line */
|
||||
for (let childIndex = 0; childIndex < documentFragmentChildren.length; childIndex++) {
|
||||
sanitizeElement(documentFragmentChildren[childIndex]);
|
||||
}
|
||||
|
||||
// Append document fragment to div
|
||||
const fragmentDiv = document.createElement('div');
|
||||
fragmentDiv.appendChild(documentFragment);
|
||||
|
||||
// First child is always the div we did our work in
|
||||
const getInnerDiv = fragmentDiv.querySelector('div');
|
||||
return (getInnerDiv !== null) ? getInnerDiv.innerHTML : fragmentDiv.innerHTML;
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clean up current element based on allowed attributes
|
||||
* and then recursively dig down into any child elements to
|
||||
* clean those up as well
|
||||
*/
|
||||
const sanitizeElement = (element: any) => {
|
||||
// IE uses childNodes, so ignore nodes that are not elements
|
||||
if (element.nodeType && element.nodeType !== 1) { return; }
|
||||
|
||||
for (let i = element.attributes.length - 1; i >= 0; i--) {
|
||||
const attribute = element.attributes[i];
|
||||
const attributeName = attribute.name;
|
||||
|
||||
// remove non-allowed attribs
|
||||
if (!allowedAttributes.includes(attributeName.toLowerCase())) {
|
||||
element.removeAttribute(attributeName);
|
||||
continue;
|
||||
}
|
||||
|
||||
// clean up any allowed attribs
|
||||
// that attempt to do any JS funny-business
|
||||
const attributeValue = attribute.value;
|
||||
|
||||
/* tslint:disable-next-line */
|
||||
if (attributeValue != null && attributeValue.toLowerCase().includes('javascript:')) {
|
||||
element.removeAttribute(attributeName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize any nested children
|
||||
*/
|
||||
const childElements = getElementChildren(element);
|
||||
|
||||
/* tslint:disable-next-line */
|
||||
for (let i = 0; i < childElements.length; i++) {
|
||||
sanitizeElement(childElements[i]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* IE doesn't always support .children
|
||||
* so we revert to .childNodes instead
|
||||
*/
|
||||
const getElementChildren = (element: any) => {
|
||||
return (element.children != null) ? element.children : element.childNodes;
|
||||
};
|
||||
|
||||
const allowedAttributes = ['class', 'id', 'href', 'src'];
|
||||
const blockedTags = ['script', 'style', 'iframe', 'meta', 'link', 'object', 'embed'];
|
||||
26
core/src/utils/sanitization/test/e2e.ts
Normal file
26
core/src/utils/sanitization/test/e2e.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
test('sanitization:', async done => {
|
||||
|
||||
const page = await newE2EPage({
|
||||
url: '/src/utils/sanitization/test?ionic:_testing=true'
|
||||
});
|
||||
|
||||
page.on('pageerror', (err: any) => {
|
||||
if (err.message.includes('sanitizeFailed')) {
|
||||
done.fail(new Error('Failed to properly sanitize'));
|
||||
}
|
||||
});
|
||||
|
||||
await page.click('#testA');
|
||||
await page.click('#testB');
|
||||
await page.click('#testC');
|
||||
await page.click('#testD');
|
||||
await page.click('#testE');
|
||||
await page.click('#testF');
|
||||
await page.click('#testG');
|
||||
await page.click('#testH');
|
||||
|
||||
done();
|
||||
|
||||
});
|
||||
113
core/src/utils/sanitization/test/index.html
Normal file
113
core/src/utils/sanitization/test/index.html
Normal file
@@ -0,0 +1,113 @@
|
||||
<!DOCTYPE html>
|
||||
<html dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Sanitization</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>
|
||||
<script type="module">
|
||||
import { sanitizeDOMString } from '../../../../dist/collection/utils/sanitization/index.js';
|
||||
window.sanitizeDOMString = sanitizeDOMString;
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<!--
|
||||
So the goal with these tests is to ensure
|
||||
that an `alert` call is never executed
|
||||
in the browser. If it is, then our sanitization
|
||||
function has failed to properly sanitize
|
||||
an input
|
||||
-->
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Sanitization</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content id="content" padding>
|
||||
<div class="results">Results will appear here</div>
|
||||
|
||||
<ion-button onclick="testA()" id="testA">Test A</ion-button>
|
||||
<ion-button onclick="testB()" id="testB">Test B</ion-button>
|
||||
<ion-button onclick="testC()" id="testC">Test C</ion-button>
|
||||
<ion-button onclick="testD()" id="testD">Test D</ion-button>
|
||||
<ion-button onclick="testE()" id="testE">Test E</ion-button>
|
||||
<ion-button onclick="testF()" id="testF">Test F</ion-button>
|
||||
<ion-button onclick="testG()" id="testG">Test G</ion-button>
|
||||
<ion-button onclick="testH()" id="testH">Test H</ion-button>
|
||||
</ion-content>
|
||||
|
||||
</ion-app>
|
||||
|
||||
<script>
|
||||
const results = document.querySelector('.results');
|
||||
|
||||
window.alert = () => {
|
||||
throw new Error('sanitizeFailed');
|
||||
}
|
||||
|
||||
function runTest(inputString) {
|
||||
console.log(`Sanitizing ${inputString}`);
|
||||
const sanitizedResult = sanitizeDOMString(inputString);
|
||||
|
||||
results.innerHTML = sanitizedResult;
|
||||
console.log(`Result ${sanitizedResult}`)
|
||||
}
|
||||
|
||||
function testA() {
|
||||
runTest('<img src="x" onerror="alert(document.cookie);">');
|
||||
}
|
||||
|
||||
function testB() {
|
||||
runTest('<button id="myButton" name="myButton" onclick="alert(document.cookie);">harmless button</button>');
|
||||
|
||||
const buttom = results.querySelector('button');
|
||||
buttom.click();
|
||||
}
|
||||
|
||||
function testC() {
|
||||
runTest('<a href="javascript:alert(document.cookie)">harmless link</a>');
|
||||
|
||||
const link = results.querySelector('a');
|
||||
link.click();
|
||||
}
|
||||
|
||||
function testD() {
|
||||
runTest('<a class="link" href="Javascript:alert(document.cookie)">harmless link</a>');
|
||||
|
||||
const link = results.querySelector('a');
|
||||
link.click();
|
||||
}
|
||||
|
||||
function testE() {
|
||||
runTest('<iframe src="javascript:alert(document.cookie)"></iframe>');
|
||||
}
|
||||
|
||||
function testF() {
|
||||
runTest('<div><button><a href="javascript:alert(document.cookie)">click me</a></button></div>');
|
||||
|
||||
const link = results.querySelector('a');
|
||||
link.click();
|
||||
}
|
||||
|
||||
function testG() {
|
||||
runTest('<object><img src="x" onerror="alert(document.cookie);"></object>');
|
||||
}
|
||||
|
||||
function testH() {
|
||||
runTest('<ion-item><ion-label>Hello!</ion-label><ion-button onclick="alert(document.cookie);">Click me</ion-button>');
|
||||
|
||||
const button = results.querySelector('ion-button');
|
||||
button.click();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user