mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
Compare commits
139 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0ff24b5d6 | ||
|
|
c8a3999da1 | ||
|
|
67617fbc0f | ||
|
|
c877061a32 | ||
|
|
2f54bc1469 | ||
|
|
2a253a1d33 | ||
|
|
4ce62b26a8 | ||
|
|
e4bf052794 | ||
|
|
630848ae5c | ||
|
|
f94e618a7b | ||
|
|
de58238333 | ||
|
|
1a2fd2fb6e | ||
|
|
a1f95264e7 | ||
|
|
e256d3f09f | ||
|
|
e828a9a693 | ||
|
|
dcf5317310 | ||
|
|
f022464cf8 | ||
|
|
38eb378d66 | ||
|
|
a2763afe8e | ||
|
|
943e3f6ae3 | ||
|
|
9282aa6871 | ||
|
|
b83e00934e | ||
|
|
dd1c8dbf3b | ||
|
|
9486e51f6d | ||
|
|
fb631742af | ||
|
|
0101a5fce2 | ||
|
|
06d4c8e6f1 | ||
|
|
cd8ffd82a0 | ||
|
|
b6b2714d70 | ||
|
|
8c1646a105 | ||
|
|
68a9b80053 | ||
|
|
26285bbc91 | ||
|
|
215eb5d4ef | ||
|
|
677d55ebe4 | ||
|
|
a4c38aca88 | ||
|
|
5300dcc693 | ||
|
|
b064fdebef | ||
|
|
c05476b88e | ||
|
|
0de75afbef | ||
|
|
19d63f6243 | ||
|
|
343f855147 | ||
|
|
da1b7a0e7a | ||
|
|
3a0465e7d6 | ||
|
|
8cb2ea6804 | ||
|
|
d46e074fb1 | ||
|
|
cb2c9b63e8 | ||
|
|
ff0f1da9f1 | ||
|
|
487349f02a | ||
|
|
b6b2d34fd4 | ||
|
|
1a5accc5f7 | ||
|
|
f7d4c21b64 | ||
|
|
fa515eb79b | ||
|
|
697bcf3420 | ||
|
|
bc17e24a03 | ||
|
|
ab1fc8f231 | ||
|
|
2a3ce9a74e | ||
|
|
e2d8e5c4dc | ||
|
|
7ecae2e4cb | ||
|
|
14e8441706 | ||
|
|
e1d6627bf0 | ||
|
|
348c50b7ea | ||
|
|
9e9a372497 | ||
|
|
64719f49f9 | ||
|
|
3d6ac1382e | ||
|
|
9a02ec8402 | ||
|
|
fbb777ab5a | ||
|
|
67b0853b28 | ||
|
|
e3a05bfeb5 | ||
|
|
5c27dd8032 | ||
|
|
b768181307 | ||
|
|
f891f66708 | ||
|
|
a01bdb8c8d | ||
|
|
09a5ed6a0d | ||
|
|
39bbb99f75 | ||
|
|
11a59133d9 | ||
|
|
1ef112ef12 | ||
|
|
78740d0dfc | ||
|
|
94518c9387 | ||
|
|
31f45cdcc9 | ||
|
|
61cf0c534e | ||
|
|
8d5ed47a28 | ||
|
|
a94e2a87fb | ||
|
|
818e387fe8 | ||
|
|
ff39d40255 | ||
|
|
91aaaab71a | ||
|
|
01afdc42e5 | ||
|
|
36939e10ae | ||
|
|
5ed73cdf4d | ||
|
|
0be79fe82b | ||
|
|
216f51b12a | ||
|
|
dc9faa6a0f | ||
|
|
4f4f31b65e | ||
|
|
9d04c127e8 | ||
|
|
181fc59ab7 | ||
|
|
53ec9cff5e | ||
|
|
d61456c46d | ||
|
|
07868354aa | ||
|
|
5275332e43 | ||
|
|
ea52db66f0 | ||
|
|
c45c8d5564 | ||
|
|
afcc46e1cc | ||
|
|
4e23aad3d9 | ||
|
|
a664ccbde9 | ||
|
|
8002114e72 | ||
|
|
04b874e32a | ||
|
|
c727419350 | ||
|
|
e1e23bc1a2 | ||
|
|
cdc2fb652f | ||
|
|
bb519b8724 | ||
|
|
1956f98968 | ||
|
|
3ed44993e1 | ||
|
|
f4ecc32c14 | ||
|
|
d5eb3a44e6 | ||
|
|
33768e1d0c | ||
|
|
ce4a381d4f | ||
|
|
2d878fc4f6 | ||
|
|
65bc99577c | ||
|
|
abad12fbdb | ||
|
|
d77a9d57ec | ||
|
|
813611a61b | ||
|
|
96d6012071 | ||
|
|
7214a8401b | ||
|
|
bf3f3bb3dc | ||
|
|
3a6fcc7d8b | ||
|
|
62dd16a5ee | ||
|
|
0956f8bc55 | ||
|
|
a4a64530ff | ||
|
|
e17c822bfb | ||
|
|
f5b0299729 | ||
|
|
1267945480 | ||
|
|
907cc7b159 | ||
|
|
e76f79d054 | ||
|
|
f130fb2b17 | ||
|
|
6b817f26b0 | ||
|
|
c356526520 | ||
|
|
caa3afa613 | ||
|
|
0a0cbd8f2a | ||
|
|
639314ab21 | ||
|
|
7512c90241 |
@@ -185,7 +185,7 @@ jobs:
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: npm install --legacy-peer-deps
|
||||
command: npm install
|
||||
working_directory: /tmp/workspace/packages/vue
|
||||
- run:
|
||||
command: sudo npm link
|
||||
@@ -208,13 +208,7 @@ jobs:
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: npm install --legacy-peer-deps
|
||||
working_directory: /tmp/workspace/packages/vue-router
|
||||
- run:
|
||||
command: sudo npm link
|
||||
working_directory: /tmp/workspace/core
|
||||
- run:
|
||||
command: sudo npm link @ionic/core
|
||||
command: npm install
|
||||
working_directory: /tmp/workspace/packages/vue-router
|
||||
- run:
|
||||
command: sudo npm link
|
||||
@@ -442,8 +436,11 @@ workflows:
|
||||
requires: [puppeteer-dependencies, build-core]
|
||||
- test-core-spec:
|
||||
requires: [build-core]
|
||||
- test-core-treeshake:
|
||||
requires: [build-core]
|
||||
# Adam requested we skip this test for now
|
||||
# since it is failing on ES5 code which
|
||||
# will be removed in Ionic Framework v6
|
||||
#- test-core-treeshake:
|
||||
# requires: [build-core]
|
||||
- test-core-screenshot:
|
||||
requires: [build-core]
|
||||
filters:
|
||||
|
||||
262
.github/COMPONENT-GUIDE.md
vendored
262
.github/COMPONENT-GUIDE.md
vendored
@@ -9,6 +9,9 @@
|
||||
* [Ripple Effect](#ripple-effect)
|
||||
* [Example Components](#example-components)
|
||||
* [References](#references)
|
||||
- [Accessibility](#accessibility)
|
||||
* [Checkbox](#checkbox)
|
||||
* [Switch](#switch)
|
||||
- [Rendering Anchor or Button](#rendering-anchor-or-button)
|
||||
* [Example Components](#example-components-1)
|
||||
* [Component Structure](#component-structure-1)
|
||||
@@ -362,6 +365,265 @@ ion-ripple-effect {
|
||||
- [iOS Buttons](https://developer.apple.com/design/human-interface-guidelines/ios/controls/buttons/)
|
||||
|
||||
|
||||
## Accessibility
|
||||
|
||||
### Checkbox
|
||||
|
||||
#### Example Components
|
||||
|
||||
- [ion-checkbox](https://github.com/ionic-team/ionic/tree/master/core/src/components/checkbox)
|
||||
|
||||
#### VoiceOver
|
||||
|
||||
In order for VoiceOver to work properly with a checkbox component there must be a native `input` with `type="checkbox"`, and `aria-checked` and `role="checkbox"` **must** be on the host element. The `aria-hidden` attribute needs to be added if the checkbox is disabled, preventing iOS users from selecting it:
|
||||
|
||||
```tsx
|
||||
render() {
|
||||
const { checked, disabled } = this;
|
||||
|
||||
return (
|
||||
<Host
|
||||
aria-checked={`${checked}`}
|
||||
aria-hidden={disabled ? 'true' : null}
|
||||
role="checkbox"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
/>
|
||||
...
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### NVDA
|
||||
|
||||
It is required to have `aria-checked` on the native input for checked to read properly and `disabled` to prevent tabbing to the input:
|
||||
|
||||
```tsx
|
||||
render() {
|
||||
const { checked, disabled } = this;
|
||||
|
||||
return (
|
||||
<Host
|
||||
aria-checked={`${checked}`}
|
||||
aria-hidden={disabled ? 'true' : null}
|
||||
role="checkbox"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
aria-checked={`${checked}`}
|
||||
disabled={disabled}
|
||||
/>
|
||||
...
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### Labels
|
||||
|
||||
A helper function has been created to get the proper `aria-label` for the checkbox. This can be imported as `getAriaLabel` like the following:
|
||||
|
||||
```tsx
|
||||
const { label, labelId, labelText } = getAriaLabel(el, inputId);
|
||||
```
|
||||
|
||||
where `el` and `inputId` are the following:
|
||||
|
||||
```tsx
|
||||
export class Checkbox implements ComponentInterface {
|
||||
private inputId = `ion-cb-${checkboxIds++}`;
|
||||
|
||||
@Element() el!: HTMLElement;
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
This can then be added to the `Host` like the following:
|
||||
|
||||
```tsx
|
||||
<Host
|
||||
aria-labelledby={label ? labelId : null}
|
||||
aria-checked={`${checked}`}
|
||||
aria-hidden={disabled ? 'true' : null}
|
||||
role="checkbox"
|
||||
>
|
||||
```
|
||||
|
||||
In addition to that, the checkbox input should have a label added:
|
||||
|
||||
```tsx
|
||||
<Host
|
||||
aria-labelledby={label ? labelId : null}
|
||||
aria-checked={`${checked}`}
|
||||
aria-hidden={disabled ? 'true' : null}
|
||||
role="checkbox"
|
||||
>
|
||||
<label htmlFor={inputId}>
|
||||
{labelText}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
aria-checked={`${checked}`}
|
||||
disabled={disabled}
|
||||
id={inputId}
|
||||
/>
|
||||
```
|
||||
|
||||
#### Hidden Input
|
||||
|
||||
A helper function to render a hidden input has been added, it can be added in the `render`:
|
||||
|
||||
```tsx
|
||||
renderHiddenInput(true, el, name, (checked ? value : ''), disabled);
|
||||
```
|
||||
|
||||
> This is required for the checkbox to work with forms.
|
||||
|
||||
#### Known Issues
|
||||
|
||||
When using VoiceOver on macOS, Chrome will announce the following when you are focused on a checkbox:
|
||||
|
||||
```
|
||||
currently on a checkbox inside of a checkbox
|
||||
```
|
||||
|
||||
This is a compromise we have to make in order for it to work with the other screen readers & Safari.
|
||||
|
||||
|
||||
### Switch
|
||||
|
||||
#### Example Components
|
||||
|
||||
- [ion-toggle](https://github.com/ionic-team/ionic/tree/master/core/src/components/toggle)
|
||||
|
||||
#### Voiceover
|
||||
|
||||
In order for VoiceOver to work properly with a switch component there must be a native `input` with `type="checkbox"` and `role="switch"`, and `aria-checked` and `role="switch"` **must** be on the host element. The `aria-hidden` attribute needs to be added if the switch is disabled, preventing iOS users from selecting it:
|
||||
|
||||
```tsx
|
||||
render() {
|
||||
const { checked, disabled } = this;
|
||||
|
||||
return (
|
||||
<Host
|
||||
aria-checked={`${checked}`}
|
||||
aria-hidden={disabled ? 'true' : null}
|
||||
role="switch"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
/>
|
||||
...
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### NVDA
|
||||
|
||||
It is required to have `aria-checked` on the native input for checked to read properly and `disabled` to prevent tabbing to the input:
|
||||
|
||||
```tsx
|
||||
render() {
|
||||
const { checked, disabled } = this;
|
||||
|
||||
return (
|
||||
<Host
|
||||
aria-checked={`${checked}`}
|
||||
aria-hidden={disabled ? 'true' : null}
|
||||
role="switch"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
aria-checked={`${checked}`}
|
||||
disabled={disabled}
|
||||
/>
|
||||
...
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### Labels
|
||||
|
||||
A helper function has been created to get the proper `aria-label` for the switch. This can be imported as `getAriaLabel` like the following:
|
||||
|
||||
```tsx
|
||||
const { label, labelId, labelText } = getAriaLabel(el, inputId);
|
||||
```
|
||||
|
||||
where `el` and `inputId` are the following:
|
||||
|
||||
```tsx
|
||||
export class Toggle implements ComponentInterface {
|
||||
private inputId = `ion-tg-${toggleIds++}`;
|
||||
|
||||
@Element() el!: HTMLElement;
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
This can then be added to the `Host` like the following:
|
||||
|
||||
```tsx
|
||||
<Host
|
||||
aria-labelledby={label ? labelId : null}
|
||||
aria-checked={`${checked}`}
|
||||
aria-hidden={disabled ? 'true' : null}
|
||||
role="switch"
|
||||
>
|
||||
```
|
||||
|
||||
In addition to that, the checkbox input should have a label added:
|
||||
|
||||
```tsx
|
||||
<Host
|
||||
aria-labelledby={label ? labelId : null}
|
||||
aria-checked={`${checked}`}
|
||||
aria-hidden={disabled ? 'true' : null}
|
||||
role="switch"
|
||||
>
|
||||
<label htmlFor={inputId}>
|
||||
{labelText}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
aria-checked={`${checked}`}
|
||||
disabled={disabled}
|
||||
id={inputId}
|
||||
/>
|
||||
```
|
||||
|
||||
|
||||
#### Hidden Input
|
||||
|
||||
A helper function to render a hidden input has been added, it can be added in the `render`:
|
||||
|
||||
```tsx
|
||||
renderHiddenInput(true, el, name, (checked ? value : ''), disabled);
|
||||
```
|
||||
|
||||
> This is required for the switch to work with forms.
|
||||
|
||||
|
||||
#### Known Issues
|
||||
|
||||
When using VoiceOver on macOS or iOS, Chrome will announce the switch as a checked or unchecked `checkbox`:
|
||||
|
||||
```
|
||||
You are currently on a switch. To select or deselect this checkbox, press Control-Option-Space.
|
||||
```
|
||||
|
||||
There is a WebKit bug open for this: https://bugs.webkit.org/show_bug.cgi?id=196354
|
||||
|
||||
|
||||
## Rendering Anchor or Button
|
||||
|
||||
Certain components can render an `<a>` or a `<button>` depending on the presence of an `href` attribute.
|
||||
|
||||
5
.github/ionic-issue-bot.yml
vendored
5
.github/ionic-issue-bot.yml
vendored
@@ -27,7 +27,10 @@ comment:
|
||||
is added to issues that need a code reproduction.
|
||||
|
||||
|
||||
Please provide a reproduction with the minimum amount of code required to reproduce the issue. Without a reliable code reproduction, it is unlikely we will be able to resolve the issue, leading to it being closed.
|
||||
Please reproduce this issue in an Ionic starter application and provide a way for us to access it (GitHub repo, StackBlitz, etc). Without a reliable code reproduction, it is unlikely we will be able to resolve the issue, leading to it being closed.
|
||||
|
||||
|
||||
If you have already provided a code snippet and are seeing this message, it is likely that the code snippet was not enough for our team to reproduce the issue.
|
||||
|
||||
|
||||
For a guide on how to create a good reproduction, see our [Contributing Guide](https://ionicframework.com/docs/contributing/how-to-contribute#creating-a-good-code-reproduction).
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -59,10 +59,10 @@ prerender-static.html
|
||||
angular/css/
|
||||
packages/react/css/
|
||||
packages/vue/css/
|
||||
core/components/
|
||||
core/css/
|
||||
core/hydrate/
|
||||
core/loader/
|
||||
core/www/
|
||||
.stencil/
|
||||
angular/build/
|
||||
core/components/
|
||||
|
||||
@@ -63,8 +63,9 @@ async function main() {
|
||||
function checkProductionRelease() {
|
||||
const corePath = common.projectPath('core');
|
||||
const hasEsm = fs.existsSync(path.join(corePath, 'dist', 'esm'));
|
||||
const hasEsmEs5 = fs.existsSync(path.join(corePath, 'dist', 'esm-es5'));
|
||||
const hasCjs = fs.existsSync(path.join(corePath, 'dist', 'cjs'));
|
||||
if (!hasEsm || !hasCjs) {
|
||||
if (!hasEsm || !hasEsmEs5 || !hasCjs) {
|
||||
throw new Error('core build is not a production build');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,25 @@ const fs = require('fs');
|
||||
[
|
||||
// core
|
||||
{
|
||||
files: ['../core/dist/index.js', '../core/dist/ionic/index.esm.js']
|
||||
files: [
|
||||
'../core/css/core.css',
|
||||
'../core/css/core.css.map',
|
||||
'../core/css/normalize.css',
|
||||
'../core/css/normalize.css.map',
|
||||
'../core/components/index.js',
|
||||
'../core/components/index.d.ts',
|
||||
'../core/components/package.json',
|
||||
'../core/dist/index.js',
|
||||
'../core/dist/ionic/index.esm.js',
|
||||
]
|
||||
},
|
||||
// hydrate
|
||||
{
|
||||
files: [
|
||||
'../core/hydrate/index.js',
|
||||
'../core/hydrate/index.d.ts',
|
||||
'../core/hydrate/package.json'
|
||||
]
|
||||
},
|
||||
// angular
|
||||
{
|
||||
|
||||
190
CHANGELOG.md
190
CHANGELOG.md
@@ -1,3 +1,193 @@
|
||||
## [5.6.1](https://github.com/ionic-team/ionic/compare/v5.6.0...v5.6.1) (2021-03-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **custom-elements:** overlays now present correctly when using custom elements build ([#23039](https://github.com/ionic-team/ionic/issues/23039)) ([e4bf052](https://github.com/ionic-team/ionic/commit/e4bf052794af9aac07f887013b9250d2a045eba3)), closes [#23029](https://github.com/ionic-team/ionic/issues/23029)
|
||||
* **item:** detail icon is no longer announced by screen readers ([#23055](https://github.com/ionic-team/ionic/issues/23055)) ([c877061](https://github.com/ionic-team/ionic/commit/c877061a328c6ab6fa7248b9880d0205c6c4f6c1)), closes [#23054](https://github.com/ionic-team/ionic/issues/23054)
|
||||
* **label:** properly float labels for non-input items ([#23060](https://github.com/ionic-team/ionic/issues/23060)) ([c8a3999](https://github.com/ionic-team/ionic/commit/c8a3999da109b1719777f2acb791ab5388d371ea))
|
||||
* **react:** only pass tab event props from IonTabs to IonTabBar if defined ([#23024](https://github.com/ionic-team/ionic/issues/23024)) ([f94e618](https://github.com/ionic-team/ionic/commit/f94e618a7b307b143eb39c061dc9e6b80e11f862)), closes [#23023](https://github.com/ionic-team/ionic/issues/23023)
|
||||
* **refresher:** progressEnd no longer errors when pulling quickly in MD native refresher ([#23056](https://github.com/ionic-team/ionic/issues/23056)) ([67617fb](https://github.com/ionic-team/ionic/commit/67617fbc0f7ec825f1fa4c6e7e2da70e3fcd2d66))
|
||||
* **virtual-scroll:** allow null in items property ([#23047](https://github.com/ionic-team/ionic/issues/23047)) ([2a253a1](https://github.com/ionic-team/ionic/commit/2a253a1d334ca2c6a478a5bc426e3115268a29af))
|
||||
* **vue:** passing params as props are correctly updated when switching pages ([#23049](https://github.com/ionic-team/ionic/issues/23049)) ([2f54bc1](https://github.com/ionic-team/ionic/commit/2f54bc14699656e6905452a4233d982f83d0001f)), closes [#23043](https://github.com/ionic-team/ionic/issues/23043)
|
||||
|
||||
|
||||
# [5.6.0 Argon](https://github.com/ionic-team/ionic/compare/v5.5.4...v5.6.0) (2021-03-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **all:** improve support for ids with special characters when getting label element ([#22680](https://github.com/ionic-team/ionic/issues/22680)) ([19d63f6](https://github.com/ionic-team/ionic/commit/19d63f62431ef9d8279f1726dd63fac2f0d4b46b)), closes [#22678](https://github.com/ionic-team/ionic/issues/22678)
|
||||
* **header:** collapsed toolbar is no longer incorrectly shown when using ion-refresher ([#22937](https://github.com/ionic-team/ionic/issues/22937)) ([5300dcc](https://github.com/ionic-team/ionic/commit/5300dcc693caf51a726f8c346cfc9a44474fd3d1)), closes [#22829](https://github.com/ionic-team/ionic/issues/22829)
|
||||
* **label:** only show placeholder with floating label when focused ([#22958](https://github.com/ionic-team/ionic/issues/22958)) ([9282aa6](https://github.com/ionic-team/ionic/commit/9282aa68715c088e9c8fcd915e78fb7ae91f551f)), closes [#17571](https://github.com/ionic-team/ionic/issues/17571)
|
||||
* **progress-bar:** use correct theme colors in dark mode ([#22965](https://github.com/ionic-team/ionic/issues/22965)) ([b6b2714](https://github.com/ionic-team/ionic/commit/b6b2714d70f71255315510c5e49708944875db72)), closes [#20098](https://github.com/ionic-team/ionic/issues/20098)
|
||||
* **radio-group:** pressing space no longer jumps screen to bottom of page ([#22892](https://github.com/ionic-team/ionic/issues/22892)) ([3a0465e](https://github.com/ionic-team/ionic/commit/3a0465e7d6f9e3cb01336a8bdbd7001e4ec34559)), closes [#22716](https://github.com/ionic-team/ionic/issues/22716)
|
||||
* **react:** IonRouterOutlet now respects animated={false} prop ([#22905](https://github.com/ionic-team/ionic/issues/22905)) ([da1b7a0](https://github.com/ionic-team/ionic/commit/da1b7a0e7a9a5e6a9120dc4d5459c97d8bca5390)), closes [#22903](https://github.com/ionic-team/ionic/issues/22903)
|
||||
* **react:** onIonTabsWillChange and onIonTabsDidChange event handlers are now properly bound to IonTabs ([#22233](https://github.com/ionic-team/ionic/issues/22233)) ([b064fde](https://github.com/ionic-team/ionic/commit/b064fdebef14018b77242b791914d5bb10863d39))
|
||||
* **react, vue:** navigating using ion-back-button now selects correct page ([#22974](https://github.com/ionic-team/ionic/issues/22974)) ([cd8ffd8](https://github.com/ionic-team/ionic/commit/cd8ffd82a03ee69ef4cbd7922544bfc39680def9)), closes [#22830](https://github.com/ionic-team/ionic/issues/22830)
|
||||
* **react, vue:** tab buttons no longer throw an error if href is undefined ([#22998](https://github.com/ionic-team/ionic/issues/22998)) ([943e3f6](https://github.com/ionic-team/ionic/commit/943e3f6ae37ecc56f21168f057dde77a05e4e144)), closes [#22997](https://github.com/ionic-team/ionic/issues/22997)
|
||||
* **refresher:** add correct dark mode styles ([#22639](https://github.com/ionic-team/ionic/issues/22639)) ([c05476b](https://github.com/ionic-team/ionic/commit/c05476b88e3e6884b4c490461c9c67dee3dca83d)), closes [#22637](https://github.com/ionic-team/ionic/issues/22637)
|
||||
* **vue:** correctly remove active state from tab button when navigating away from tab ([#23000](https://github.com/ionic-team/ionic/issues/23000)) ([a2763af](https://github.com/ionic-team/ionic/commit/a2763afe8e1fe1dc0decdbcb757a03bc5038045e)), closes [#22597](https://github.com/ionic-team/ionic/issues/22597)
|
||||
* **vue:** prevent race conditions when opening overlays ([#22883](https://github.com/ionic-team/ionic/issues/22883)) ([68a9b80](https://github.com/ionic-team/ionic/commit/68a9b800532f9c0b308a3b74ed18a7068a942301)), closes [#22880](https://github.com/ionic-team/ionic/issues/22880)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **custom-elements:** add experimental custom elements build ([#22863](https://github.com/ionic-team/ionic/issues/22863)) ([0de75af](https://github.com/ionic-team/ionic/commit/0de75afbefc521c1d76adcd587f77ba19c285a95))
|
||||
* **progress-bar:** add parts for more design customization ([#22938](https://github.com/ionic-team/ionic/issues/22938)) ([e256d3f](https://github.com/ionic-team/ionic/commit/e256d3f09fd6f231c4d9e1d0f0927612a591466b)), closes [#20062](https://github.com/ionic-team/ionic/issues/20062) [#21820](https://github.com/ionic-team/ionic/issues/21820)
|
||||
* **react:** add react hooks to control overlay components ([#22484](https://github.com/ionic-team/ionic/issues/22484)) ([b83e009](https://github.com/ionic-team/ionic/commit/b83e00934e794a936c9d3d23d7f94bbe89cedcd5))
|
||||
* **searchbar:** add showClearIcon property ([#22759](https://github.com/ionic-team/ionic/issues/22759)) ([215eb5d](https://github.com/ionic-team/ionic/commit/215eb5d4efbb9ade942dba1687469caf61da21e7)), closes [#22738](https://github.com/ionic-team/ionic/issues/22738)
|
||||
* **vue:** add composition API ionic lifecycle hooks ([#22970](https://github.com/ionic-team/ionic/issues/22970)) ([dd1c8db](https://github.com/ionic-team/ionic/commit/dd1c8dbf3b20fbd423f70c96846d9c366d90e7c5)), closes [#22769](https://github.com/ionic-team/ionic/issues/22769)
|
||||
|
||||
|
||||
|
||||
## [5.5.5](https://github.com/ionic-team/ionic/compare/v5.5.4...v5.5.5) (2021-02-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **vue:** account for event name changes in vue 3.0.6+ ([#22980](https://github.com/ionic-team/ionic/issues/22980)) ([7dd2e6d](https://github.com/ionic-team/ionic/commit/7dd2e6d287b47cca758e1d4a71928dd3dc9ac24d)), closes [#22977](https://github.com/ionic-team/ionic/issues/22977)
|
||||
|
||||
|
||||
|
||||
## [5.5.4](https://github.com/ionic-team/ionic/compare/v5.5.3...v5.5.4) (2021-02-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** update ngAdd schematic ([#22858](https://github.com/ionic-team/ionic/issues/22858)) ([487349f](https://github.com/ionic-team/ionic/commit/487349f02a41344db2478735d27bf79f2a1c99b3))
|
||||
* **app:** keyboard no longer hides when using contenteditable ([#22857](https://github.com/ionic-team/ionic/issues/22857)) ([b6b2d34](https://github.com/ionic-team/ionic/commit/b6b2d34fd446feb06cf0143946a014d19231a78e)), closes [#22856](https://github.com/ionic-team/ionic/issues/22856)
|
||||
* **ios**: scroll assist no longer prevents first click event from firing ([#22845](https://github.com/ionic-team/ionic/issues/22845)) ([f7d4c21](https://github.com/ionic-team/ionic/commit/f7d4c21b64e27f9b655bc1ab2522d6357dc6010f)), closes [#21871](https://github.com/ionic-team/ionic/issues/21871)
|
||||
* **select:** class on component now indicates when select is open ([#22846](https://github.com/ionic-team/ionic/issues/22846)) ([1a5accc](https://github.com/ionic-team/ionic/commit/1a5accc5f707f84063469c0bd3e5e153489f1e5d)), closes [#22801](https://github.com/ionic-team/ionic/issues/22801)
|
||||
* **vue:** ionChange events now propagate correctly ([#22872](https://github.com/ionic-team/ionic/issues/22872)) ([ff0f1da](https://github.com/ionic-team/ionic/commit/ff0f1da9f11915b48c4258af7c48c4513785f3fc)), closes [#22870](https://github.com/ionic-team/ionic/issues/22870)
|
||||
|
||||
|
||||
|
||||
## [5.5.3](https://github.com/ionic-team/ionic/compare/v5.5.2...v5.5.3) (2021-01-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **react:** do not unmount overlay inner component until overlay is dismissed ([#22813](https://github.com/ionic-team/ionic/issues/22813)) ([ab1fc8f](https://github.com/ionic-team/ionic/commit/ab1fc8f2311fd252146942c7a947ebc96efd054f)), closes [#22761](https://github.com/ionic-team/ionic/issues/22761)
|
||||
* **react:** adding dynamic class to ion-page no longer hides component ([#22666](https://github.com/ionic-team/ionic/issues/22666)) ([a01bdb8](https://github.com/ionic-team/ionic/commit/a01bdb8c8dfee760721eeb35a8b556954f3b5b13)), closes [#22631](https://github.com/ionic-team/ionic/issues/22631)
|
||||
* **react:** improve view matching logic ([#22569](https://github.com/ionic-team/ionic/issues/22569)) ([f891f66](https://github.com/ionic-team/ionic/commit/f891f667082d2deb5f1b5f0f27af46e46ed1ca0f))
|
||||
* **react, vue:** do not show back button when replacing to root page ([#22750](https://github.com/ionic-team/ionic/issues/22750)) ([9e9a372](https://github.com/ionic-team/ionic/commit/9e9a3724979e95f3df1a340be21d16d8664a013c)), closes [#22528](https://github.com/ionic-team/ionic/issues/22528)
|
||||
* **refresher:** correctly detect spinner when using native refresher ([#22800](https://github.com/ionic-team/ionic/issues/22800)) ([e2d8e5c](https://github.com/ionic-team/ionic/commit/e2d8e5c4dcf893ddd8aaa556c1dd8fcaf52411c9)), closes [#22706](https://github.com/ionic-team/ionic/issues/22706)
|
||||
* **title:** only add large title transition when using collapsible header ([#22762](https://github.com/ionic-team/ionic/issues/22762)) ([348c50b](https://github.com/ionic-team/ionic/commit/348c50b7ea5d4c74498c5d26e40c1c4fe923ee55)), closes [#22760](https://github.com/ionic-team/ionic/issues/22760)
|
||||
* **vue:** all ionic vue components can now use router link ([#22743](https://github.com/ionic-team/ionic/issues/22743)) ([3d6ac13](https://github.com/ionic-team/ionic/commit/3d6ac1382e23663a3d010fd253d3c6017d3923e4))
|
||||
* **vue:** correctly determine leaving view when transitioning to a new instance of a previous page ([#22655](https://github.com/ionic-team/ionic/issues/22655)) ([e3a05bf](https://github.com/ionic-team/ionic/commit/e3a05bfeb55d8eaa38aa08a37859aa4df6ffa2d4)), closes [#22654](https://github.com/ionic-team/ionic/issues/22654) [#22658](https://github.com/ionic-team/ionic/issues/22658)
|
||||
* **vue:** ensure v-model value is properly synced before ionChange event ([#22749](https://github.com/ionic-team/ionic/issues/22749)) ([e1d6627](https://github.com/ionic-team/ionic/commit/e1d6627bf0ef1f47f980db1573c6b2a3d16d7677)), closes [#22610](https://github.com/ionic-team/ionic/issues/22610)
|
||||
* **vue:** improve path matching with tabs, deprecated adding additional pages as children of tabs without a router outlet ([#22807](https://github.com/ionic-team/ionic/issues/22807)) ([2a3ce9a](https://github.com/ionic-team/ionic/commit/2a3ce9a74e85111a2f1f470b9d8bfe2cda793ca5)), closes [#22519](https://github.com/ionic-team/ionic/issues/22519)
|
||||
* **vue:** improve v-model binding sync between vue wrappers and web components ([#22745](https://github.com/ionic-team/ionic/issues/22745)) ([64719f4](https://github.com/ionic-team/ionic/commit/64719f49f979c0296a01827d3c02599a48ba93a6)), closes [#22731](https://github.com/ionic-team/ionic/issues/22731)
|
||||
* **vue:** output commonjs format for node environments ([#22766](https://github.com/ionic-team/ionic/issues/22766)) ([7ecae2e](https://github.com/ionic-team/ionic/commit/7ecae2e4cb5d0eebc6041a8a7a5acc156132c2e1))
|
||||
* **vue:** tab bar is now correctly hidden when keyboard is open ([#22687](https://github.com/ionic-team/ionic/issues/22687)) ([5c27dd8](https://github.com/ionic-team/ionic/commit/5c27dd8032d32ebb57c31e1f6c112dc513344b93))
|
||||
|
||||
|
||||
|
||||
## [5.5.2](https://github.com/ionic-team/ionic/compare/v5.5.1...v5.5.2) (2020-12-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **android:** setting hardwareBackButton: false in config now disables default webview behavior ([#22555](https://github.com/ionic-team/ionic/issues/22555)) ([dc9faa6](https://github.com/ionic-team/ionic/commit/dc9faa6a0fbebb64c83c107c79cfd486cc0c096a)), closes [#18237](https://github.com/ionic-team/ionic/issues/18237)
|
||||
* **button:** allow aria-label to be inherited on inner button ([#22632](https://github.com/ionic-team/ionic/issues/22632)) ([818e387](https://github.com/ionic-team/ionic/commit/818e387fe81ac7026fb374d8865116dadd433c87)), closes [#22629](https://github.com/ionic-team/ionic/issues/22629)
|
||||
* **react:** hardware back button now navigates correctly ([36939e1](https://github.com/ionic-team/ionic/commit/36939e10ae0b8ac9a9275ee06d8e0d345de7c64f))
|
||||
* **react:** setting a ref now allows other props to be passed in ([31f45cd](https://github.com/ionic-team/ionic/commit/31f45cdcc953b08749d9db08321fa5ec6cbe2532)), closes [#22609](https://github.com/ionic-team/ionic/issues/22609)
|
||||
* **refresher:** clean up old css if calling refresh method before native refresher is setup ([#22640](https://github.com/ionic-team/ionic/issues/22640)) ([8d5ed47](https://github.com/ionic-team/ionic/commit/8d5ed47a282f92a60a2c4126a673cc2a5733067e)), closes [#22636](https://github.com/ionic-team/ionic/issues/22636)
|
||||
* **refresher:** refresher correctly detects native refresher when shown asynchronously ([#22623](https://github.com/ionic-team/ionic/issues/22623)) ([5ed73cd](https://github.com/ionic-team/ionic/commit/5ed73cdf4d63eeee25ef28d9676fcaa4f8e07b47)), closes [#22616](https://github.com/ionic-team/ionic/issues/22616)
|
||||
* **vue:** adding non tab button elements inside ion-tab-bar no longer causes errors ([#22643](https://github.com/ionic-team/ionic/issues/22643)) ([61cf0c5](https://github.com/ionic-team/ionic/commit/61cf0c534e45ce09410be6bfb50bdc27b657d1bc)), closes [#22642](https://github.com/ionic-team/ionic/issues/22642)
|
||||
* **vue:** correctly handle navigation failures ([#22621](https://github.com/ionic-team/ionic/issues/22621)) ([216f51b](https://github.com/ionic-team/ionic/commit/216f51b12a8c4ae7b410f47ce3d350ea513b68a1)), closes [#22591](https://github.com/ionic-team/ionic/issues/22591)
|
||||
* **vue:** correctly remove old view when replacing route ([#22566](https://github.com/ionic-team/ionic/issues/22566)) ([4f4f31b](https://github.com/ionic-team/ionic/commit/4f4f31b65e48294c3130ff24ae00b1a2aa1f9d31)), closes [#22492](https://github.com/ionic-team/ionic/issues/22492)
|
||||
* **vue:** pass in correct route to props function ([#22605](https://github.com/ionic-team/ionic/issues/22605)) ([01afdc4](https://github.com/ionic-team/ionic/commit/01afdc42e5b1598d4d15cb51761bbb3eb5d13893)), closes [#22602](https://github.com/ionic-team/ionic/issues/22602)
|
||||
* **vue:** query strings are now correctly handled when navigating back ([#22615](https://github.com/ionic-team/ionic/issues/22615)) ([a94e2a8](https://github.com/ionic-team/ionic/commit/a94e2a87fb759e7b7daed2d0304c1199dbc7afd1)), closes [#22517](https://github.com/ionic-team/ionic/issues/22517)
|
||||
* **vue:** swipe back gesture is properly disabled when swipeBackEnabled config is false ([#22568](https://github.com/ionic-team/ionic/issues/22568)) ([9d04c12](https://github.com/ionic-team/ionic/commit/9d04c127e817676983940b034a4c7efc92fdfbc6)), closes [#22567](https://github.com/ionic-team/ionic/issues/22567)
|
||||
|
||||
### For Ionic Vue Developers
|
||||
|
||||
Vue Router 4 has been released! Be sure to update from the release candidate to the latest stable version of Vue Router.
|
||||
|
||||
For more information on the changes in Vue Router 4, see https://github.com/vuejs/vue-router-next/releases/tag/v4.0.0.
|
||||
|
||||
```
|
||||
npm install vue-router@4
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.5.1](https://github.com/ionic-team/ionic/compare/v5.5.0...v5.5.1) (2020-11-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **checkbox:** click handler now fires properly ([#22573](https://github.com/ionic-team/ionic/issues/22573)) ([0786835](https://github.com/ionic-team/ionic/commit/07868354aaf88deebf7472a5bf0f34d7c823de17)), closes [#22557](https://github.com/ionic-team/ionic/issues/22557)
|
||||
* **radio:** properly announce radios on screen readers and resolve axe errors ([#22507](https://github.com/ionic-team/ionic/issues/22507)) ([afcc46e](https://github.com/ionic-team/ionic/commit/afcc46e1cc4d7f6e9d1a50f8b367da4b1d0c3143))
|
||||
* **react:** eliminate use of deprecated `findDOMNode`, resolves [#20972](https://github.com/ionic-team/ionic/issues/20972) ([5275332](https://github.com/ionic-team/ionic/commit/5275332e43694f3ee8738a1726c0d202b16c3052))
|
||||
* **router:** navigation guards now fire when navigating to a page with params ([#22521](https://github.com/ionic-team/ionic/issues/22521)) ([1956f98](https://github.com/ionic-team/ionic/commit/1956f9896883dc4687488e5418e50ce0f6cbe6c9)), closes [#22516](https://github.com/ionic-team/ionic/issues/22516)
|
||||
* **select:** fix a11y issues with axe and screen readers ([#22494](https://github.com/ionic-team/ionic/issues/22494)) ([04b874e](https://github.com/ionic-team/ionic/commit/04b874e32a65588ca79eda9399ab7e9d86a3cb77)), closes [#21552](https://github.com/ionic-team/ionic/issues/21552) [#21548](https://github.com/ionic-team/ionic/issues/21548)
|
||||
* **select:** improvements for announcing placeholder and value on screenreaders ([#22556](https://github.com/ionic-team/ionic/issues/22556)) ([ea52db6](https://github.com/ionic-team/ionic/commit/ea52db66f05a185fed6b2e849734a7ffa1c6c6ea))
|
||||
* **vue:** onBeforeRouteLeave and onBeforeRouteUpdate hooks now fire properly ([#22542](https://github.com/ionic-team/ionic/issues/22542)) ([8002114](https://github.com/ionic-team/ionic/commit/8002114e720361e60d7a7fe2d15ab88b49a72e1b)), closes [#22540](https://github.com/ionic-team/ionic/issues/22540)
|
||||
* **vue:** tabs now correctly fire lifecycle events ([#22479](https://github.com/ionic-team/ionic/issues/22479)) ([cdc2fb6](https://github.com/ionic-team/ionic/commit/cdc2fb652fe5aa149eaa751a77fb506ac1f64195)), closes [#22466](https://github.com/ionic-team/ionic/issues/22466)
|
||||
* **vue:** unit testing a routerLink-capable component no longer warns of missing router dependency ([#22532](https://github.com/ionic-team/ionic/issues/22532)) ([4e23aad](https://github.com/ionic-team/ionic/commit/4e23aad3d911188e4a2706545463a81332c00ce9)), closes [#22506](https://github.com/ionic-team/ionic/issues/22506)
|
||||
|
||||
### For Ionic Vue Developers
|
||||
|
||||
When updating to Ionic Vue v5.5.1 make sure you are on the latest version of `vue-router@next` to take advantage of the bug fixes in this release:
|
||||
|
||||
```
|
||||
npm install vue-router@next
|
||||
```
|
||||
|
||||
|
||||
|
||||
# [5.5.0 Chlorine](https://github.com/ionic-team/ionic/compare/v5.4.4...v5.5.0) (2020-11-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **backdrop:** nvda no longer incorrectly announces backdrop ([#22481](https://github.com/ionic-team/ionic/issues/22481)) ([2d878fc](https://github.com/ionic-team/ionic/commit/2d878fc4f6c7a710dbfb722e188e3e402e1672f9)), closes [#22102](https://github.com/ionic-team/ionic/issues/22102)
|
||||
* **checkbox:** use a native input to fix a11y issues with axe and screen readers ([#22402](https://github.com/ionic-team/ionic/issues/22402)) ([7214a84](https://github.com/ionic-team/ionic/commit/7214a8401b709e1353155304cf6e9f97b2b4d294)), closes [#21644](https://github.com/ionic-team/ionic/issues/21644) [#20517](https://github.com/ionic-team/ionic/issues/20517) [#17796](https://github.com/ionic-team/ionic/issues/17796)
|
||||
* **input:** title attribute is now automatically inherited ([#22493](https://github.com/ionic-team/ionic/issues/22493)) ([abad12f](https://github.com/ionic-team/ionic/commit/abad12fbdb1378066282fe8e9b7761747951b685)), closes [#22055](https://github.com/ionic-team/ionic/issues/22055)
|
||||
* **refresher:** ios native refresher now works in side menu ([#22449](https://github.com/ionic-team/ionic/issues/22449)) ([a4a6453](https://github.com/ionic-team/ionic/commit/a4a64530ff083b83187b293dfdacb0fa45ad9f51))
|
||||
* **refresher:** md native refresher now works in side menu ([#22446](https://github.com/ionic-team/ionic/issues/22446)) ([6b817f2](https://github.com/ionic-team/ionic/commit/6b817f26b08d01d8367d16308db775b6192e7628)), closes [#20832](https://github.com/ionic-team/ionic/issues/20832)
|
||||
* **toggle:** use a native input to fix a11y issues with axe and screen readers ([#22477](https://github.com/ionic-team/ionic/issues/22477)) ([813611a](https://github.com/ionic-team/ionic/commit/813611a61b664c9827760ccaa889d0e2fcae7d94)), closes [#22011](https://github.com/ionic-team/ionic/issues/22011) [#21552](https://github.com/ionic-team/ionic/issues/21552)
|
||||
* **vue:** correctly pass route props to components ([#22476](https://github.com/ionic-team/ionic/issues/22476)) ([0956f8b](https://github.com/ionic-team/ionic/commit/0956f8bc5588836996c8c74f98166c347414a312)), closes [#22472](https://github.com/ionic-team/ionic/issues/22472)
|
||||
* **vue:** tab bar now works with slot="top" ([#22461](https://github.com/ionic-team/ionic/issues/22461)) ([e17c822](https://github.com/ionic-team/ionic/commit/e17c822bfbc2a876226738b77a4c95c02e0b5953)), closes [#22456](https://github.com/ionic-team/ionic/issues/22456)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **chip:** add disabled property ([#20658](https://github.com/ionic-team/ionic/issues/20658)) ([0a0cbd8](https://github.com/ionic-team/ionic/commit/0a0cbd8f2a505ad2b3d8afb60cb1e940ced52e0d)), closes [#19510](https://github.com/ionic-team/ionic/issues/19510)
|
||||
* **segment:** add swipeGesture property to allow for disabling of the swipe gesture ([#22087](https://github.com/ionic-team/ionic/issues/22087)) ([65bc995](https://github.com/ionic-team/ionic/commit/65bc99577c44cce653dafd9937c4d8f9c45fff61)), closes [#22048](https://github.com/ionic-team/ionic/issues/22048)
|
||||
* **vue:** composition api lifecycle methods ([#22241](https://github.com/ionic-team/ionic/issues/22241)) ([f5b0299](https://github.com/ionic-team/ionic/commit/f5b0299729c2c639e432612e62fb7eaa189ca969))
|
||||
* **vue:** vetur support ([#22403](https://github.com/ionic-team/ionic/issues/22403)) ([e76f79d](https://github.com/ionic-team/ionic/commit/e76f79d0548c97edd193808f5e0a19889cffae5b))
|
||||
* **vue:** web-types support ([#22428](https://github.com/ionic-team/ionic/issues/22428)) ([639314a](https://github.com/ionic-team/ionic/commit/639314ab218b65a9a2de6040417b0e1b363e47ef)), closes [#19522](https://github.com/ionic-team/ionic/issues/19522)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **ios:** move content to stacking context while preserving position: fixed behavior ([#22489](https://github.com/ionic-team/ionic/issues/22489)) ([d77a9d5](https://github.com/ionic-team/ionic/commit/d77a9d57ec02c69df43ec2a286eea674a85cae36)), closes [#22473](https://github.com/ionic-team/ionic/issues/22473)
|
||||
|
||||
|
||||
|
||||
## [5.4.4](https://github.com/ionic-team/ionic/compare/v5.4.3...v5.4.4) (2020-11-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **build:** add missing es5 output ([228d349](https://github.com/ionic-team/ionic/commit/228d349c6e29b62cbfee5d5502883682cfa5032f))
|
||||
|
||||
|
||||
|
||||
## [5.4.3](https://github.com/ionic-team/ionic/compare/v5.4.2...v5.4.3) (2020-11-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **all** add missing vendor prefixes to css ([0989ea5](https://github.com/ionic-team/ionic/commit/0989ea5ac897f528e8fce5434861ca080b9b4a56))
|
||||
|
||||
|
||||
|
||||
## [5.4.2](https://github.com/ionic-team/ionic/compare/v5.4.1...v5.4.2) (2020-11-05)
|
||||
|
||||
|
||||
|
||||
@@ -16,14 +16,14 @@ diverse, inclusive, and healthy community.
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
community include the ability to:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
* Demonstrate empathy and kindness towards people
|
||||
* Be respectful of differing opinions, viewpoints, and experiences
|
||||
* Give and gracefully accept constructive feedback
|
||||
* Accept responsibility and apologize to those affected by our mistakes,
|
||||
and learn from the experience
|
||||
* Focus on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
133
angular/package-lock.json
generated
133
angular/package-lock.json
generated
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"name": "@ionic/angular",
|
||||
"version": "5.4.2",
|
||||
"version": "5.6.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@ionic/angular",
|
||||
"version": "5.4.2",
|
||||
"version": "5.6.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ionic/core": "file:../core",
|
||||
"@ionic/core": "5.6.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -42,43 +42,6 @@
|
||||
"zone.js": ">=0.8.26"
|
||||
}
|
||||
},
|
||||
"../core": {
|
||||
"version": "5.4.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ionicons": "^5.1.2",
|
||||
"tslib": "^1.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-node-resolve": "^8.4.0",
|
||||
"@rollup/plugin-virtual": "^2.0.3",
|
||||
"@stencil/core": "2.1.2",
|
||||
"@stencil/sass": "1.3.2",
|
||||
"@stencil/vue-output-target": "0.1.8",
|
||||
"@types/jest": "^26.0.10",
|
||||
"@types/node": "^14.6.0",
|
||||
"@types/puppeteer": "3.0.1",
|
||||
"@types/swiper": "5.4.0",
|
||||
"aws-sdk": "^2.738.0",
|
||||
"clean-css-cli": "^4.1.11",
|
||||
"domino": "^2.1.6",
|
||||
"fs-extra": "^9.0.1",
|
||||
"jest": "^26.4.1",
|
||||
"jest-cli": "^26.4.1",
|
||||
"np": "^6.4.0",
|
||||
"pixelmatch": "4.0.2",
|
||||
"puppeteer": "^5.2.1",
|
||||
"rollup": "^2.26.4",
|
||||
"sass": "^1.26.10",
|
||||
"stylelint": "^13.6.1",
|
||||
"stylelint-order": "^4.1.0",
|
||||
"swiper": "5.4.1",
|
||||
"tslint": "^6.1.3",
|
||||
"tslint-ionic-rules": "0.0.21",
|
||||
"tslint-react": "^5.0.0",
|
||||
"typescript": "^4.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/core": {
|
||||
"version": "8.3.17",
|
||||
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.17.tgz",
|
||||
@@ -241,8 +204,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@ionic/core": {
|
||||
"resolved": "../core",
|
||||
"link": true
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.6.0.tgz",
|
||||
"integrity": "sha512-dRwNRjHBiE2nB0JKY7tNHbcYMGXmDp7QCCVmtQbU511rZkwDlPUtg5qMJFHuLQVVUE21bjmWIJliE0aB9kcwYQ==",
|
||||
"dependencies": {
|
||||
"@stencil/core": "^2.4.0",
|
||||
"ionicons": "^5.5.0",
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/plugin-commonjs": {
|
||||
"version": "11.1.0",
|
||||
@@ -322,6 +291,18 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@stencil/core": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.4.0.tgz",
|
||||
"integrity": "sha512-gU6+Yyd6O0KrCSS/O6j8KKqmRo+/Dcs2fI0+APCpbAWK+nqhwDISpdnSEfGDCLMoAC08XOZCycBRk2K1VGnEcg==",
|
||||
"bin": {
|
||||
"stencil": "bin/stencil"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.10.0",
|
||||
"npm": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@szmarczak/http-timer": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
|
||||
@@ -945,7 +926,6 @@
|
||||
"anymatch": "^2.0.0",
|
||||
"async-each": "^1.0.1",
|
||||
"braces": "^2.3.2",
|
||||
"fsevents": "^1.2.7",
|
||||
"glob-parent": "^3.1.0",
|
||||
"inherits": "^2.0.3",
|
||||
"is-binary-path": "^1.0.0",
|
||||
@@ -2047,6 +2027,14 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/ionicons": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-5.5.0.tgz",
|
||||
"integrity": "sha512-0DUHTeoIrGSY+KNyNDaQW7v5+mDstjSkjx8dzT925kXKYBDrN3sGs8kUcSSQbTK132U4CbgDEZkn7FDUa9x8Qw==",
|
||||
"dependencies": {
|
||||
"@stencil/core": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-accessor-descriptor": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
|
||||
@@ -2429,13 +2417,7 @@
|
||||
"integrity": "sha512-+1V2PCMFkL+OIj2/HrtrvZw0BC0sYLMICJfbQjuj/K8CEnlrFX6R5cKKgzzttsZDHyxQNL1jqMREjKN3ja/E3Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"errno": "^0.1.1",
|
||||
"graceful-fs": "^4.1.2",
|
||||
"image-size": "~0.5.0",
|
||||
"make-dir": "^2.1.0",
|
||||
"mime": "^1.4.1",
|
||||
"native-request": "^1.0.5",
|
||||
"source-map": "~0.6.0",
|
||||
"tslib": "^1.10.0"
|
||||
},
|
||||
"bin": {
|
||||
@@ -2818,7 +2800,6 @@
|
||||
"dependencies": {
|
||||
"anymatch": "~3.1.1",
|
||||
"braces": "~3.0.2",
|
||||
"fsevents": "~2.1.2",
|
||||
"glob-parent": "~5.1.0",
|
||||
"is-binary-path": "~2.1.0",
|
||||
"is-glob": "~4.0.1",
|
||||
@@ -2959,9 +2940,6 @@
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.7.5.tgz",
|
||||
"integrity": "sha512-xQSM8uzhgtF6tTnTVEvOQThrcG3LPUP3T/4l4EukzDp0kbTY1QRDuXjiwtYzs9odKj9Bj/PccRG6viFfS7DmCQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fsevents": "~2.1.2"
|
||||
},
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
},
|
||||
@@ -5173,37 +5151,13 @@
|
||||
}
|
||||
},
|
||||
"@ionic/core": {
|
||||
"version": "file:../core",
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.6.0.tgz",
|
||||
"integrity": "sha512-dRwNRjHBiE2nB0JKY7tNHbcYMGXmDp7QCCVmtQbU511rZkwDlPUtg5qMJFHuLQVVUE21bjmWIJliE0aB9kcwYQ==",
|
||||
"requires": {
|
||||
"@rollup/plugin-node-resolve": "^8.4.0",
|
||||
"@rollup/plugin-virtual": "^2.0.3",
|
||||
"@stencil/core": "2.1.2",
|
||||
"@stencil/sass": "1.3.2",
|
||||
"@stencil/vue-output-target": "0.1.8",
|
||||
"@types/jest": "^26.0.10",
|
||||
"@types/node": "^14.6.0",
|
||||
"@types/puppeteer": "3.0.1",
|
||||
"@types/swiper": "5.4.0",
|
||||
"aws-sdk": "^2.738.0",
|
||||
"clean-css-cli": "^4.1.11",
|
||||
"domino": "^2.1.6",
|
||||
"fs-extra": "^9.0.1",
|
||||
"ionicons": "^5.1.2",
|
||||
"jest": "^26.4.1",
|
||||
"jest-cli": "^26.4.1",
|
||||
"np": "^6.4.0",
|
||||
"pixelmatch": "4.0.2",
|
||||
"puppeteer": "^5.2.1",
|
||||
"rollup": "^2.26.4",
|
||||
"sass": "^1.26.10",
|
||||
"stylelint": "^13.6.1",
|
||||
"stylelint-order": "^4.1.0",
|
||||
"swiper": "5.4.1",
|
||||
"tslib": "^1.10.0",
|
||||
"tslint": "^6.1.3",
|
||||
"tslint-ionic-rules": "0.0.21",
|
||||
"tslint-react": "^5.0.0",
|
||||
"typescript": "^4.0.5"
|
||||
"@stencil/core": "^2.4.0",
|
||||
"ionicons": "^5.5.0",
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"@rollup/plugin-commonjs": {
|
||||
@@ -5276,6 +5230,11 @@
|
||||
"integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@stencil/core": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.4.0.tgz",
|
||||
"integrity": "sha512-gU6+Yyd6O0KrCSS/O6j8KKqmRo+/Dcs2fI0+APCpbAWK+nqhwDISpdnSEfGDCLMoAC08XOZCycBRk2K1VGnEcg=="
|
||||
},
|
||||
"@szmarczak/http-timer": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
|
||||
@@ -6690,6 +6649,14 @@
|
||||
"integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
|
||||
"dev": true
|
||||
},
|
||||
"ionicons": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-5.5.0.tgz",
|
||||
"integrity": "sha512-0DUHTeoIrGSY+KNyNDaQW7v5+mDstjSkjx8dzT925kXKYBDrN3sGs8kUcSSQbTK132U4CbgDEZkn7FDUa9x8Qw==",
|
||||
"requires": {
|
||||
"@stencil/core": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"is-accessor-descriptor": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/angular",
|
||||
"version": "5.4.2",
|
||||
"version": "5.6.1",
|
||||
"description": "Angular specific wrappers for @ionic/core",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -42,7 +42,7 @@
|
||||
"validate": "npm i && npm run lint && npm run test && npm run build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ionic/core": "5.4.2",
|
||||
"@ionic/core": "5.6.1",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Location } from '@angular/common';
|
||||
import { Attribute, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, Injector, NgZone, OnDestroy, OnInit, Optional, Output, SkipSelf, ViewContainerRef } from '@angular/core';
|
||||
import { ActivatedRoute, ChildrenOutletContexts, OutletContext, PRIMARY_OUTLET, Router } from '@angular/router';
|
||||
import { componentOnReady } from '@ionic/core';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { distinctUntilChanged, filter, switchMap } from 'rxjs/operators';
|
||||
|
||||
@@ -96,13 +97,12 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
this.activateWith(context.route, context.resolver || null);
|
||||
}
|
||||
}
|
||||
if ((this.nativeEl as any).componentOnReady) {
|
||||
this.nativeEl.componentOnReady().then(() => {
|
||||
if (this._swipeGesture === undefined) {
|
||||
this.swipeGesture = this.config.getBoolean('swipeBackEnabled', (this.nativeEl as any).mode === 'ios');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
new Promise(resolve => componentOnReady(this.nativeEl, resolve)).then(() => {
|
||||
if (this._swipeGesture === undefined) {
|
||||
this.swipeGesture = this.config.getBoolean('swipeBackEnabled', (this.nativeEl as any).mode === 'ios');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get isActivated(): boolean {
|
||||
|
||||
@@ -156,8 +156,8 @@ export class IonCheckbox {
|
||||
}
|
||||
export declare interface IonChip extends Components.IonChip {
|
||||
}
|
||||
@ProxyCmp({ inputs: ["color", "mode", "outline"] })
|
||||
@Component({ selector: "ion-chip", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>", inputs: ["color", "mode", "outline"] })
|
||||
@ProxyCmp({ inputs: ["color", "disabled", "mode", "outline"] })
|
||||
@Component({ selector: "ion-chip", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>", inputs: ["color", "disabled", "mode", "outline"] })
|
||||
export class IonChip {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
@@ -278,8 +278,8 @@ export class IonHeader {
|
||||
}
|
||||
export declare interface IonIcon extends Components.IonIcon {
|
||||
}
|
||||
@ProxyCmp({ inputs: ["ariaLabel", "color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "size", "src"] })
|
||||
@Component({ selector: "ion-icon", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>", inputs: ["ariaLabel", "color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "size", "src"] })
|
||||
@ProxyCmp({ inputs: ["ariaHidden", "ariaLabel", "color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] })
|
||||
@Component({ selector: "ion-icon", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>", inputs: ["ariaHidden", "ariaLabel", "color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] })
|
||||
export class IonIcon {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
@@ -643,8 +643,8 @@ export class IonRow {
|
||||
}
|
||||
export declare interface IonSearchbar extends Components.IonSearchbar {
|
||||
}
|
||||
@ProxyCmp({ inputs: ["animated", "autocomplete", "autocorrect", "cancelButtonIcon", "cancelButtonText", "clearIcon", "color", "debounce", "disabled", "enterkeyhint", "inputmode", "mode", "placeholder", "searchIcon", "showCancelButton", "spellcheck", "type", "value"], "methods": ["setFocus", "getInputElement"] })
|
||||
@Component({ selector: "ion-searchbar", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>", inputs: ["animated", "autocomplete", "autocorrect", "cancelButtonIcon", "cancelButtonText", "clearIcon", "color", "debounce", "disabled", "enterkeyhint", "inputmode", "mode", "placeholder", "searchIcon", "showCancelButton", "spellcheck", "type", "value"] })
|
||||
@ProxyCmp({ inputs: ["animated", "autocomplete", "autocorrect", "cancelButtonIcon", "cancelButtonText", "clearIcon", "color", "debounce", "disabled", "enterkeyhint", "inputmode", "mode", "placeholder", "searchIcon", "showCancelButton", "showClearButton", "spellcheck", "type", "value"], "methods": ["setFocus", "getInputElement"] })
|
||||
@Component({ selector: "ion-searchbar", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>", inputs: ["animated", "autocomplete", "autocorrect", "cancelButtonIcon", "cancelButtonText", "clearIcon", "color", "debounce", "disabled", "enterkeyhint", "inputmode", "mode", "placeholder", "searchIcon", "showCancelButton", "showClearButton", "spellcheck", "type", "value"] })
|
||||
export class IonSearchbar {
|
||||
ionInput!: EventEmitter<CustomEvent>;
|
||||
ionChange!: EventEmitter<CustomEvent>;
|
||||
@@ -661,8 +661,8 @@ export class IonSearchbar {
|
||||
}
|
||||
export declare interface IonSegment extends Components.IonSegment {
|
||||
}
|
||||
@ProxyCmp({ inputs: ["color", "disabled", "mode", "scrollable", "value"] })
|
||||
@Component({ selector: "ion-segment", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>", inputs: ["color", "disabled", "mode", "scrollable", "value"] })
|
||||
@ProxyCmp({ inputs: ["color", "disabled", "mode", "scrollable", "swipeGesture", "value"] })
|
||||
@Component({ selector: "ion-segment", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>", inputs: ["color", "disabled", "mode", "scrollable", "swipeGesture", "value"] })
|
||||
export class IonSegment {
|
||||
ionChange!: EventEmitter<CustomEvent>;
|
||||
protected el: HTMLElement;
|
||||
|
||||
@@ -71,7 +71,7 @@ export declare interface IonVirtualScroll {
|
||||
* entire virtual scroll is reset, which is an expensive operation and
|
||||
* should be avoided if possible.
|
||||
*/
|
||||
items?: any[];
|
||||
items?: any[] | null;
|
||||
|
||||
/**
|
||||
* An optional function that maps each item within their height.
|
||||
|
||||
@@ -40,20 +40,23 @@ function addIonicAngularModuleToAppModule(projectSourceRoot: Path): Rule {
|
||||
function addIonicStyles(projectName: string, projectSourceRoot: Path): Rule {
|
||||
return (host: Tree) => {
|
||||
const ionicStyles = [
|
||||
'node_modules/@ionic/angular/css/core.css',
|
||||
'node_modules/@ionic/angular/css/normalize.css',
|
||||
'node_modules/@ionic/angular/css/structure.css',
|
||||
'node_modules/@ionic/angular/css/typography.css',
|
||||
'node_modules/@ionic/angular/css/core.css',
|
||||
'node_modules/@ionic/angular/css/display.css',
|
||||
'node_modules/@ionic/angular/css/padding.css',
|
||||
'node_modules/@ionic/angular/css/float-elements.css',
|
||||
'node_modules/@ionic/angular/css/text-alignment.css',
|
||||
'node_modules/@ionic/angular/css/text-transformation.css',
|
||||
'node_modules/@ionic/angular/css/flex-utils.css',
|
||||
`${projectSourceRoot}/theme/variables.css`
|
||||
].forEach(entry => {
|
||||
]
|
||||
|
||||
ionicStyles.forEach(entry => {
|
||||
addStyle(host, projectName, entry);
|
||||
});
|
||||
return host;
|
||||
return host;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -64,7 +67,8 @@ function addIonicons(projectName: string): Rule {
|
||||
input: 'node_modules/ionicons/dist/ionicons/svg',
|
||||
output: './svg'
|
||||
};
|
||||
addAsset(host, projectName, ioniconsGlob);
|
||||
addAsset(host, projectName, 'build', ioniconsGlob);
|
||||
addAsset(host, projectName, 'test', ioniconsGlob);
|
||||
return host;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -62,10 +62,10 @@ export function addStyle(host: Tree, projectName: string, stylePath: string) {
|
||||
writeConfig(host, config);
|
||||
}
|
||||
|
||||
export function addAsset(host: Tree, projectName: string, asset: string | {glob: string; input: string; output: string}) {
|
||||
export function addAsset(host: Tree, projectName: string, architect: string, asset: string | {glob: string; input: string; output: string}) {
|
||||
const config = readConfig(host);
|
||||
const appConfig = getAngularAppConfig(config, projectName);
|
||||
appConfig.architect.build.options.assets.push(asset);
|
||||
appConfig.architect[architect].options.assets.push(asset);
|
||||
writeConfig(host, config);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,6 @@ export interface IonicWindow extends Window {
|
||||
}
|
||||
|
||||
export interface HTMLStencilElement extends HTMLElement {
|
||||
componentOnReady(): Promise<this>;
|
||||
forceUpdate(): void;
|
||||
componentOnReady?(): Promise<this>;
|
||||
forceUpdate?(): void;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"sync:build": "sh scripts/build-ionic.sh",
|
||||
"sync": "sh scripts/sync.sh",
|
||||
"build": "npm run sync && ng build --prod --no-progress",
|
||||
"pretest": "webdriver-manager update --versions.chrome 85.0.4183.87",
|
||||
"pretest": "webdriver-manager update --versions.chrome 89.0.4389.23",
|
||||
"test": "ng e2e --prod --webdriver-update=false",
|
||||
"test.dev": "npm run sync && ng e2e",
|
||||
"lint": "ng lint",
|
||||
|
||||
@@ -40,6 +40,24 @@ The `@ionic/core` package can by used in simple HTML, or by vanilla JavaScript w
|
||||
* [@ionic/angular](https://www.npmjs.com/package/@ionic/angular)
|
||||
|
||||
|
||||
## Custom Elements Build (Experimental)
|
||||
|
||||
In addition to the default, self lazy-loading components built by Stencil, this package also comes with each component exported as a stand-alone custom element within `@ionic/core/components`. Each component extends `HTMLElement`, and does not lazy-load itself. Instead, this package is useful for projects already using a bundler such as Webpack or Rollup. While all components are available to be imported, the custom elements build also ensures bundlers only import what's used, and tree-shakes any unused components.
|
||||
|
||||
Below is an example of importing `ion-toggle`, and initializing Ionic so it's able to correctly load the "mode", such as Material Design or iOS. Additionally, the `initialize({...})` function can receive the Ionic config.
|
||||
|
||||
```typescript
|
||||
import { IonBadge } from "@ionic/core/components/ion-badge";
|
||||
import { initialize } from "@ionic/core/components";
|
||||
|
||||
initialize();
|
||||
|
||||
customElements.define("ion-badge", IonBadge);
|
||||
```
|
||||
|
||||
Notice how `IonBadge` is imported from `@ionic/core/components/ion-badge` rather than just `@ionic/core/components`. Additionally, the `initialize` function is imported from `@ionic/core/components` rather than `@ionic/core`. All of this helps to ensure bundlers do not pull in more code than is needed.
|
||||
|
||||
|
||||
## How to contribute
|
||||
|
||||
[Check out the CONTRIBUTE guide](CONTRIBUTING.md)
|
||||
|
||||
@@ -248,6 +248,7 @@ ion-checkbox,part,mark
|
||||
|
||||
ion-chip,shadow
|
||||
ion-chip,prop,color,string | undefined,undefined,false,false
|
||||
ion-chip,prop,disabled,boolean,false,false,false
|
||||
ion-chip,prop,mode,"ios" | "md",undefined,false,false
|
||||
ion-chip,prop,outline,boolean,false,false,false
|
||||
ion-chip,css-prop,--background
|
||||
@@ -843,6 +844,9 @@ ion-progress-bar,prop,value,number,0,false,false
|
||||
ion-progress-bar,css-prop,--background
|
||||
ion-progress-bar,css-prop,--buffer-background
|
||||
ion-progress-bar,css-prop,--progress-background
|
||||
ion-progress-bar,part,progress
|
||||
ion-progress-bar,part,stream
|
||||
ion-progress-bar,part,track
|
||||
|
||||
ion-radio,shadow
|
||||
ion-radio,prop,color,string | undefined,undefined,false,false
|
||||
@@ -986,6 +990,7 @@ ion-searchbar,prop,mode,"ios" | "md",undefined,false,false
|
||||
ion-searchbar,prop,placeholder,string,'Search',false,false
|
||||
ion-searchbar,prop,searchIcon,string | undefined,undefined,false,false
|
||||
ion-searchbar,prop,showCancelButton,"always" | "focus" | "never",'never',false,false
|
||||
ion-searchbar,prop,showClearButton,"always" | "focus" | "never",'focus',false,false
|
||||
ion-searchbar,prop,spellcheck,boolean,false,false,false
|
||||
ion-searchbar,prop,type,"email" | "number" | "password" | "search" | "tel" | "text" | "url",'search',false,false
|
||||
ion-searchbar,prop,value,null | string | undefined,'',false,false
|
||||
@@ -1014,6 +1019,7 @@ ion-segment,prop,color,string | undefined,undefined,false,false
|
||||
ion-segment,prop,disabled,boolean,false,false,false
|
||||
ion-segment,prop,mode,"ios" | "md",undefined,false,false
|
||||
ion-segment,prop,scrollable,boolean,false,false,false
|
||||
ion-segment,prop,swipeGesture,boolean,true,false,false
|
||||
ion-segment,prop,value,null | string | undefined,undefined,false,false
|
||||
ion-segment,event,ionChange,SegmentChangeEventDetail,true
|
||||
ion-segment,css-prop,--background
|
||||
|
||||
12848
core/package-lock.json
generated
12848
core/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/core",
|
||||
"version": "5.4.2",
|
||||
"version": "5.6.1",
|
||||
"description": "Base components for Ionic",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -24,24 +24,26 @@
|
||||
"collection": "dist/collection/collection-manifest.json",
|
||||
"types": "dist/types/interface.d.ts",
|
||||
"files": [
|
||||
"dist/",
|
||||
"components/",
|
||||
"css/",
|
||||
"dist/",
|
||||
"hydrate/",
|
||||
"loader/"
|
||||
],
|
||||
"dependencies": {
|
||||
"ionicons": "^5.1.2",
|
||||
"@stencil/core": "^2.4.0",
|
||||
"ionicons": "^5.5.0",
|
||||
"tslib": "^1.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jest/core": "^26.6.3",
|
||||
"@rollup/plugin-node-resolve": "^8.4.0",
|
||||
"@rollup/plugin-virtual": "^2.0.3",
|
||||
"@stencil/core": "2.1.2",
|
||||
"@stencil/sass": "1.3.2",
|
||||
"@stencil/vue-output-target": "0.1.8",
|
||||
"@types/jest": "^26.0.10",
|
||||
"@stencil/vue-output-target": "^0.4.1",
|
||||
"@types/jest": "^26.0.20",
|
||||
"@types/node": "^14.6.0",
|
||||
"@types/puppeteer": "3.0.1",
|
||||
"@types/puppeteer": "5.4.3",
|
||||
"@types/swiper": "5.4.0",
|
||||
"aws-sdk": "^2.738.0",
|
||||
"clean-css-cli": "^4.1.11",
|
||||
@@ -51,7 +53,7 @@
|
||||
"jest-cli": "^26.4.1",
|
||||
"np": "^6.4.0",
|
||||
"pixelmatch": "4.0.2",
|
||||
"puppeteer": "^5.2.1",
|
||||
"puppeteer": "^7.0.1",
|
||||
"rollup": "^2.26.4",
|
||||
"sass": "^1.26.10",
|
||||
"stylelint": "^13.6.1",
|
||||
@@ -75,7 +77,7 @@
|
||||
"css.sass": "sass src/css:./css",
|
||||
"lint": "npm run lint.ts && npm run lint.sass",
|
||||
"lint.fix": "npm run lint.ts.fix && npm run lint.sass.fix",
|
||||
"lint.sass": "stylelint 'src/**/*.scss'",
|
||||
"lint.sass": "stylelint \"src/**/*.scss\"",
|
||||
"lint.sass.fix": "npm run lint.sass -- --fix",
|
||||
"lint.ts": "tslint --project .",
|
||||
"lint.ts.fix": "tslint --project . --fix",
|
||||
|
||||
2
core/scripts/custom-elements/custom-elements.d.ts
vendored
Normal file
2
core/scripts/custom-elements/custom-elements.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './index';
|
||||
export * from '../dist/types/interface';
|
||||
9
core/scripts/custom-elements/package.json
Normal file
9
core/scripts/custom-elements/package.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "@ionic/core/components",
|
||||
"version": "0.0.0",
|
||||
"description": "Ionic Components exported as custom elements, extending HTMLElement.",
|
||||
"main": "./index.js",
|
||||
"types": "./custom-elements.d.ts",
|
||||
"private": true,
|
||||
"sideEffects": false
|
||||
}
|
||||
@@ -4,15 +4,15 @@ const virtual = require('@rollup/plugin-virtual');
|
||||
const fs = require('fs');
|
||||
|
||||
async function main() {
|
||||
const input = process.argv[2] || getInput();
|
||||
const result = await check(input);
|
||||
const relative = path.relative(process.cwd(), input);
|
||||
const input = process.argv[2] || getMainEntry();
|
||||
const result = await check(input);
|
||||
const relative = path.relative(process.cwd(), input);
|
||||
|
||||
if (result.shaken) {
|
||||
console.error(`Success! ${relative} is fully tree-shakeable`);
|
||||
} else {
|
||||
error(`Failed to tree-shake ${relative}`);
|
||||
}
|
||||
if (result.shaken) {
|
||||
console.error(`Success! ${relative} is fully tree-shakeable`);
|
||||
} else {
|
||||
error(`Failed to tree-shake ${relative}`);
|
||||
};
|
||||
}
|
||||
|
||||
function error(msg) {
|
||||
@@ -20,10 +20,10 @@ function error(msg) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function getInput() {
|
||||
if (!fs.existsSync('package.json')) {
|
||||
error(`Could not find package.json`);
|
||||
}
|
||||
function getMainEntry() {
|
||||
if (!fs.existsSync('package.json')) {
|
||||
error(`Could not find package.json`);
|
||||
}
|
||||
|
||||
const pkg = JSON.parse(fs.readFileSync('package.json'), 'utf-8');
|
||||
|
||||
@@ -38,20 +38,20 @@ function getInput() {
|
||||
}
|
||||
|
||||
function resolve(file) {
|
||||
if (isDirectory(file)) {
|
||||
return ifExists(`${file}/index.js`) || ifExists(`${file}/index.cjs.js`);
|
||||
}
|
||||
if (isDirectory(file)) {
|
||||
return ifExists(`${file}/index.cjs.js`) || ifExists(`${file}/index.js`);
|
||||
}
|
||||
|
||||
return ifExists(file) || ifExists(`${file}.js`) || ifExists(`${file}.cjs.js`);
|
||||
return ifExists(file) || ifExists(`${file}.cjs.js`) || ifExists(`${file}.js`);
|
||||
}
|
||||
|
||||
function isDirectory(file) {
|
||||
try {
|
||||
const stats = fs.statSync(file);
|
||||
return stats.isDirectory();
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
const stats = fs.statSync(file);
|
||||
return stats.isDirectory();
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function ifExists(file) {
|
||||
@@ -67,7 +67,7 @@ async function check(input) {
|
||||
virtual({
|
||||
__agadoo__: `import ${JSON.stringify(resolved)}`,
|
||||
tslib: `
|
||||
const noop = () => {};
|
||||
const noop = () => {};
|
||||
export const __awaiter = noop;
|
||||
export const __extends = noop;
|
||||
export const __generator = noop;
|
||||
|
||||
46
core/src/components.d.ts
vendored
46
core/src/components.d.ts
vendored
@@ -394,7 +394,7 @@ export namespace Components {
|
||||
*/
|
||||
"name": string;
|
||||
/**
|
||||
* The value of the toggle does not mean if it's checked or not, use the `checked` property for that. The value of a toggle is analogous to the value of a `<input type="checkbox">`, it's only used when the toggle participates in a native `<form>`.
|
||||
* The value of the checkbox does not mean if it's checked or not, use the `checked` property for that. The value of a checkbox is analogous to the value of an `<input type="checkbox">`, it's only used when the checkbox participates in a native `<form>`.
|
||||
*/
|
||||
"value": string;
|
||||
}
|
||||
@@ -403,6 +403,10 @@ export namespace Components {
|
||||
* 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).
|
||||
*/
|
||||
"color"?: Color;
|
||||
/**
|
||||
* If `true`, the user cannot interact with the chip.
|
||||
*/
|
||||
"disabled": boolean;
|
||||
/**
|
||||
* The mode determines which platform styles to use.
|
||||
*/
|
||||
@@ -830,7 +834,7 @@ export namespace Components {
|
||||
*/
|
||||
"accept"?: string;
|
||||
/**
|
||||
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
|
||||
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
|
||||
*/
|
||||
"autocapitalize": string;
|
||||
/**
|
||||
@@ -1221,7 +1225,7 @@ export namespace Components {
|
||||
*/
|
||||
"close": (animated?: boolean) => Promise<boolean>;
|
||||
/**
|
||||
* The content's id the menu should use.
|
||||
* The `id` of the main content. When using a router this is typically `ion-router-outlet`. When not using a router, this is typically your main view's `ion-content`. This is not the id of the `ion-content` inside of your `ion-menu`.
|
||||
*/
|
||||
"contentId"?: string;
|
||||
/**
|
||||
@@ -1704,7 +1708,7 @@ export namespace Components {
|
||||
*/
|
||||
"name": string;
|
||||
"setButtonTabindex": (value: number) => Promise<void>;
|
||||
"setFocus": () => Promise<void>;
|
||||
"setFocus": (ev: any) => Promise<void>;
|
||||
/**
|
||||
* the value of the radio.
|
||||
*/
|
||||
@@ -2030,6 +2034,10 @@ export namespace Components {
|
||||
* Sets the behavior for the cancel button. Defaults to `"never"`. Setting to `"focus"` shows the cancel button on focus. Setting to `"never"` hides the cancel button. Setting to `"always"` shows the cancel button regardless of focus state.
|
||||
*/
|
||||
"showCancelButton": 'never' | 'focus' | 'always';
|
||||
/**
|
||||
* Sets the behavior for the clear button. Defaults to `"focus"`. Setting to `"focus"` shows the clear button on focus if the input is not empty. Setting to `"never"` hides the clear button. Setting to `"always"` shows the clear button regardless of focus state, but only if the input is not empty.
|
||||
*/
|
||||
"showClearButton": 'never' | 'focus' | 'always';
|
||||
/**
|
||||
* If `true`, enable spellcheck on the input.
|
||||
*/
|
||||
@@ -2060,6 +2068,10 @@ export namespace Components {
|
||||
* If `true`, the segment buttons will overflow and the user can swipe to see them. In addition, this will disable the gesture to drag the indicator between the buttons in order to swipe to see hidden buttons.
|
||||
*/
|
||||
"scrollable": boolean;
|
||||
/**
|
||||
* If `true`, users will be able to swipe between segment buttons to activate them.
|
||||
*/
|
||||
"swipeGesture": boolean;
|
||||
/**
|
||||
* the value of the segment.
|
||||
*/
|
||||
@@ -2291,7 +2303,7 @@ export namespace Components {
|
||||
}
|
||||
interface IonSplitPane {
|
||||
/**
|
||||
* The content `id` of the split-pane's main content.
|
||||
* The `id` of the main content. When using a router this is typically `ion-router-outlet`. When not using a router, this is typically your main view's `ion-content`. This is not the id of the `ion-content` inside of your `ion-menu`.
|
||||
*/
|
||||
"contentId"?: string;
|
||||
/**
|
||||
@@ -3701,7 +3713,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"name"?: string;
|
||||
/**
|
||||
* Emitted when the toggle loses focus.
|
||||
* Emitted when the checkbox loses focus.
|
||||
*/
|
||||
"onIonBlur"?: (event: CustomEvent<void>) => void;
|
||||
/**
|
||||
@@ -3709,7 +3721,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"onIonChange"?: (event: CustomEvent<CheckboxChangeEventDetail>) => void;
|
||||
/**
|
||||
* Emitted when the toggle has focus.
|
||||
* Emitted when the checkbox has focus.
|
||||
*/
|
||||
"onIonFocus"?: (event: CustomEvent<void>) => void;
|
||||
/**
|
||||
@@ -3717,7 +3729,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"onIonStyle"?: (event: CustomEvent<StyleEventDetail>) => void;
|
||||
/**
|
||||
* The value of the toggle does not mean if it's checked or not, use the `checked` property for that. The value of a toggle is analogous to the value of a `<input type="checkbox">`, it's only used when the toggle participates in a native `<form>`.
|
||||
* The value of the checkbox does not mean if it's checked or not, use the `checked` property for that. The value of a checkbox is analogous to the value of an `<input type="checkbox">`, it's only used when the checkbox participates in a native `<form>`.
|
||||
*/
|
||||
"value"?: string;
|
||||
}
|
||||
@@ -3726,6 +3738,10 @@ declare namespace LocalJSX {
|
||||
* 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).
|
||||
*/
|
||||
"color"?: Color;
|
||||
/**
|
||||
* If `true`, the user cannot interact with the chip.
|
||||
*/
|
||||
"disabled"?: boolean;
|
||||
/**
|
||||
* The mode determines which platform styles to use.
|
||||
*/
|
||||
@@ -4169,7 +4185,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"accept"?: string;
|
||||
/**
|
||||
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
|
||||
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
|
||||
*/
|
||||
"autocapitalize"?: string;
|
||||
/**
|
||||
@@ -4552,7 +4568,7 @@ declare namespace LocalJSX {
|
||||
}
|
||||
interface IonMenu {
|
||||
/**
|
||||
* The content's id the menu should use.
|
||||
* The `id` of the main content. When using a router this is typically `ion-router-outlet`. When not using a router, this is typically your main view's `ion-content`. This is not the id of the `ion-content` inside of your `ion-menu`.
|
||||
*/
|
||||
"contentId"?: string;
|
||||
/**
|
||||
@@ -5324,6 +5340,10 @@ declare namespace LocalJSX {
|
||||
* Sets the behavior for the cancel button. Defaults to `"never"`. Setting to `"focus"` shows the cancel button on focus. Setting to `"never"` hides the cancel button. Setting to `"always"` shows the cancel button regardless of focus state.
|
||||
*/
|
||||
"showCancelButton"?: 'never' | 'focus' | 'always';
|
||||
/**
|
||||
* Sets the behavior for the clear button. Defaults to `"focus"`. Setting to `"focus"` shows the clear button on focus if the input is not empty. Setting to `"never"` hides the clear button. Setting to `"always"` shows the clear button regardless of focus state, but only if the input is not empty.
|
||||
*/
|
||||
"showClearButton"?: 'never' | 'focus' | 'always';
|
||||
/**
|
||||
* If `true`, enable spellcheck on the input.
|
||||
*/
|
||||
@@ -5366,6 +5386,10 @@ declare namespace LocalJSX {
|
||||
* If `true`, the segment buttons will overflow and the user can swipe to see them. In addition, this will disable the gesture to drag the indicator between the buttons in order to swipe to see hidden buttons.
|
||||
*/
|
||||
"scrollable"?: boolean;
|
||||
/**
|
||||
* If `true`, users will be able to swipe between segment buttons to activate them.
|
||||
*/
|
||||
"swipeGesture"?: boolean;
|
||||
/**
|
||||
* the value of the segment.
|
||||
*/
|
||||
@@ -5601,7 +5625,7 @@ declare namespace LocalJSX {
|
||||
}
|
||||
interface IonSplitPane {
|
||||
/**
|
||||
* The content `id` of the split-pane's main content.
|
||||
* The `id` of the main content. When using a router this is typically `ion-router-outlet`. When not using a router, this is typically your main view's `ion-content`. This is not the id of the `ion-content` inside of your `ion-menu`.
|
||||
*/
|
||||
"contentId"?: string;
|
||||
/**
|
||||
|
||||
@@ -155,6 +155,59 @@ async function presentActionSheet() {
|
||||
### React
|
||||
|
||||
```tsx
|
||||
/* Using with useIonActionSheet Hook */
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
IonButton,
|
||||
IonContent,
|
||||
IonPage,
|
||||
useIonActionSheet,
|
||||
} from '@ionic/react';
|
||||
|
||||
const ActionSheetExample: React.FC = () => {
|
||||
const [present, dismiss] = useIonActionSheet();
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present({
|
||||
buttons: [{ text: 'Ok' }, { text: 'Cancel' }],
|
||||
header: 'Action Sheet'
|
||||
})
|
||||
}
|
||||
>
|
||||
Show ActionSheet
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present([{ text: 'Ok' }, { text: 'Cancel' }], 'Action Sheet')
|
||||
}
|
||||
>
|
||||
Show ActionSheet using params
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
present([{ text: 'Ok' }, { text: 'Cancel' }], 'Action Sheet');
|
||||
setTimeout(dismiss, 3000);
|
||||
}}
|
||||
>
|
||||
Show ActionSheet, hide after 3 seconds
|
||||
</IonButton>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
```tsx
|
||||
/* Using with IonActionSheet Component */
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { IonActionSheet, IonContent, IonButton } from '@ionic/react';
|
||||
import { trash, share, caretForwardCircle, heart, close } from 'ionicons/icons';
|
||||
|
||||
@@ -71,7 +71,7 @@ export const testActionSheetAlert = async (
|
||||
|
||||
const alert = await page.find('ion-alert');
|
||||
await alert.waitForVisible();
|
||||
await page.waitFor(250);
|
||||
await page.waitForTimeout(250);
|
||||
|
||||
screenshotCompares.push(await page.compareScreenshot(`alert open`));
|
||||
|
||||
|
||||
@@ -1,4 +1,57 @@
|
||||
```tsx
|
||||
/* Using with useIonActionSheet Hook */
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
IonButton,
|
||||
IonContent,
|
||||
IonPage,
|
||||
useIonActionSheet,
|
||||
} from '@ionic/react';
|
||||
|
||||
const ActionSheetExample: React.FC = () => {
|
||||
const [present, dismiss] = useIonActionSheet();
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present({
|
||||
buttons: [{ text: 'Ok' }, { text: 'Cancel' }],
|
||||
header: 'Action Sheet'
|
||||
})
|
||||
}
|
||||
>
|
||||
Show ActionSheet
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present([{ text: 'Ok' }, { text: 'Cancel' }], 'Action Sheet')
|
||||
}
|
||||
>
|
||||
Show ActionSheet using params
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
present([{ text: 'Ok' }, { text: 'Cancel' }], 'Action Sheet');
|
||||
setTimeout(dismiss, 3000);
|
||||
}}
|
||||
>
|
||||
Show ActionSheet, hide after 3 seconds
|
||||
</IonButton>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
```tsx
|
||||
/* Using with IonActionSheet Component */
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { IonActionSheet, IonContent, IonButton } from '@ionic/react';
|
||||
import { trash, share, caretForwardCircle, heart, close } from 'ionicons/icons';
|
||||
|
||||
@@ -158,7 +158,7 @@ export class Alert implements ComponentInterface, OverlayInterface {
|
||||
|
||||
// If hitting arrow down or arrow right, move to the next radio
|
||||
// If we're on the last radio, move to the first radio
|
||||
if (['ArrowDown', 'ArrowRight'].includes(ev.key)) {
|
||||
if (['ArrowDown', 'ArrowRight'].includes(ev.code)) {
|
||||
nextEl = (index === radios.length - 1)
|
||||
? radios[0]
|
||||
: radios[index + 1];
|
||||
@@ -166,7 +166,7 @@ export class Alert implements ComponentInterface, OverlayInterface {
|
||||
|
||||
// If hitting arrow up or arrow left, move to the previous radio
|
||||
// If we're on the first radio, move to the last radio
|
||||
if (['ArrowUp', 'ArrowLeft'].includes(ev.key)) {
|
||||
if (['ArrowUp', 'ArrowLeft'].includes(ev.code)) {
|
||||
nextEl = (index === 0)
|
||||
? radios[radios.length - 1]
|
||||
: radios[index - 1];
|
||||
@@ -373,22 +373,24 @@ export class Alert implements ComponentInterface, OverlayInterface {
|
||||
return values;
|
||||
}
|
||||
|
||||
private renderAlertInputs(labelledBy: string | undefined) {
|
||||
private renderAlertInputs() {
|
||||
switch (this.inputType) {
|
||||
case 'checkbox': return this.renderCheckbox(labelledBy);
|
||||
case 'radio': return this.renderRadio(labelledBy);
|
||||
default: return this.renderInput(labelledBy);
|
||||
case 'checkbox': return this.renderCheckbox();
|
||||
case 'radio': return this.renderRadio();
|
||||
default: return this.renderInput();
|
||||
}
|
||||
}
|
||||
|
||||
private renderCheckbox(labelledby: string | undefined) {
|
||||
private renderCheckbox() {
|
||||
const inputs = this.processedInputs;
|
||||
const mode = getIonMode(this);
|
||||
|
||||
if (inputs.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="alert-checkbox-group" aria-labelledby={labelledby}>
|
||||
<div class="alert-checkbox-group">
|
||||
{ inputs.map(i => (
|
||||
<button
|
||||
type="button"
|
||||
@@ -422,13 +424,15 @@ export class Alert implements ComponentInterface, OverlayInterface {
|
||||
);
|
||||
}
|
||||
|
||||
private renderRadio(labelledby: string | undefined) {
|
||||
private renderRadio() {
|
||||
const inputs = this.processedInputs;
|
||||
|
||||
if (inputs.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="alert-radio-group" role="radiogroup" aria-labelledby={labelledby} aria-activedescendant={this.activeId}>
|
||||
<div class="alert-radio-group" role="radiogroup" aria-activedescendant={this.activeId}>
|
||||
{ inputs.map(i => (
|
||||
<button
|
||||
type="button"
|
||||
@@ -459,13 +463,13 @@ export class Alert implements ComponentInterface, OverlayInterface {
|
||||
);
|
||||
}
|
||||
|
||||
private renderInput(labelledby: string | undefined) {
|
||||
private renderInput() {
|
||||
const inputs = this.processedInputs;
|
||||
if (inputs.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div class="alert-input-group" aria-labelledby={labelledby}>
|
||||
<div class="alert-input-group">
|
||||
{ inputs.map(i => {
|
||||
if (i.type === 'textarea') {
|
||||
return (
|
||||
@@ -552,13 +556,6 @@ export class Alert implements ComponentInterface, OverlayInterface {
|
||||
const subHdrId = `alert-${overlayIndex}-sub-hdr`;
|
||||
const msgId = `alert-${overlayIndex}-msg`;
|
||||
|
||||
let labelledById: string | undefined;
|
||||
if (header !== undefined) {
|
||||
labelledById = hdrId;
|
||||
} else if (subHeader !== undefined) {
|
||||
labelledById = subHdrId;
|
||||
}
|
||||
|
||||
return (
|
||||
<Host
|
||||
role="dialog"
|
||||
@@ -589,7 +586,7 @@ export class Alert implements ComponentInterface, OverlayInterface {
|
||||
|
||||
<div id={msgId} class="alert-message" innerHTML={sanitizeDOMString(this.message)}></div>
|
||||
|
||||
{this.renderAlertInputs(labelledById)}
|
||||
{this.renderAlertInputs()}
|
||||
{this.renderAlertButtons()}
|
||||
|
||||
</div>
|
||||
|
||||
@@ -588,6 +588,48 @@ function presentAlertCheckbox() {
|
||||
### React
|
||||
|
||||
```tsx
|
||||
/* Using with useIonAlert Hook */
|
||||
|
||||
import React from 'react';
|
||||
import { IonButton, IonContent, IonPage, useIonAlert } from '@ionic/react';
|
||||
|
||||
const AlertExample: React.FC = () => {
|
||||
const [present] = useIonAlert();
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent fullscreen>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present({
|
||||
cssClass: 'my-css',
|
||||
header: 'Alert',
|
||||
message: 'alert from hook',
|
||||
buttons: [
|
||||
'Cancel',
|
||||
{ text: 'Ok', handler: (d) => console.log('ok pressed') },
|
||||
],
|
||||
onDidDismiss: (e) => console.log('did dismiss'),
|
||||
})
|
||||
}
|
||||
>
|
||||
Show Alert
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => present('hello with params', [{ text: 'Ok' }])}
|
||||
>
|
||||
Show Alert using params
|
||||
</IonButton>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
```tsx
|
||||
/* Using with IonAlert Component */
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { IonAlert, IonButton, IonContent } from '@ionic/react';
|
||||
|
||||
|
||||
@@ -1,4 +1,46 @@
|
||||
```tsx
|
||||
/* Using with useIonAlert Hook */
|
||||
|
||||
import React from 'react';
|
||||
import { IonButton, IonContent, IonPage, useIonAlert } from '@ionic/react';
|
||||
|
||||
const AlertExample: React.FC = () => {
|
||||
const [present] = useIonAlert();
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent fullscreen>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present({
|
||||
cssClass: 'my-css',
|
||||
header: 'Alert',
|
||||
message: 'alert from hook',
|
||||
buttons: [
|
||||
'Cancel',
|
||||
{ text: 'Ok', handler: (d) => console.log('ok pressed') },
|
||||
],
|
||||
onDidDismiss: (e) => console.log('did dismiss'),
|
||||
})
|
||||
}
|
||||
>
|
||||
Show Alert
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => present('hello with params', [{ text: 'Ok' }])}
|
||||
>
|
||||
Show Alert using params
|
||||
</IonButton>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
```tsx
|
||||
/* Using with IonAlert Component */
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { IonAlert, IonButton, IonContent } from '@ionic/react';
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ export class App implements ComponentInterface {
|
||||
|
||||
componentDidLoad() {
|
||||
if (Build.isBrowser) {
|
||||
rIC(() => {
|
||||
rIC(async () => {
|
||||
const isHybrid = isPlatform(window, 'hybrid');
|
||||
if (!config.getBoolean('_testing')) {
|
||||
import('../../utils/tap-click').then(module => module.startTapClick(config));
|
||||
@@ -24,8 +24,11 @@ export class App implements ComponentInterface {
|
||||
if (config.getBoolean('inputShims', needInputShims())) {
|
||||
import('../../utils/input-shims/input-shims').then(module => module.startInputShims(config));
|
||||
}
|
||||
const hardwareBackButtonModule = await import('../../utils/hardware-back-button');
|
||||
if (config.getBoolean('hardwareBackButton', isHybrid)) {
|
||||
import('../../utils/hardware-back-button').then(module => module.startHardwareBackButton());
|
||||
hardwareBackButtonModule.startHardwareBackButton();
|
||||
} else {
|
||||
hardwareBackButtonModule.blockHardwareBackButton();
|
||||
}
|
||||
if (typeof (window as any) !== 'undefined') {
|
||||
import('../../utils/keyboard/keyboard').then(module => module.startKeyboardAssist(window));
|
||||
|
||||
@@ -67,6 +67,7 @@ export class Backdrop implements ComponentInterface {
|
||||
return (
|
||||
<Host
|
||||
tabindex="-1"
|
||||
aria-hidden="true"
|
||||
class={{
|
||||
[mode]: true,
|
||||
'backdrop-hide': !this.visible,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Host, Prop
|
||||
import { getIonMode } from '../../global/ionic-global';
|
||||
import { AnimationBuilder, Color, RouterDirection } from '../../interface';
|
||||
import { AnchorInterface, ButtonInterface } from '../../utils/element-interface';
|
||||
import { hasShadowDom } from '../../utils/helpers';
|
||||
import { hasShadowDom, inheritAttributes } from '../../utils/helpers';
|
||||
import { createColorClasses, hostContext, openURL } from '../../utils/theme';
|
||||
|
||||
/**
|
||||
@@ -28,6 +28,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
|
||||
private inItem = false;
|
||||
private inListHeader = false;
|
||||
private inToolbar = false;
|
||||
private inheritedAttributes: { [k: string]: any } = {};
|
||||
|
||||
@Element() el!: HTMLElement;
|
||||
|
||||
@@ -134,6 +135,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
|
||||
this.inToolbar = !!this.el.closest('ion-buttons');
|
||||
this.inListHeader = !!this.el.closest('ion-list-header');
|
||||
this.inItem = !!this.el.closest('ion-item') || !!this.el.closest('ion-item-divider');
|
||||
this.inheritedAttributes = inheritAttributes(this.el, ['aria-label']);
|
||||
}
|
||||
|
||||
private get hasIconOnly() {
|
||||
@@ -184,7 +186,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
|
||||
|
||||
render() {
|
||||
const mode = getIonMode(this);
|
||||
const { buttonType, type, disabled, rel, target, size, href, color, expand, hasIconOnly, shape, strong } = this;
|
||||
const { buttonType, type, disabled, rel, target, size, href, color, expand, hasIconOnly, shape, strong, inheritedAttributes } = this;
|
||||
const finalSize = size === undefined && this.inItem ? 'small' : size;
|
||||
const TagType = href === undefined ? 'button' : 'a' as any;
|
||||
const attrs = (TagType === 'button')
|
||||
@@ -227,6 +229,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
|
||||
disabled={disabled}
|
||||
onFocus={this.onFocus}
|
||||
onBlur={this.onBlur}
|
||||
{...inheritedAttributes}
|
||||
>
|
||||
<span class="button-inner">
|
||||
<slot name="icon-only"></slot>
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
<ion-button class="wide">wide</ion-button>
|
||||
<ion-button class="large">large</ion-button>
|
||||
<ion-button class="round">rounded</ion-button>
|
||||
<ion-button aria-label="this is my custom label">custom aria-label</ion-button>
|
||||
|
||||
<!-- Custom Colors -->
|
||||
<ion-button class="custom">custom</ion-button>
|
||||
|
||||
@@ -40,8 +40,18 @@
|
||||
--checkmark-color: #{current-color(contrast)};
|
||||
}
|
||||
|
||||
button {
|
||||
label {
|
||||
@include input-cover();
|
||||
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
input {
|
||||
@include visually-hidden();
|
||||
}
|
||||
|
||||
.checkbox-icon {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Host, Prop
|
||||
|
||||
import { getIonMode } from '../../global/ionic-global';
|
||||
import { CheckboxChangeEventDetail, Color, StyleEventDetail } from '../../interface';
|
||||
import { findItemLabel, renderHiddenInput } from '../../utils/helpers';
|
||||
import { getAriaLabel, renderHiddenInput } from '../../utils/helpers';
|
||||
import { createColorClasses, hostContext } from '../../utils/theme';
|
||||
|
||||
/**
|
||||
@@ -22,7 +22,7 @@ import { createColorClasses, hostContext } from '../../utils/theme';
|
||||
export class Checkbox implements ComponentInterface {
|
||||
|
||||
private inputId = `ion-cb-${checkboxIds++}`;
|
||||
private buttonEl?: HTMLElement;
|
||||
private focusEl?: HTMLElement;
|
||||
|
||||
@Element() el!: HTMLElement;
|
||||
|
||||
@@ -54,11 +54,11 @@ export class Checkbox implements ComponentInterface {
|
||||
@Prop() disabled = false;
|
||||
|
||||
/**
|
||||
* The value of the toggle does not mean if it's checked or not, use the `checked`
|
||||
* The value of the checkbox does not mean if it's checked or not, use the `checked`
|
||||
* property for that.
|
||||
*
|
||||
* The value of a toggle is analogous to the value of a `<input type="checkbox">`,
|
||||
* it's only used when the toggle participates in a native `<form>`.
|
||||
* The value of a checkbox is analogous to the value of an `<input type="checkbox">`,
|
||||
* it's only used when the checkbox participates in a native `<form>`.
|
||||
*/
|
||||
@Prop() value = 'on';
|
||||
|
||||
@@ -68,12 +68,12 @@ export class Checkbox implements ComponentInterface {
|
||||
@Event() ionChange!: EventEmitter<CheckboxChangeEventDetail>;
|
||||
|
||||
/**
|
||||
* Emitted when the toggle has focus.
|
||||
* Emitted when the checkbox has focus.
|
||||
*/
|
||||
@Event() ionFocus!: EventEmitter<void>;
|
||||
|
||||
/**
|
||||
* Emitted when the toggle loses focus.
|
||||
* Emitted when the checkbox loses focus.
|
||||
*/
|
||||
@Event() ionBlur!: EventEmitter<void>;
|
||||
|
||||
@@ -109,12 +109,14 @@ export class Checkbox implements ComponentInterface {
|
||||
}
|
||||
|
||||
private setFocus() {
|
||||
if (this.buttonEl) {
|
||||
this.buttonEl.focus();
|
||||
if (this.focusEl) {
|
||||
this.focusEl.focus();
|
||||
}
|
||||
}
|
||||
|
||||
private onClick = () => {
|
||||
private onClick = (ev: any) => {
|
||||
ev.preventDefault();
|
||||
|
||||
this.setFocus();
|
||||
this.checked = !this.checked;
|
||||
this.indeterminate = false;
|
||||
@@ -129,14 +131,11 @@ export class Checkbox implements ComponentInterface {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { inputId, indeterminate, disabled, checked, value, color, el } = this;
|
||||
const labelId = inputId + '-lbl';
|
||||
const { color, checked, disabled, el, indeterminate, inputId, name, value } = this;
|
||||
const mode = getIonMode(this);
|
||||
const label = findItemLabel(el);
|
||||
if (label) {
|
||||
label.id = labelId;
|
||||
}
|
||||
renderHiddenInput(true, el, this.name, (checked ? value : ''), disabled);
|
||||
const { label, labelId, labelText } = getAriaLabel(el, inputId);
|
||||
|
||||
renderHiddenInput(true, el, name, (checked ? value : ''), disabled);
|
||||
|
||||
let path = indeterminate
|
||||
? <path d="M6 12L18 12" part="mark" />
|
||||
@@ -151,10 +150,10 @@ export class Checkbox implements ComponentInterface {
|
||||
return (
|
||||
<Host
|
||||
onClick={this.onClick}
|
||||
role="checkbox"
|
||||
aria-disabled={disabled ? 'true' : null}
|
||||
aria-labelledby={label ? labelId : null}
|
||||
aria-checked={`${checked}`}
|
||||
aria-labelledby={labelId}
|
||||
aria-hidden={disabled ? 'true' : null}
|
||||
role="checkbox"
|
||||
class={createColorClasses(color, {
|
||||
[mode]: true,
|
||||
'in-item': hostContext('ion-item', el),
|
||||
@@ -167,14 +166,18 @@ export class Checkbox implements ComponentInterface {
|
||||
<svg class="checkbox-icon" viewBox="0 0 24 24" part="container">
|
||||
{path}
|
||||
</svg>
|
||||
<button
|
||||
type="button"
|
||||
onFocus={this.onFocus}
|
||||
onBlur={this.onBlur}
|
||||
disabled={this.disabled}
|
||||
ref={btnEl => this.buttonEl = btnEl}
|
||||
>
|
||||
</button>
|
||||
<label htmlFor={inputId}>
|
||||
{labelText}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
aria-checked={`${checked}`}
|
||||
disabled={disabled}
|
||||
id={inputId}
|
||||
onFocus={() => this.onFocus()}
|
||||
onBlur={() => this.onBlur()}
|
||||
ref={focusEl => this.focusEl = focusEl}
|
||||
/>
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -228,8 +228,8 @@ export class CheckboxExample {
|
||||
<ion-label>{{entry.val}}</ion-label>
|
||||
<ion-checkbox
|
||||
slot="end"
|
||||
@input="entry.checked = $event.target.value"
|
||||
:value="entry.isChecked">
|
||||
@update:modelValue="entry.isChecked = $event"
|
||||
:modelValue="entry.isChecked">
|
||||
</ion-checkbox>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
@@ -266,16 +266,16 @@ export default defineComponent({
|
||||
| `indeterminate` | `indeterminate` | If `true`, the checkbox will visually appear as indeterminate. | `boolean` | `false` |
|
||||
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` |
|
||||
| `name` | `name` | The name of the control, which is submitted with the form data. | `string` | `this.inputId` |
|
||||
| `value` | `value` | The value of the toggle does not mean if it's checked or not, use the `checked` property for that. The value of a toggle is analogous to the value of a `<input type="checkbox">`, it's only used when the toggle participates in a native `<form>`. | `string` | `'on'` |
|
||||
| `value` | `value` | The value of the checkbox does not mean if it's checked or not, use the `checked` property for that. The value of a checkbox is analogous to the value of an `<input type="checkbox">`, it's only used when the checkbox participates in a native `<form>`. | `string` | `'on'` |
|
||||
|
||||
|
||||
## Events
|
||||
|
||||
| Event | Description | Type |
|
||||
| ----------- | ---------------------------------------------- | ---------------------------------------- |
|
||||
| `ionBlur` | Emitted when the toggle loses focus. | `CustomEvent<void>` |
|
||||
| `ionBlur` | Emitted when the checkbox loses focus. | `CustomEvent<void>` |
|
||||
| `ionChange` | Emitted when the checked property has changed. | `CustomEvent<CheckboxChangeEventDetail>` |
|
||||
| `ionFocus` | Emitted when the toggle has focus. | `CustomEvent<void>` |
|
||||
| `ionFocus` | Emitted when the checkbox has focus. | `CustomEvent<void>` |
|
||||
|
||||
|
||||
## Shadow Parts
|
||||
|
||||
@@ -26,6 +26,11 @@
|
||||
</ion-header>
|
||||
|
||||
<ion-content id="content">
|
||||
<ion-item onClick="clickItem()">
|
||||
<ion-label>Clickable Item</ion-label>
|
||||
<ion-checkbox></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Default</ion-label>
|
||||
<ion-checkbox checked></ion-checkbox>
|
||||
@@ -38,7 +43,7 @@
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Secondary</ion-label>
|
||||
<ion-checkbox checked color="secondary"></ion-checkbox>
|
||||
<ion-checkbox disabled checked color="secondary"></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
@@ -103,6 +108,35 @@
|
||||
</ion-content>
|
||||
|
||||
</ion-app>
|
||||
|
||||
<script>
|
||||
const inputs = document.querySelectorAll('ion-checkbox');
|
||||
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
const input = inputs[i];
|
||||
|
||||
input.addEventListener('ionBlur', function() {
|
||||
console.log('Listen ionBlur: fired');
|
||||
});
|
||||
|
||||
input.addEventListener('ionFocus', function() {
|
||||
console.log('Listen ionFocus: fired');
|
||||
});
|
||||
|
||||
input.addEventListener('ionChange', function(ev) {
|
||||
console.log('Listen ionChange: fired', ev.detail);
|
||||
});
|
||||
|
||||
input.addEventListener('click', function() {
|
||||
console.log('Listen click: fired');
|
||||
});
|
||||
}
|
||||
|
||||
const clickItem = () => {
|
||||
console.log('Item click: fired');
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -31,22 +31,22 @@
|
||||
<div class="ion-padding-start">
|
||||
<!-- Default to unchecked -->
|
||||
<label for="unchecked">Unchecked</label>
|
||||
<input name="unchecked" type="checkbox">
|
||||
<input name="unchecked" id="unchecked" type="checkbox">
|
||||
<br>
|
||||
|
||||
<!-- Default to checked -->
|
||||
<label for="checked">Checked</label>
|
||||
<input name="checked" type="checkbox" checked />
|
||||
<input name="checked" id="checked" type="checkbox" checked />
|
||||
<br>
|
||||
|
||||
<!-- Default to indeterminate -->
|
||||
<label for="indeterminate">Indeterminate</label>
|
||||
<input name="indeterminate" type="checkbox" class="indeterminate">
|
||||
<input name="indeterminate" id="indeterminate" type="checkbox" class="indeterminate">
|
||||
<br>
|
||||
|
||||
<!-- Default to checked / indeterminate -->
|
||||
<label for="both">Checked / Indeterminate</label>
|
||||
<input name="both" type="checkbox" checked class="indeterminate">
|
||||
<input name="both" id="both" type="checkbox" checked class="indeterminate">
|
||||
<br>
|
||||
</div>
|
||||
|
||||
@@ -81,15 +81,15 @@
|
||||
</ion-label>
|
||||
</ion-list-header>
|
||||
<div class="ion-padding-start">
|
||||
<ion-checkbox indeterminate></ion-checkbox>
|
||||
<ion-checkbox indeterminate color="secondary"></ion-checkbox>
|
||||
<ion-checkbox indeterminate color="tertiary"></ion-checkbox>
|
||||
<ion-checkbox indeterminate color="success"></ion-checkbox>
|
||||
<ion-checkbox indeterminate color="warning"></ion-checkbox>
|
||||
<ion-checkbox indeterminate color="danger"></ion-checkbox>
|
||||
<ion-checkbox indeterminate color="dark"></ion-checkbox>
|
||||
<ion-checkbox indeterminate color="medium"></ion-checkbox>
|
||||
<ion-checkbox indeterminate color="light"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Default Indeterminate" indeterminate></ion-checkbox>
|
||||
<ion-checkbox aria-label="Secondary Indeterminate" indeterminate color="secondary"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Tertiary Indeterminate" indeterminate color="tertiary"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Success Indeterminate" indeterminate color="success"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Warning Indeterminate" indeterminate color="warning"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Danger Indeterminate" indeterminate color="danger"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Dark Indeterminate" indeterminate color="dark"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Medium Indeterminate" indeterminate color="medium"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Light Indeterminate" indeterminate color="light"></ion-checkbox>
|
||||
</div>
|
||||
|
||||
<ion-list-header>
|
||||
@@ -100,20 +100,20 @@
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<ion-checkbox name="tall" id="tall" indeterminate></ion-checkbox>
|
||||
<label for="tall">Tall Things</label>
|
||||
<ion-checkbox aria-labelledby="tall-label-0" indeterminate></ion-checkbox>
|
||||
<label id="tall-label-0">Tall Things</label>
|
||||
<ul>
|
||||
<li>
|
||||
<ion-checkbox name="tall-1" id="tall-1" checked></ion-checkbox>
|
||||
<label for="tall-1">Skyscrapers</label>
|
||||
<ion-checkbox aria-labelledby="tall-label-1" checked></ion-checkbox>
|
||||
<label id="tall-label-1">Skyscrapers</label>
|
||||
</li>
|
||||
<li>
|
||||
<ion-checkbox name="tall-2" id="tall-2"></ion-checkbox>
|
||||
<label for="tall-2">Trees</label>
|
||||
<ion-checkbox aria-labelledby="tall-label-2"></ion-checkbox>
|
||||
<label id="tall-label-2">Trees</label>
|
||||
</li>
|
||||
<li>
|
||||
<ion-checkbox name="tall-2" id="tall-2"></ion-checkbox>
|
||||
<label for="tall-2">Giants</label>
|
||||
<ion-checkbox aria-labelledby="tall-label-3"></ion-checkbox>
|
||||
<label id="tall-label-3">Giants</label>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
@@ -13,67 +13,67 @@
|
||||
|
||||
<body class="ion-padding">
|
||||
<h1>Default</h1>
|
||||
<ion-checkbox></ion-checkbox>
|
||||
<ion-checkbox checked></ion-checkbox>
|
||||
<ion-checkbox disabled></ion-checkbox>
|
||||
<ion-checkbox disabled checked></ion-checkbox>
|
||||
<ion-checkbox aria-label="Default Checkbox"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Default Checkbox" checked></ion-checkbox>
|
||||
<ion-checkbox aria-label="Default Checkbox" disabled></ion-checkbox>
|
||||
<ion-checkbox aria-label="Default Checkbox" disabled checked></ion-checkbox>
|
||||
|
||||
<h1>Colors</h1>
|
||||
<ion-checkbox color="primary"></ion-checkbox>
|
||||
<ion-checkbox color="secondary"></ion-checkbox>
|
||||
<ion-checkbox color="tertiary"></ion-checkbox>
|
||||
<ion-checkbox color="success"></ion-checkbox>
|
||||
<ion-checkbox color="warning"></ion-checkbox>
|
||||
<ion-checkbox color="danger"></ion-checkbox>
|
||||
<ion-checkbox color="light"></ion-checkbox>
|
||||
<ion-checkbox color="medium"></ion-checkbox>
|
||||
<ion-checkbox color="dark"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Primary" color="primary"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Secondary" color="secondary"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Tertiary" color="tertiary"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Success" color="success"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Warning" color="warning"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Danger" color="danger"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Light" color="light"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Medium" color="medium"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Dark" color="dark"></ion-checkbox>
|
||||
|
||||
<hr>
|
||||
|
||||
<ion-checkbox checked color="primary"></ion-checkbox>
|
||||
<ion-checkbox checked color="secondary"></ion-checkbox>
|
||||
<ion-checkbox checked color="tertiary"></ion-checkbox>
|
||||
<ion-checkbox checked color="success"></ion-checkbox>
|
||||
<ion-checkbox checked color="warning"></ion-checkbox>
|
||||
<ion-checkbox checked color="danger"></ion-checkbox>
|
||||
<ion-checkbox checked color="light"></ion-checkbox>
|
||||
<ion-checkbox checked color="medium"></ion-checkbox>
|
||||
<ion-checkbox checked color="dark"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Primary" checked color="primary"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Secondary" checked color="secondary"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Tertiary" checked color="tertiary"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Success" checked color="success"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Warning" checked color="warning"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Danger" checked color="danger"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Light" checked color="light"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Medium" checked color="medium"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Dark" checked color="dark"></ion-checkbox>
|
||||
|
||||
<hr>
|
||||
|
||||
<ion-checkbox checked disabled color="primary"></ion-checkbox>
|
||||
<ion-checkbox checked disabled color="secondary"></ion-checkbox>
|
||||
<ion-checkbox checked disabled color="tertiary"></ion-checkbox>
|
||||
<ion-checkbox checked disabled color="success"></ion-checkbox>
|
||||
<ion-checkbox checked disabled color="warning"></ion-checkbox>
|
||||
<ion-checkbox checked disabled color="danger"></ion-checkbox>
|
||||
<ion-checkbox checked disabled color="light"></ion-checkbox>
|
||||
<ion-checkbox checked disabled color="medium"></ion-checkbox>
|
||||
<ion-checkbox checked disabled color="dark"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Primary" checked disabled color="primary"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Secondary" checked disabled color="secondary"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Tertiary" checked disabled color="tertiary"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Success" checked disabled color="success"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Warning" checked disabled color="warning"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Danger" checked disabled color="danger"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Light" checked disabled color="light"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Medium" checked disabled color="medium"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Dark" checked disabled color="dark"></ion-checkbox>
|
||||
|
||||
<h1>Custom</h1>
|
||||
<ion-checkbox class="custom"></ion-checkbox>
|
||||
<ion-checkbox class="custom" checked></ion-checkbox>
|
||||
<ion-checkbox class="custom" disabled></ion-checkbox>
|
||||
<ion-checkbox class="custom" disabled checked></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Custom" class="custom"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Custom" class="custom" checked></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Custom" class="custom" disabled></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Custom" class="custom" disabled checked></ion-checkbox>
|
||||
|
||||
<h1>Custom: checked</h1>
|
||||
<ion-checkbox class="custom-checked"></ion-checkbox>
|
||||
<ion-checkbox class="custom-checked" checked></ion-checkbox>
|
||||
<ion-checkbox class="custom-checked" disabled></ion-checkbox>
|
||||
<ion-checkbox class="custom-checked" disabled checked></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Custom" class="custom-checked"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Custom" class="custom-checked" checked></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Custom" class="custom-checked" disabled></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Custom" class="custom-checked" disabled checked></ion-checkbox>
|
||||
|
||||
<h1>Custom: light</h1>
|
||||
<ion-checkbox class="custom-light"></ion-checkbox>
|
||||
<ion-checkbox class="custom-light" checked></ion-checkbox>
|
||||
<ion-checkbox class="custom-light" disabled></ion-checkbox>
|
||||
<ion-checkbox class="custom-light" disabled checked></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Custom Light" class="custom-light"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Custom Light" class="custom-light" checked></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Custom Light" class="custom-light" disabled></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Custom Light" class="custom-light" disabled checked></ion-checkbox>
|
||||
|
||||
<h1>Custom: transition</h1>
|
||||
<ion-checkbox class="custom-transition"></ion-checkbox>
|
||||
<ion-checkbox class="custom-transition" checked></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Custom Transition" class="custom-transition"></ion-checkbox>
|
||||
<ion-checkbox aria-label="Checkbox Custom Transition" class="custom-transition" checked></ion-checkbox>
|
||||
|
||||
<style>
|
||||
.custom {
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
<ion-label>{{entry.val}}</ion-label>
|
||||
<ion-checkbox
|
||||
slot="end"
|
||||
@input="entry.checked = $event.target.value"
|
||||
:value="entry.isChecked">
|
||||
@update:modelValue="entry.isChecked = $event"
|
||||
:modelValue="entry.isChecked">
|
||||
</ion-checkbox>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
@@ -37,6 +37,11 @@
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:host(.chip-disabled) {
|
||||
cursor: default;
|
||||
opacity: .4;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
// Chip Colors
|
||||
// ---------------------------------------------
|
||||
|
||||
@@ -28,14 +28,21 @@ export class Chip implements ComponentInterface {
|
||||
*/
|
||||
@Prop() outline = false;
|
||||
|
||||
/**
|
||||
* If `true`, the user cannot interact with the chip.
|
||||
*/
|
||||
@Prop() disabled = false;
|
||||
|
||||
render() {
|
||||
const mode = getIonMode(this);
|
||||
|
||||
return (
|
||||
<Host
|
||||
aria-disabled={this.disabled ? 'true' : null}
|
||||
class={createColorClasses(this.color, {
|
||||
[mode]: true,
|
||||
'chip-outline': this.outline,
|
||||
'chip-disabled': this.disabled,
|
||||
'ion-activatable': true,
|
||||
})}
|
||||
>
|
||||
|
||||
@@ -7,7 +7,7 @@ Chips represent complex entities in small blocks, such as a contact. A chip can
|
||||
|
||||
## Usage
|
||||
|
||||
### Angular / javascript
|
||||
### Angular
|
||||
|
||||
```html
|
||||
<ion-chip>
|
||||
@@ -22,6 +22,60 @@ Chips represent complex entities in small blocks, such as a contact. A chip can
|
||||
<ion-label color="dark">Secondary w/ Dark label</ion-label>
|
||||
</ion-chip>
|
||||
|
||||
<ion-chip [disabled]="true">
|
||||
<ion-label>Disabled Chip</ion-label>
|
||||
</ion-chip>
|
||||
|
||||
<ion-chip>
|
||||
<ion-icon name="pin"></ion-icon>
|
||||
<ion-label>Default</ion-label>
|
||||
</ion-chip>
|
||||
|
||||
<ion-chip>
|
||||
<ion-icon name="heart" color="dark"></ion-icon>
|
||||
<ion-label>Default</ion-label>
|
||||
</ion-chip>
|
||||
|
||||
<ion-chip>
|
||||
<ion-label>Button Chip</ion-label>
|
||||
<ion-icon name="close-circle"></ion-icon>
|
||||
</ion-chip>
|
||||
|
||||
<ion-chip>
|
||||
<ion-icon name="pin" color="primary"></ion-icon>
|
||||
<ion-label>Icon Chip</ion-label>
|
||||
<ion-icon name="close"></ion-icon>
|
||||
</ion-chip>
|
||||
|
||||
<ion-chip>
|
||||
<ion-avatar>
|
||||
<img src="https://gravatar.com/avatar/dba6bae8c566f9d4041fb9cd9ada7741?d=identicon&f=y">
|
||||
</ion-avatar>
|
||||
<ion-label>Avatar Chip</ion-label>
|
||||
<ion-icon name="close-circle"></ion-icon>
|
||||
</ion-chip>
|
||||
```
|
||||
|
||||
|
||||
### Javascript
|
||||
|
||||
```html
|
||||
<ion-chip>
|
||||
<ion-label>Default</ion-label>
|
||||
</ion-chip>
|
||||
|
||||
<ion-chip>
|
||||
<ion-label color="secondary">Secondary Label</ion-label>
|
||||
</ion-chip>
|
||||
|
||||
<ion-chip color="secondary">
|
||||
<ion-label color="dark">Secondary w/ Dark label</ion-label>
|
||||
</ion-chip>
|
||||
|
||||
<ion-chip disabled="true">
|
||||
<ion-label>Disabled Chip</ion-label>
|
||||
</ion-chip>
|
||||
|
||||
<ion-chip>
|
||||
<ion-icon name="pin"></ion-icon>
|
||||
<ion-label>Default</ion-label>
|
||||
@@ -81,6 +135,10 @@ export const ChipExamples: React.FC = () => {
|
||||
<IonLabel color="dark">Secondary w/ Dark label</IonLabel>
|
||||
</IonChip>
|
||||
|
||||
<IonChip disabled={true}>
|
||||
<IonLabel>Disabled Chip</IonLabel>
|
||||
</IonChip>
|
||||
|
||||
<IonChip>
|
||||
<IonIcon icon={pin} />
|
||||
<IonLabel>Default</IonLabel>
|
||||
@@ -191,6 +249,10 @@ export class ChipExample {
|
||||
<ion-label color="dark">Secondary w/ Dark label</ion-label>
|
||||
</ion-chip>
|
||||
|
||||
<ion-chip :disabled="true">
|
||||
<ion-label>Disabled Chip</ion-label>
|
||||
</ion-chip>
|
||||
|
||||
<ion-chip>
|
||||
<ion-icon :icon="pin"></ion-icon>
|
||||
<ion-label>Default</ion-label>
|
||||
@@ -240,11 +302,12 @@ export default defineComponent({
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Attribute | Description | Type | Default |
|
||||
| --------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | ----------- |
|
||||
| `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` |
|
||||
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` |
|
||||
| `outline` | `outline` | Display an outline style button. | `boolean` | `false` |
|
||||
| Property | Attribute | Description | Type | Default |
|
||||
| ---------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | ----------- |
|
||||
| `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` |
|
||||
| `disabled` | `disabled` | If `true`, the user cannot interact with the chip. | `boolean` | `false` |
|
||||
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` |
|
||||
| `outline` | `outline` | Display an outline style button. | `boolean` | `false` |
|
||||
|
||||
|
||||
## CSS Custom Properties
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<ion-app>
|
||||
<ion-content>
|
||||
<h3>Default</h3>
|
||||
|
||||
<p>
|
||||
<ion-chip>
|
||||
<ion-label>Default</ion-label>
|
||||
@@ -195,6 +196,26 @@
|
||||
</ion-chip>
|
||||
</p>
|
||||
|
||||
<h3>Disabled</h3>
|
||||
|
||||
<p>
|
||||
<ion-chip disabled>
|
||||
<ion-label>Disabled</ion-label>
|
||||
</ion-chip>
|
||||
<ion-chip outline color="danger" class="ion-focused" disabled>
|
||||
<ion-label>Disabled Outline</ion-label>
|
||||
</ion-chip>
|
||||
<ion-chip color="secondary" class="ion-focused" disabled>
|
||||
<ion-icon name="checkmark-circle"></ion-icon>
|
||||
<ion-label>Disabled Secondary with Icon</ion-label>
|
||||
</ion-chip>
|
||||
<ion-chip outline class="ion-focused" disabled>
|
||||
<ion-icon name="git-pull-request"></ion-icon>
|
||||
<ion-label>Disabled Outline with Icon and Avatar</ion-label>
|
||||
<ion-icon name="close-circle"></ion-icon>
|
||||
</ion-chip>
|
||||
</p>
|
||||
|
||||
<h3>Custom</h3>
|
||||
|
||||
<!-- Custom Font -->
|
||||
@@ -246,11 +267,6 @@
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
ion-chip {
|
||||
display: inline-block !important;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.wide {
|
||||
--background: #d1f3ff;
|
||||
--background-hover: #add8e6;
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
<ion-label color="dark">Secondary w/ Dark label</ion-label>
|
||||
</ion-chip>
|
||||
|
||||
<ion-chip [disabled]="true">
|
||||
<ion-label>Disabled Chip</ion-label>
|
||||
</ion-chip>
|
||||
|
||||
<ion-chip>
|
||||
<ion-icon name="pin"></ion-icon>
|
||||
<ion-label>Default</ion-label>
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
<ion-label color="dark">Secondary w/ Dark label</ion-label>
|
||||
</ion-chip>
|
||||
|
||||
<ion-chip disabled="true">
|
||||
<ion-label>Disabled Chip</ion-label>
|
||||
</ion-chip>
|
||||
|
||||
<ion-chip>
|
||||
<ion-icon name="pin"></ion-icon>
|
||||
<ion-label>Default</ion-label>
|
||||
|
||||
@@ -24,6 +24,10 @@ export const ChipExamples: React.FC = () => {
|
||||
<IonLabel color="dark">Secondary w/ Dark label</IonLabel>
|
||||
</IonChip>
|
||||
|
||||
<IonChip disabled={true}>
|
||||
<IonLabel>Disabled Chip</IonLabel>
|
||||
</IonChip>
|
||||
|
||||
<IonChip>
|
||||
<IonIcon icon={pin} />
|
||||
<IonLabel>Default</IonLabel>
|
||||
@@ -57,4 +61,4 @@ export const ChipExamples: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
```
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
<ion-label color="dark">Secondary w/ Dark label</ion-label>
|
||||
</ion-chip>
|
||||
|
||||
<ion-chip :disabled="true">
|
||||
<ion-label>Disabled Chip</ion-label>
|
||||
</ion-chip>
|
||||
|
||||
<ion-chip>
|
||||
<ion-icon :icon="pin"></ion-icon>
|
||||
<ion-label>Default</ion-label>
|
||||
|
||||
@@ -100,7 +100,9 @@
|
||||
*
|
||||
* See: https://bugs.webkit.org/show_bug.cgi?id=216701
|
||||
*/
|
||||
will-change: scroll-position, transform;
|
||||
z-index: 0;
|
||||
|
||||
will-change: scroll-position;
|
||||
}
|
||||
|
||||
.scroll-y {
|
||||
|
||||
@@ -5,6 +5,11 @@ const getActiveElementText = async (page) => {
|
||||
return await page.evaluate(el => el && el.textContent, activeElement);
|
||||
}
|
||||
|
||||
const getActiveElementClass = async (page) => {
|
||||
const activeElement = await page.evaluateHandle(() => document.activeElement);
|
||||
return await page.evaluate(el => el && el.className, activeElement);
|
||||
}
|
||||
|
||||
test('datetime/picker: focus trap', async () => {
|
||||
const page = await newE2EPage({ url: '/src/components/datetime/test/basic?ionic:_testing=true' });
|
||||
await page.click('#datetime-part');
|
||||
@@ -15,7 +20,7 @@ test('datetime/picker: focus trap', async () => {
|
||||
expect(datetime).not.toBe(null);
|
||||
|
||||
// TODO fix
|
||||
await page.waitFor(100);
|
||||
await page.waitForTimeout(250);
|
||||
|
||||
await page.keyboard.press('Tab');
|
||||
|
||||
@@ -26,8 +31,8 @@ test('datetime/picker: focus trap', async () => {
|
||||
await page.keyboard.press('Tab');
|
||||
await page.keyboard.up('Shift');
|
||||
|
||||
const activeElementTextTwo = await getActiveElementText(page);
|
||||
expect(activeElementTextTwo).toEqual('1920');
|
||||
const activeElementClass = await getActiveElementClass(page);
|
||||
expect(activeElementClass).toEqual('picker-opt');
|
||||
|
||||
await page.keyboard.press('Tab');
|
||||
|
||||
@@ -49,7 +54,7 @@ test('datetime: basic', async () => {
|
||||
|
||||
const picker = await page.find('ion-picker');
|
||||
await picker.waitForVisible();
|
||||
await page.waitFor(250);
|
||||
await page.waitForTimeout(250);
|
||||
|
||||
compare = await page.compareScreenshot('should open custom picker');
|
||||
expect(compare).toMatchScreenshot();
|
||||
@@ -65,7 +70,7 @@ test('datetime: basic-rtl', async () => {
|
||||
|
||||
const picker = await page.find('ion-picker');
|
||||
await picker.waitForVisible();
|
||||
await page.waitFor(250);
|
||||
await page.waitForTimeout(250);
|
||||
|
||||
const compare = await page.compareScreenshot('should open custom picker');
|
||||
expect(compare).toMatchScreenshot();
|
||||
|
||||
@@ -13,14 +13,14 @@ test('datetime: standalone', async () => {
|
||||
|
||||
const picker = await page.find('ion-picker');
|
||||
await picker.waitForVisible();
|
||||
await page.waitFor(250);
|
||||
await page.waitForTimeout(250);
|
||||
|
||||
compare = await page.compareScreenshot('should open basic picker');
|
||||
expect(compare).toMatchScreenshot();
|
||||
|
||||
const octoberOpt = await page.find({ text: 'October' });
|
||||
await octoberOpt.click();
|
||||
await page.waitFor(500);
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
compare = await page.compareScreenshot('should click "October" option');
|
||||
expect(compare).toMatchScreenshot();
|
||||
|
||||
@@ -20,7 +20,7 @@ export const testFab = async (
|
||||
const fab = await getFabComponent(page, selector);
|
||||
await fab.click();
|
||||
|
||||
await page.waitFor(250);
|
||||
await page.waitForTimeout(250);
|
||||
|
||||
await ensureFabState(fab, 'active');
|
||||
|
||||
@@ -29,7 +29,7 @@ export const testFab = async (
|
||||
const fabButton = await getFabButton(fab);
|
||||
await fabButton.click();
|
||||
|
||||
await page.waitFor(250);
|
||||
await page.waitForTimeout(250);
|
||||
|
||||
await ensureFabState(fab, 'inactive');
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ export class Header implements ComponentInterface {
|
||||
* as well as progressively showing/hiding the main header
|
||||
* border as the top-most toolbar collapses or expands.
|
||||
*/
|
||||
const toolbarIntersection = (ev: any) => { handleToolbarIntersection(ev, mainHeaderIndex, scrollHeaderIndex); };
|
||||
const toolbarIntersection = (ev: any) => { handleToolbarIntersection(ev, mainHeaderIndex, scrollHeaderIndex, this.scrollEl!); };
|
||||
|
||||
this.intersectionObserver = new IntersectionObserver(toolbarIntersection, { root: contentEl, threshold: [0.25, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1] });
|
||||
this.intersectionObserver.observe(scrollHeaderIndex.toolbars[scrollHeaderIndex.toolbars.length - 1].el);
|
||||
|
||||
@@ -72,7 +72,7 @@ export const setToolbarBackgroundOpacity = (toolbar: ToolbarIndex, opacity?: num
|
||||
}
|
||||
};
|
||||
|
||||
const handleToolbarBorderIntersection = (ev: any, mainHeaderIndex: HeaderIndex) => {
|
||||
const handleToolbarBorderIntersection = (ev: any, mainHeaderIndex: HeaderIndex, scrollTop: number) => {
|
||||
if (!ev[0].isIntersecting) { return; }
|
||||
|
||||
/**
|
||||
@@ -80,8 +80,13 @@ const handleToolbarBorderIntersection = (ev: any, mainHeaderIndex: HeaderIndex)
|
||||
* does not always reset the scrollTop position to 0 when letting go. It will
|
||||
* set to 1 once the rubber band effect has ended. This causes the background to
|
||||
* appear slightly on certain app setups.
|
||||
*
|
||||
* Additionally, we check if user is rubber banding (scrolling is negative)
|
||||
* as this can mean they are using pull to refresh. Once the refresher starts,
|
||||
* the content is transformed which can cause the intersection observer to erroneously
|
||||
* fire here as well.
|
||||
*/
|
||||
const scale = (ev[0].intersectionRatio > 0.9) ? 0 : ((1 - ev[0].intersectionRatio) * 100) / 75;
|
||||
const scale = (ev[0].intersectionRatio > 0.9 || scrollTop <= 0) ? 0 : ((1 - ev[0].intersectionRatio) * 100) / 75;
|
||||
|
||||
mainHeaderIndex.toolbars.forEach(toolbar => {
|
||||
setToolbarBackgroundOpacity(toolbar, (scale === 1) ? undefined : scale);
|
||||
@@ -93,9 +98,10 @@ const handleToolbarBorderIntersection = (ev: any, mainHeaderIndex: HeaderIndex)
|
||||
* and show the primary toolbar content. If the toolbars are not intersecting,
|
||||
* hide the primary toolbar content and show the scrollable toolbar content
|
||||
*/
|
||||
export const handleToolbarIntersection = (ev: any, mainHeaderIndex: HeaderIndex, scrollHeaderIndex: HeaderIndex) => {
|
||||
export const handleToolbarIntersection = (ev: any, mainHeaderIndex: HeaderIndex, scrollHeaderIndex: HeaderIndex, scrollEl: HTMLElement) => {
|
||||
writeTask(() => {
|
||||
handleToolbarBorderIntersection(ev, mainHeaderIndex);
|
||||
const scrollTop = scrollEl.scrollTop;
|
||||
handleToolbarBorderIntersection(ev, mainHeaderIndex, scrollTop);
|
||||
|
||||
const event = ev[0];
|
||||
|
||||
@@ -127,7 +133,7 @@ export const handleToolbarIntersection = (ev: any, mainHeaderIndex: HeaderIndex,
|
||||
|
||||
const hasValidIntersection = (intersection.x === 0 && intersection.y === 0) || (intersection.width !== 0 && intersection.height !== 0);
|
||||
|
||||
if (hasValidIntersection) {
|
||||
if (hasValidIntersection && scrollTop > 0) {
|
||||
setHeaderActive(mainHeaderIndex);
|
||||
setHeaderActive(scrollHeaderIndex, false);
|
||||
setToolbarBackgroundOpacity(mainHeaderIndex.toolbars[0]);
|
||||
|
||||
@@ -169,3 +169,18 @@
|
||||
:host(.has-focus) button {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
|
||||
// Item Floating: Placeholder
|
||||
// ----------------------------------------------------------------
|
||||
// When used with a floating item the placeholder should hide
|
||||
|
||||
:host-context(.item-label-floating.item-has-placeholder:not(.item-has-value)) {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
:host-context(.item-label-floating.item-has-placeholder:not(.item-has-value).item-has-focus) {
|
||||
transition: opacity 0.15s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Build, Component, ComponentInterface, Element, Event, EventEmitter, Hos
|
||||
|
||||
import { getIonMode } from '../../global/ionic-global';
|
||||
import { AutocompleteTypes, Color, InputChangeEventDetail, StyleEventDetail, TextFieldTypes } from '../../interface';
|
||||
import { debounceEvent, findItemLabel } from '../../utils/helpers';
|
||||
import { debounceEvent, findItemLabel, inheritAttributes } from '../../utils/helpers';
|
||||
import { createColorClasses } from '../../utils/theme';
|
||||
|
||||
/**
|
||||
@@ -21,7 +21,7 @@ export class Input implements ComponentInterface {
|
||||
private nativeInput?: HTMLInputElement;
|
||||
private inputId = `ion-input-${inputIds++}`;
|
||||
private didBlurAfterEdit = false;
|
||||
private tabindex?: string | number;
|
||||
private inheritedAttributes: { [k: string]: any } = {};
|
||||
|
||||
/**
|
||||
* This is required for a WebKit bug which requires us to
|
||||
@@ -51,6 +51,7 @@ export class Input implements ComponentInterface {
|
||||
|
||||
/**
|
||||
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
|
||||
* Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
|
||||
*/
|
||||
@Prop() autocapitalize = 'off';
|
||||
|
||||
@@ -189,15 +190,6 @@ export class Input implements ComponentInterface {
|
||||
*/
|
||||
@Prop({ mutable: true }) value?: string | number | null = '';
|
||||
|
||||
/**
|
||||
* Update the native input element when the value changes
|
||||
*/
|
||||
@Watch('value')
|
||||
protected valueChanged() {
|
||||
this.emitStyle();
|
||||
this.ionChange.emit({ value: this.value == null ? this.value : this.value.toString() });
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted when a keyboard input occurred.
|
||||
*/
|
||||
@@ -224,15 +216,25 @@ export class Input implements ComponentInterface {
|
||||
*/
|
||||
@Event() ionStyle!: EventEmitter<StyleEventDetail>;
|
||||
|
||||
/**
|
||||
* Update the item classes when the placeholder changes
|
||||
*/
|
||||
@Watch('placeholder')
|
||||
protected placeholderChanged() {
|
||||
this.emitStyle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the native input element when the value changes
|
||||
*/
|
||||
@Watch('value')
|
||||
protected valueChanged() {
|
||||
this.emitStyle();
|
||||
this.ionChange.emit({ value: this.value == null ? this.value : this.value.toString() });
|
||||
}
|
||||
|
||||
componentWillLoad() {
|
||||
// If the ion-input has a tabindex attribute we get the value
|
||||
// and pass it down to the native input, then remove it from the
|
||||
// ion-input to avoid causing tabbing twice on the same element
|
||||
if (this.el.hasAttribute('tabindex')) {
|
||||
const tabindex = this.el.getAttribute('tabindex');
|
||||
this.tabindex = tabindex !== null ? tabindex : undefined;
|
||||
this.el.removeAttribute('tabindex');
|
||||
}
|
||||
this.inheritedAttributes = inheritAttributes(this.el, ['tabindex', 'title']);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
@@ -428,13 +430,13 @@ export class Input implements ComponentInterface {
|
||||
spellcheck={this.spellcheck}
|
||||
step={this.step}
|
||||
size={this.size}
|
||||
tabindex={this.tabindex}
|
||||
type={this.type}
|
||||
value={value}
|
||||
onInput={this.onInput}
|
||||
onBlur={this.onBlur}
|
||||
onFocus={this.onFocus}
|
||||
onKeyDown={this.onKeydown}
|
||||
{...this.inheritedAttributes}
|
||||
/>
|
||||
{(this.clearInput && !this.readonly && !this.disabled) && <button
|
||||
aria-label="reset"
|
||||
|
||||
@@ -301,7 +301,7 @@ export default defineComponent({
|
||||
| Property | Attribute | Description | Type | Default |
|
||||
| ---------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- |
|
||||
| `accept` | `accept` | If the value of the type attribute is `"file"`, then this attribute will indicate the types of files that the server accepts, otherwise it will be ignored. The value must be a comma-separated list of unique content type specifiers. | `string \| undefined` | `undefined` |
|
||||
| `autocapitalize` | `autocapitalize` | Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. | `string` | `'off'` |
|
||||
| `autocapitalize` | `autocapitalize` | Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`. | `string` | `'off'` |
|
||||
| `autocomplete` | `autocomplete` | Indicates whether the value of the control can be automatically completed by the browser. | `"on" \| "off" \| "name" \| "honorific-prefix" \| "given-name" \| "additional-name" \| "family-name" \| "honorific-suffix" \| "nickname" \| "email" \| "username" \| "new-password" \| "current-password" \| "one-time-code" \| "organization-title" \| "organization" \| "street-address" \| "address-line1" \| "address-line2" \| "address-line3" \| "address-level4" \| "address-level3" \| "address-level2" \| "address-level1" \| "country" \| "country-name" \| "postal-code" \| "cc-name" \| "cc-given-name" \| "cc-additional-name" \| "cc-family-name" \| "cc-number" \| "cc-exp" \| "cc-exp-month" \| "cc-exp-year" \| "cc-csc" \| "cc-type" \| "transaction-currency" \| "transaction-amount" \| "language" \| "bday" \| "bday-day" \| "bday-month" \| "bday-year" \| "sex" \| "tel" \| "tel-country-code" \| "tel-national" \| "tel-area-code" \| "tel-local" \| "tel-extension" \| "impp" \| "url" \| "photo"` | `'off'` |
|
||||
| `autocorrect` | `autocorrect` | Whether auto correction should be enabled when the user is entering/editing the text value. | `"off" \| "on"` | `'off'` |
|
||||
| `autofocus` | `autofocus` | This Boolean attribute lets you specify that a form control should have input focus when the page loads. | `boolean` | `false` |
|
||||
|
||||
@@ -132,11 +132,11 @@
|
||||
<ion-label>Right</ion-label>
|
||||
<ion-input class="ion-text-right" value="Narrow input"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
</ion-content>
|
||||
|
||||
<script>
|
||||
document.querySelector('ion-input').addEventListener('ionBlur', (ev) => { console.log(ev)})
|
||||
document.querySelector('ion-input').addEventListener('ionBlur', (ev) => { console.log(ev)});
|
||||
|
||||
function toggleBoolean(id, prop) {
|
||||
var el = document.getElementById(id);
|
||||
|
||||
|
||||
@@ -13,27 +13,130 @@
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Input - Spec</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<h1>Floating Inputs</h1>
|
||||
|
||||
<div class="grid">
|
||||
<div class="column">
|
||||
<h2>Inactive</h2>
|
||||
<ion-item>
|
||||
<ion-label position="floating">Label</ion-label>
|
||||
<ion-input placeholder="Placeholder Text"></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<h2>Focused</h2>
|
||||
<ion-item class="item-has-focus">
|
||||
<ion-label position="floating">Label</ion-label>
|
||||
<ion-input placeholder="Placeholder Text"></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<h2>Activated</h2>
|
||||
<ion-item>
|
||||
<ion-label position="floating">Label</ion-label>
|
||||
<ion-input placeholder="Placeholder Text" value="Input Text"></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<h2>Hover</h2>
|
||||
<ion-item class="item-hovered">
|
||||
<ion-label position="floating">Label</ion-label>
|
||||
<ion-input></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<h2>Disabled</h2>
|
||||
<ion-item>
|
||||
<ion-label position="floating">Label</ion-label>
|
||||
<ion-input disabled></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<h2>Toggle Placeholder</h2>
|
||||
<ion-item>
|
||||
<ion-label position="floating">Label</ion-label>
|
||||
<ion-input id="floatingToggle" type="password"></ion-input>
|
||||
<ion-button fill="clear" slot="end" onClick="togglePlaceholder('#floatingToggle')" class="ion-align-self-center">
|
||||
Toggle
|
||||
</ion-button>
|
||||
</ion-item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1>Stacked Inputs</h1>
|
||||
|
||||
<div class="grid">
|
||||
<div class="column">
|
||||
<h2>Inactive</h2>
|
||||
<ion-item>
|
||||
<ion-label position="stacked">Label</ion-label>
|
||||
<ion-input></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<h2>Focused</h2>
|
||||
<ion-item class="item-has-focus">
|
||||
<ion-label position="stacked">Label</ion-label>
|
||||
<ion-input placeholder="Placeholder Text"></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<h2>Activated</h2>
|
||||
<ion-item>
|
||||
<ion-label position="stacked">Label</ion-label>
|
||||
<ion-input placeholder="Placeholder Text" value="Input Text"></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<h2>Hover</h2>
|
||||
<ion-item class="item-hovered">
|
||||
<ion-label position="stacked">Label</ion-label>
|
||||
<ion-input></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<h2>Disabled</h2>
|
||||
<ion-item>
|
||||
<ion-label position="stacked">Label</ion-label>
|
||||
<ion-input disabled></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<h2>Toggle Placeholder</h2>
|
||||
<ion-item>
|
||||
<ion-label position="stacked">Label</ion-label>
|
||||
<ion-input id="stackedToggle" type="password"></ion-input>
|
||||
<ion-button fill="clear" slot="end" onClick="togglePlaceholder('#stackedToggle')" class="ion-align-self-center">
|
||||
Toggle
|
||||
</ion-button>
|
||||
</ion-item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>Stacked Div</h2>
|
||||
<ion-item>
|
||||
<ion-label position="floating">Floating: input</ion-label>
|
||||
<ion-input></ion-input>
|
||||
</ion-item>
|
||||
<ion-item class="item-has-focus">
|
||||
<ion-label position="floating">Floating: input focused value</ion-label>
|
||||
<ion-input value="value"></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label position="stacked">Stacked: input</ion-label>
|
||||
<ion-input></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label position="stacked">Stacked: input value</ion-label>
|
||||
<ion-input value="value"></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label position="stacked">Stacked: div</ion-label>
|
||||
<ion-label position="stacked">Label</ion-label>
|
||||
<div>A div</div>
|
||||
</ion-item>
|
||||
|
||||
<ion-item class="ion-align-items-center">
|
||||
<ion-icon slot="start" name="planet"></ion-icon>
|
||||
<ion-label position="stacked">Align items: center</ion-label>
|
||||
@@ -68,8 +171,45 @@
|
||||
</ion-content>
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
font-size: 14px;
|
||||
color: #54575e;
|
||||
|
||||
margin: 25px 0 5px 25px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
|
||||
color: #a1a7b0;
|
||||
|
||||
margin-top: 10px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
hr {
|
||||
background: #eff1f3;
|
||||
|
||||
margin-top: 18px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
row-gap: 20px;
|
||||
column-gap: 20px;
|
||||
padding: 0 20px 20px;
|
||||
}
|
||||
|
||||
ion-item {
|
||||
--background: #f5f5f5;
|
||||
--background: #e0e0e0;
|
||||
--background-hover: #d3d3d3;
|
||||
}
|
||||
|
||||
.custom {
|
||||
@@ -84,6 +224,13 @@
|
||||
color: purple !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function togglePlaceholder(id) {
|
||||
const input = document.querySelector(id);
|
||||
input.placeholder = input.placeholder ? undefined : 'Placeholder Text';
|
||||
}
|
||||
</script>
|
||||
</ion-app>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -209,7 +209,14 @@ export class ItemSliding implements ComponentInterface {
|
||||
this.leftOptions = this.rightOptions = undefined;
|
||||
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
const option = await options.item(i).componentOnReady();
|
||||
const item = options.item(i);
|
||||
|
||||
/**
|
||||
* We cannot use the componentOnReady helper
|
||||
* util here since we need to wait for all of these items
|
||||
* to be ready before we set `this.sides` and `this.optsDirty`.
|
||||
*/
|
||||
const option = ((item as any).componentOnReady !== undefined) ? await item.componentOnReady() : item;
|
||||
|
||||
const side = isEndSide(option.side) ? 'end' : 'start';
|
||||
|
||||
|
||||
@@ -47,5 +47,5 @@ const deleteItemSliding = async (item: any, page: any, id: string) => {
|
||||
// Wait for element to be removed from DOM
|
||||
await page.waitForSelector(id, { hidden: true });
|
||||
|
||||
await page.waitFor(1000);
|
||||
await page.waitForTimeout(1000);
|
||||
};
|
||||
|
||||
@@ -42,7 +42,7 @@ export const openItemSliding = async (id: string, page: any, rtl = false) => {
|
||||
await page.mouse.up();
|
||||
|
||||
// Add a timeout to make sure the item is open
|
||||
await page.waitFor(2000);
|
||||
await page.waitForTimeout(2000);
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
@@ -54,5 +54,5 @@ export const closeItemSliding = async (page: any) => {
|
||||
await page.mouse.move(0, 0);
|
||||
await page.mouse.down();
|
||||
await page.mouse.up();
|
||||
await page.waitFor(1000);
|
||||
await page.waitForTimeout(1000);
|
||||
};
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
--color: #{$item-md-color};
|
||||
--transition: opacity 15ms linear, background-color 15ms linear;
|
||||
--padding-start: #{$item-md-padding-start};
|
||||
--color: #{$item-md-color};
|
||||
--border-color: #{$item-md-border-bottom-color};
|
||||
--inner-padding-end: #{$item-md-padding-end};
|
||||
--inner-border-width: #{0 0 $item-md-border-bottom-width 0};
|
||||
|
||||
@@ -305,7 +305,7 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
|
||||
<slot></slot>
|
||||
</div>
|
||||
<slot name="end"></slot>
|
||||
{showDetail && <ion-icon icon={detailIcon} lazy={false} class="item-detail-icon" part="detail-icon"></ion-icon>}
|
||||
{showDetail && <ion-icon icon={detailIcon} lazy={false} class="item-detail-icon" part="detail-icon" aria-hidden="true"></ion-icon>}
|
||||
<div class="item-inner-highlight"></div>
|
||||
</div>
|
||||
{canActivate && mode === 'md' && <ion-ripple-effect></ion-ripple-effect>}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<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" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -11,7 +11,7 @@ test('item: inputs', async () => {
|
||||
page,
|
||||
'{"date":"","select":"n64","toggle":"","input":"","input2":"","checkbox":"","range":"10"}'
|
||||
);
|
||||
await page.waitFor(100);
|
||||
await page.waitForTimeout(100);
|
||||
|
||||
// Default case, enabled and no value
|
||||
let compare = await page.compareScreenshot();
|
||||
@@ -21,13 +21,13 @@ test('item: inputs', async () => {
|
||||
const disableToggle = await page.find('#btnDisabled');
|
||||
await disableToggle.waitForVisible();
|
||||
await disableToggle.click();
|
||||
await page.waitFor(300);
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
// check form
|
||||
await page.click('#submit');
|
||||
await page.waitFor(100);
|
||||
await page.waitForTimeout(100);
|
||||
await checkFormResult(page, '{}');
|
||||
await page.waitFor(100);
|
||||
await page.waitForTimeout(100);
|
||||
|
||||
// screenshot
|
||||
compare = await page.compareScreenshot('should disable all');
|
||||
@@ -36,7 +36,7 @@ test('item: inputs', async () => {
|
||||
// Reenable and set some value
|
||||
await disableToggle.click();
|
||||
await page.click('#btnSomeValue');
|
||||
await page.waitFor(100);
|
||||
await page.waitForTimeout(100);
|
||||
|
||||
// check form
|
||||
await page.click('#submit');
|
||||
@@ -44,28 +44,28 @@ test('item: inputs', async () => {
|
||||
page,
|
||||
'{"date":"2016-12-09","select":"nes","toggle":"on","input":"Some text","input2":"Some text","checkbox":"on","range":"20"}'
|
||||
);
|
||||
await page.waitFor(100);
|
||||
await page.waitForTimeout(100);
|
||||
|
||||
compare = await page.compareScreenshot('should reenable and set value');
|
||||
expect(compare).toMatchScreenshot();
|
||||
|
||||
// Set "null"
|
||||
await page.click('#btnNullValue');
|
||||
await page.waitFor(100);
|
||||
await page.waitForTimeout(100);
|
||||
|
||||
compare = await page.compareScreenshot('should set null');
|
||||
expect(compare).toMatchScreenshot();
|
||||
|
||||
// Set "empty"
|
||||
await page.click('#btnEmptyValue');
|
||||
await page.waitFor(100);
|
||||
await page.waitForTimeout(100);
|
||||
|
||||
compare = await page.compareScreenshot('should set empty');
|
||||
expect(compare).toMatchScreenshot();
|
||||
|
||||
// Set "empty"
|
||||
await page.click('#btnEmptyValue');
|
||||
await page.waitFor(100);
|
||||
await page.waitForTimeout(100);
|
||||
|
||||
compare = await page.compareScreenshot('should set empty');
|
||||
expect(compare).toMatchScreenshot();
|
||||
@@ -73,7 +73,7 @@ test('item: inputs', async () => {
|
||||
// Test multiple
|
||||
await page.click('#checkbox-start');
|
||||
await page.click('#datetime-end');
|
||||
await page.waitFor(300);
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
compare = await page.compareScreenshot(
|
||||
'should check checkbox and open datepicker'
|
||||
@@ -81,7 +81,7 @@ test('item: inputs', async () => {
|
||||
expect(compare).toMatchScreenshot();
|
||||
|
||||
await page.click('#button-end');
|
||||
await page.waitFor(100);
|
||||
await page.waitForTimeout(100);
|
||||
|
||||
compare = await page.compareScreenshot('should change button color to red');
|
||||
expect(compare).toMatchScreenshot();
|
||||
|
||||
@@ -23,24 +23,27 @@
|
||||
|
||||
:host(.label-floating) {
|
||||
@include margin(null, null, 0, null);
|
||||
@include transform(translate3d(0, 27px, 0));
|
||||
@include transform(translate3d(0, 29px, 0));
|
||||
@include transform-origin(start, top);
|
||||
|
||||
transition: transform 150ms ease-in-out;
|
||||
}
|
||||
|
||||
:host-context(.item-textarea).label-floating {
|
||||
@include transform(translate3d(0, 28px, 0));
|
||||
}
|
||||
|
||||
:host-context(.item-has-focus).label-stacked,
|
||||
:host-context(.item-has-focus).label-floating {
|
||||
color: $label-ios-text-color-focused;
|
||||
}
|
||||
|
||||
:host-context(.item-has-focus).label-floating,
|
||||
:host-context(.item-has-placeholder).label-floating,
|
||||
:host-context(.item-has-placeholder:not(.item-input)).label-floating,
|
||||
:host-context(.item-has-value).label-floating {
|
||||
@include transform(translate3d(0, 0, 0), scale(.82));
|
||||
}
|
||||
|
||||
|
||||
// iOS Typography
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
@@ -33,13 +33,17 @@
|
||||
transform 150ms $label-md-transition-timing-function;
|
||||
}
|
||||
|
||||
:host-context(.item-textarea).label-floating {
|
||||
@include transform(translateY(185%));
|
||||
}
|
||||
|
||||
:host(.label-stacked),
|
||||
:host(.label-floating) {
|
||||
@include margin(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
:host-context(.item-has-focus).label-floating,
|
||||
:host-context(.item-has-placeholder).label-floating,
|
||||
:host-context(.item-has-placeholder:not(.item-input)).label-floating,
|
||||
:host-context(.item-has-value).label-floating {
|
||||
@include transform(translateY(50%), scale(.75));
|
||||
}
|
||||
|
||||
121
core/src/components/label/test/floating/index.html
Normal file
121
core/src/components/label/test/floating/index.html
Normal file
@@ -0,0 +1,121 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Label - Floating</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">
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script></head>
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Label - Floating</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-list-header>
|
||||
<ion-label>Default</ion-label>
|
||||
</ion-list-header>
|
||||
|
||||
<ion-item>
|
||||
<ion-label position="floating">Input</ion-label>
|
||||
<ion-input></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label position="floating">Textarea</ion-label>
|
||||
<ion-textarea></ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label position="floating">Datetime</ion-label>
|
||||
<ion-datetime></ion-datetime>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label position="floating">Select</ion-label>
|
||||
<ion-select>
|
||||
<ion-select-option>Option</ion-select-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
<ion-list>
|
||||
<ion-list-header>
|
||||
<ion-label>Placeholders</ion-label>
|
||||
</ion-list-header>
|
||||
|
||||
<ion-item>
|
||||
<ion-label position="floating">Input</ion-label>
|
||||
<ion-input placeholder="Placeholder Text"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label position="floating">Textarea</ion-label>
|
||||
<ion-textarea placeholder="Placeholder Text"></ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label position="floating">Datetime</ion-label>
|
||||
<ion-datetime placeholder="Placeholder Text"></ion-datetime>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label position="floating">Select</ion-label>
|
||||
<ion-select placeholder="Placeholder Text">
|
||||
<ion-select-option>Option</ion-select-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
<ion-list>
|
||||
<ion-list-header>
|
||||
<ion-label>Values</ion-label>
|
||||
</ion-list-header>
|
||||
|
||||
<ion-item>
|
||||
<ion-label position="floating">Input</ion-label>
|
||||
<ion-input value="Input Value"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label position="floating">Textarea</ion-label>
|
||||
<ion-textarea value="Textarea Value"></ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label position="floating">Datetime</ion-label>
|
||||
<ion-datetime value="2020-03-04"></ion-datetime>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label position="floating">Select</ion-label>
|
||||
<ion-select value="option">
|
||||
<ion-select-option value="option">Option</ion-select-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
|
||||
<style>
|
||||
ion-content {
|
||||
--background: #f6f6f6;
|
||||
}
|
||||
|
||||
ion-list {
|
||||
background: transparent !important;
|
||||
}
|
||||
</style>
|
||||
</ion-app>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -131,6 +131,43 @@ async function presentLoadingWithOptions() {
|
||||
### React
|
||||
|
||||
```tsx
|
||||
/* Using with useIonLoading Hook */
|
||||
|
||||
import React from 'react';
|
||||
import { IonButton, IonContent, IonPage, useIonLoading } from '@ionic/react';
|
||||
|
||||
interface LoadingProps {}
|
||||
|
||||
const LoadingExample: React.FC<LoadingProps> = () => {
|
||||
const [present] = useIonLoading();
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present({
|
||||
duration: 3000,
|
||||
})
|
||||
}
|
||||
>
|
||||
Show Loading
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => present('Loading', 2000, 'dots')}
|
||||
>
|
||||
Show Loading using params
|
||||
</IonButton>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
```tsx
|
||||
/* Using with IonLoading Component */
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { IonLoading, IonButton, IonContent } from '@ionic/react';
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script></head>
|
||||
<script type="module">
|
||||
import { loadingController } from '../../../../dist/ionic/index.esm.js';
|
||||
import { loadingController } from '../../../../../dist/ionic/index.esm.js';
|
||||
window.loadingController = loadingController;
|
||||
</script>
|
||||
<body>
|
||||
|
||||
@@ -1,4 +1,41 @@
|
||||
```tsx
|
||||
/* Using with useIonLoading Hook */
|
||||
|
||||
import React from 'react';
|
||||
import { IonButton, IonContent, IonPage, useIonLoading } from '@ionic/react';
|
||||
|
||||
interface LoadingProps {}
|
||||
|
||||
const LoadingExample: React.FC<LoadingProps> = () => {
|
||||
const [present] = useIonLoading();
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present({
|
||||
duration: 3000,
|
||||
})
|
||||
}
|
||||
>
|
||||
Show Loading
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => present('Loading', 2000, 'dots')}
|
||||
>
|
||||
Show Loading using params
|
||||
</IonButton>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
```tsx
|
||||
/* Using with IonLoading Component */
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { IonLoading, IonButton, IonContent } from '@ionic/react';
|
||||
|
||||
|
||||
@@ -46,7 +46,11 @@ export class Menu implements ComponentInterface, MenuI {
|
||||
@State() isEndSide = false;
|
||||
|
||||
/**
|
||||
* The content's id the menu should use.
|
||||
* The `id` of the main content. When using
|
||||
* a router this is typically `ion-router-outlet`.
|
||||
* When not using a router, this is typically
|
||||
* your main view's `ion-content`. This is not the
|
||||
* id of the `ion-content` inside of your `ion-menu`.
|
||||
*/
|
||||
@Prop({ reflect: true }) contentId?: string;
|
||||
|
||||
|
||||
@@ -423,7 +423,7 @@ export class MenuExample {
|
||||
</ion-content>
|
||||
</ion-menu>
|
||||
|
||||
<ion-router-outlet main></ion-router-outlet>
|
||||
<ion-router-outlet id="main"></ion-router-outlet>
|
||||
</template>
|
||||
<style>
|
||||
.my-custom-menu {
|
||||
@@ -477,15 +477,15 @@ export default defineComponent({
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Attribute | Description | Type | Default |
|
||||
| -------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------ | --------------------- | ----------- |
|
||||
| `contentId` | `content-id` | The content's id the menu should use. | `string \| undefined` | `undefined` |
|
||||
| `disabled` | `disabled` | If `true`, the menu is disabled. | `boolean` | `false` |
|
||||
| `maxEdgeStart` | `max-edge-start` | The edge threshold for dragging the menu open. If a drag/swipe happens over this value, the menu is not triggered. | `number` | `50` |
|
||||
| `menuId` | `menu-id` | An id for the menu. | `string \| undefined` | `undefined` |
|
||||
| `side` | `side` | Which side of the view the menu should be placed. | `"end" \| "start"` | `'start'` |
|
||||
| `swipeGesture` | `swipe-gesture` | If `true`, swiping the menu is enabled. | `boolean` | `true` |
|
||||
| `type` | `type` | The display type of the menu. Available options: `"overlay"`, `"reveal"`, `"push"`. | `string \| undefined` | `undefined` |
|
||||
| Property | Attribute | Description | Type | Default |
|
||||
| -------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | ----------- |
|
||||
| `contentId` | `content-id` | The `id` of the main content. When using a router this is typically `ion-router-outlet`. When not using a router, this is typically your main view's `ion-content`. This is not the id of the `ion-content` inside of your `ion-menu`. | `string \| undefined` | `undefined` |
|
||||
| `disabled` | `disabled` | If `true`, the menu is disabled. | `boolean` | `false` |
|
||||
| `maxEdgeStart` | `max-edge-start` | The edge threshold for dragging the menu open. If a drag/swipe happens over this value, the menu is not triggered. | `number` | `50` |
|
||||
| `menuId` | `menu-id` | An id for the menu. | `string \| undefined` | `undefined` |
|
||||
| `side` | `side` | Which side of the view the menu should be placed. | `"end" \| "start"` | `'start'` |
|
||||
| `swipeGesture` | `swipe-gesture` | If `true`, swiping the menu is enabled. | `boolean` | `true` |
|
||||
| `type` | `type` | The display type of the menu. Available options: `"overlay"`, `"reveal"`, `"push"`. | `string \| undefined` | `undefined` |
|
||||
|
||||
|
||||
## Events
|
||||
|
||||
@@ -25,12 +25,12 @@ export const testMenu = async (
|
||||
const menu = await page.find(selector);
|
||||
|
||||
await menu.callMethod('open');
|
||||
await page.waitFor(1000);
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
screenshotCompares.push(await page.compareScreenshot());
|
||||
|
||||
await menu.callMethod('close');
|
||||
await page.waitFor(250);
|
||||
await page.waitForTimeout(250);
|
||||
|
||||
screenshotCompares.push(await page.compareScreenshot('dismiss'));
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
</ion-content>
|
||||
</ion-menu>
|
||||
|
||||
<ion-router-outlet main></ion-router-outlet>
|
||||
<ion-router-outlet id="main"></ion-router-outlet>
|
||||
</template>
|
||||
<style>
|
||||
.my-custom-menu {
|
||||
|
||||
@@ -79,6 +79,8 @@ export class ModalPage {
|
||||
}
|
||||
```
|
||||
|
||||
> If you need a wrapper element inside of your modal component, we recommend using a `<div class="ion-page">` so that the component dimensions are still computed properly.
|
||||
|
||||
### Passing Data
|
||||
|
||||
During creation of a modal, data can be passed in through the `componentProps`.
|
||||
@@ -251,6 +253,8 @@ function presentModal() {
|
||||
}
|
||||
```
|
||||
|
||||
> If you need a wrapper element inside of your modal component, we recommend using a `<div class="ion-page">` so that the component dimensions are still computed properly.
|
||||
|
||||
### Passing Data
|
||||
|
||||
During creation of a modal, data can be passed in through the `componentProps`. The previous example can be written to include data:
|
||||
@@ -328,6 +332,70 @@ modalElement.presentingElement = await modalController.getTop(); // Get the top-
|
||||
### React
|
||||
|
||||
```tsx
|
||||
/* Using with useIonModal Hook */
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { IonButton, IonContent, IonPage, useIonModal } from '@ionic/react';
|
||||
|
||||
const Body: React.FC<{
|
||||
count: number;
|
||||
onDismiss: () => void;
|
||||
onIncrement: () => void;
|
||||
}> = ({ count, onDismiss, onIncrement }) => (
|
||||
<div>
|
||||
count: {count}
|
||||
<IonButton expand="block" onClick={() => onIncrement()}>
|
||||
Increment Count
|
||||
</IonButton>
|
||||
<IonButton expand="block" onClick={() => onDismiss()}>
|
||||
Close
|
||||
</IonButton>
|
||||
</div>
|
||||
);
|
||||
|
||||
const ModalExample: React.FC = () => {
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
const handleIncrement = () => {
|
||||
setCount(count + 1);
|
||||
};
|
||||
|
||||
const handleDismiss = () => {
|
||||
dismiss();
|
||||
};
|
||||
|
||||
/**
|
||||
* First parameter is the component to show, second is the props to pass
|
||||
*/
|
||||
const [present, dismiss] = useIonModal(Body, {
|
||||
count,
|
||||
onDismiss: handleDismiss,
|
||||
onIncrement: handleIncrement,
|
||||
});
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent fullscreen>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
present({
|
||||
cssClass: 'my-class',
|
||||
});
|
||||
}}
|
||||
>
|
||||
Show Modal
|
||||
</IonButton>
|
||||
<div>Count: {count}</div>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
```tsx
|
||||
/* Using with IonModal Component */
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { IonModal, IonButton, IonContent } from '@ionic/react';
|
||||
|
||||
@@ -467,6 +535,8 @@ export class PageModal {
|
||||
}
|
||||
```
|
||||
|
||||
> If you need a wrapper element inside of your modal component, we recommend using a `<div class="ion-page">` so that the component dimensions are still computed properly.
|
||||
|
||||
### Passing Data
|
||||
|
||||
During creation of a modal, data can be passed in through the `componentProps`.
|
||||
@@ -578,16 +648,14 @@ async presentModal() {
|
||||
|
||||
```html
|
||||
<template>
|
||||
<div>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>{{ title }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding">
|
||||
{{ content }}
|
||||
</ion-content>
|
||||
</div>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>{{ title }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding">
|
||||
{{ content }}
|
||||
</ion-content>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -672,6 +740,8 @@ export default defineComponent({
|
||||
</script>
|
||||
```
|
||||
|
||||
> If you need a wrapper element inside of your modal component, we recommend using an `<ion-page>` so that the component dimensions are still computed properly.
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
@@ -28,7 +28,7 @@ export const testModal = async (
|
||||
|
||||
let modal = await page.find('ion-modal');
|
||||
await modal.waitForVisible();
|
||||
await page.waitFor(100);
|
||||
await page.waitForTimeout(100);
|
||||
|
||||
screenshotCompares.push(await page.compareScreenshot());
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@ export class ModalPage {
|
||||
}
|
||||
```
|
||||
|
||||
> If you need a wrapper element inside of your modal component, we recommend using a `<div class="ion-page">` so that the component dimensions are still computed properly.
|
||||
|
||||
### Passing Data
|
||||
|
||||
During creation of a modal, data can be passed in through the `componentProps`.
|
||||
|
||||
@@ -31,6 +31,8 @@ function presentModal() {
|
||||
}
|
||||
```
|
||||
|
||||
> If you need a wrapper element inside of your modal component, we recommend using a `<div class="ion-page">` so that the component dimensions are still computed properly.
|
||||
|
||||
### Passing Data
|
||||
|
||||
During creation of a modal, data can be passed in through the `componentProps`. The previous example can be written to include data:
|
||||
|
||||
@@ -1,4 +1,68 @@
|
||||
```tsx
|
||||
/* Using with useIonModal Hook */
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { IonButton, IonContent, IonPage, useIonModal } from '@ionic/react';
|
||||
|
||||
const Body: React.FC<{
|
||||
count: number;
|
||||
onDismiss: () => void;
|
||||
onIncrement: () => void;
|
||||
}> = ({ count, onDismiss, onIncrement }) => (
|
||||
<div>
|
||||
count: {count}
|
||||
<IonButton expand="block" onClick={() => onIncrement()}>
|
||||
Increment Count
|
||||
</IonButton>
|
||||
<IonButton expand="block" onClick={() => onDismiss()}>
|
||||
Close
|
||||
</IonButton>
|
||||
</div>
|
||||
);
|
||||
|
||||
const ModalExample: React.FC = () => {
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
const handleIncrement = () => {
|
||||
setCount(count + 1);
|
||||
};
|
||||
|
||||
const handleDismiss = () => {
|
||||
dismiss();
|
||||
};
|
||||
|
||||
/**
|
||||
* First parameter is the component to show, second is the props to pass
|
||||
*/
|
||||
const [present, dismiss] = useIonModal(Body, {
|
||||
count,
|
||||
onDismiss: handleDismiss,
|
||||
onIncrement: handleIncrement,
|
||||
});
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent fullscreen>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
present({
|
||||
cssClass: 'my-class',
|
||||
});
|
||||
}}
|
||||
>
|
||||
Show Modal
|
||||
</IonButton>
|
||||
<div>Count: {count}</div>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
```tsx
|
||||
/* Using with IonModal Component */
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { IonModal, IonButton, IonContent } from '@ionic/react';
|
||||
|
||||
|
||||
@@ -44,6 +44,8 @@ export class PageModal {
|
||||
}
|
||||
```
|
||||
|
||||
> If you need a wrapper element inside of your modal component, we recommend using a `<div class="ion-page">` so that the component dimensions are still computed properly.
|
||||
|
||||
### Passing Data
|
||||
|
||||
During creation of a modal, data can be passed in through the `componentProps`.
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
```html
|
||||
<template>
|
||||
<div>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>{{ title }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding">
|
||||
{{ content }}
|
||||
</ion-content>
|
||||
</div>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>{{ title }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding">
|
||||
{{ content }}
|
||||
</ion-content>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -93,3 +91,5 @@ export default defineComponent({
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
> If you need a wrapper element inside of your modal component, we recommend using an `<ion-page>` so that the component dimensions are still computed properly.
|
||||
|
||||
@@ -11,13 +11,13 @@ test.skip('nav: basic', async () => {
|
||||
expect(await page.compareScreenshot()).toMatchScreenshot();
|
||||
|
||||
page.click('page-one ion-button.next');
|
||||
await page.waitFor(navChanged);
|
||||
await page.waitForTimeout(navChanged);
|
||||
page.click('page-two ion-button.next');
|
||||
await page.waitFor(navChanged);
|
||||
await page.waitForTimeout(navChanged);
|
||||
page.click('page-three ion-back-button');
|
||||
await page.waitFor(navChanged);
|
||||
await page.waitForTimeout(navChanged);
|
||||
page.click('page-two ion-back-button');
|
||||
await page.waitFor(navChanged);
|
||||
await page.waitForTimeout(navChanged);
|
||||
|
||||
expect(await page.compareScreenshot('stack traversal')).toMatchScreenshot();
|
||||
});
|
||||
|
||||
@@ -12,17 +12,17 @@ test.skip('nav: nested', async () => {
|
||||
expect(await page.compareScreenshot()).toMatchScreenshot();
|
||||
|
||||
await page.click('page-one ion-button.next');
|
||||
await page.waitFor(navChanged);
|
||||
await page.waitForTimeout(navChanged);
|
||||
await page.click('page-two-one ion-button.next');
|
||||
await page.waitFor(navChanged);
|
||||
await page.waitForTimeout(navChanged);
|
||||
await page.click('page-two-two ion-button.next');
|
||||
await page.waitFor(navChanged);
|
||||
await page.waitForTimeout(navChanged);
|
||||
await page.click('page-three ion-back-button');
|
||||
await page.waitFor(navChanged);
|
||||
await page.waitForTimeout(navChanged);
|
||||
await page.click('page-two-two ion-back-button');
|
||||
await page.waitFor(navChanged);
|
||||
await page.waitForTimeout(navChanged);
|
||||
await page.click('page-two-one ion-back-button');
|
||||
await page.waitFor(navChanged);
|
||||
await page.waitForTimeout(navChanged);
|
||||
|
||||
expect(await page.compareScreenshot('stack traversal')).toMatchScreenshot();
|
||||
});
|
||||
|
||||
@@ -11,17 +11,17 @@ test.skip('nav: routing', async () => {
|
||||
expect(await page.compareScreenshot()).toMatchScreenshot();
|
||||
|
||||
await page.click('page-root ion-button.next');
|
||||
await page.waitFor(navChanged);
|
||||
await page.waitForTimeout(navChanged);
|
||||
await page.click('page-one ion-button.next');
|
||||
await page.waitFor(navChanged);
|
||||
await page.waitForTimeout(navChanged);
|
||||
await page.click('page-two ion-button.next');
|
||||
await page.waitFor(navChanged);
|
||||
await page.waitForTimeout(navChanged);
|
||||
await page.click('page-three ion-back-button');
|
||||
await page.waitFor(navChanged);
|
||||
await page.waitForTimeout(navChanged);
|
||||
await page.click('page-two ion-back-button');
|
||||
await page.waitFor(navChanged);
|
||||
await page.waitForTimeout(navChanged);
|
||||
await page.click('page-one ion-back-button');
|
||||
await page.waitFor(navChanged);
|
||||
await page.waitForTimeout(navChanged);
|
||||
|
||||
expect(await page.compareScreenshot('stack traversal')).toMatchScreenshot();
|
||||
});
|
||||
|
||||
@@ -18,7 +18,7 @@ export const testPickerColumn = async (
|
||||
|
||||
const openButton = await page.find(selector);
|
||||
await openButton.click();
|
||||
await page.waitFor(250);
|
||||
await page.waitForTimeout(250);
|
||||
|
||||
screenshotCompares.push(await page.compareScreenshot());
|
||||
|
||||
|
||||
@@ -7,6 +7,95 @@ A Picker is a dialog that displays a row of buttons and columns underneath. It a
|
||||
<!-- Auto Generated Below -->
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
### React
|
||||
|
||||
```tsx
|
||||
/* Using with useIonPicker Hook */
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { IonButton, IonContent, IonPage, useIonPicker } from '@ionic/react';
|
||||
|
||||
const PickerExample: React.FC = () => {
|
||||
const [present] = useIonPicker();
|
||||
const [value, setValue] = useState('');
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present({
|
||||
buttons: [
|
||||
{
|
||||
text: 'Confirm',
|
||||
handler: (selected) => {
|
||||
setValue(selected.animal.value)
|
||||
},
|
||||
},
|
||||
],
|
||||
columns: [
|
||||
{
|
||||
name: 'animal',
|
||||
options: [
|
||||
{ text: 'Dog', value: 'dog' },
|
||||
{ text: 'Cat', value: 'cat' },
|
||||
{ text: 'Bird', value: 'bird' },
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
>
|
||||
Show Picker
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present(
|
||||
[
|
||||
{
|
||||
name: 'animal',
|
||||
options: [
|
||||
{ text: 'Dog', value: 'dog' },
|
||||
{ text: 'Cat', value: 'cat' },
|
||||
{ text: 'Bird', value: 'bird' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'vehicle',
|
||||
options: [
|
||||
{ text: 'Car', value: 'car' },
|
||||
{ text: 'Truck', value: 'truck' },
|
||||
{ text: 'Bike', value: 'bike' },
|
||||
],
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
text: 'Confirm',
|
||||
handler: (selected) => {
|
||||
setValue(`${selected.animal.value}, ${selected.vehicle.value}`)
|
||||
},
|
||||
},
|
||||
]
|
||||
)
|
||||
}
|
||||
>
|
||||
Show Picker using params
|
||||
</IonButton>
|
||||
{value && (
|
||||
<div>Selected Value: {value}</div>
|
||||
)}
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Attribute | Description | Type | Default |
|
||||
|
||||
82
core/src/components/picker/usage/react.md
Normal file
82
core/src/components/picker/usage/react.md
Normal file
@@ -0,0 +1,82 @@
|
||||
```tsx
|
||||
/* Using with useIonPicker Hook */
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { IonButton, IonContent, IonPage, useIonPicker } from '@ionic/react';
|
||||
|
||||
const PickerExample: React.FC = () => {
|
||||
const [present] = useIonPicker();
|
||||
const [value, setValue] = useState('');
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present({
|
||||
buttons: [
|
||||
{
|
||||
text: 'Confirm',
|
||||
handler: (selected) => {
|
||||
setValue(selected.animal.value)
|
||||
},
|
||||
},
|
||||
],
|
||||
columns: [
|
||||
{
|
||||
name: 'animal',
|
||||
options: [
|
||||
{ text: 'Dog', value: 'dog' },
|
||||
{ text: 'Cat', value: 'cat' },
|
||||
{ text: 'Bird', value: 'bird' },
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
>
|
||||
Show Picker
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present(
|
||||
[
|
||||
{
|
||||
name: 'animal',
|
||||
options: [
|
||||
{ text: 'Dog', value: 'dog' },
|
||||
{ text: 'Cat', value: 'cat' },
|
||||
{ text: 'Bird', value: 'bird' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'vehicle',
|
||||
options: [
|
||||
{ text: 'Car', value: 'car' },
|
||||
{ text: 'Truck', value: 'truck' },
|
||||
{ text: 'Bike', value: 'bike' },
|
||||
],
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
text: 'Confirm',
|
||||
handler: (selected) => {
|
||||
setValue(`${selected.animal.value}, ${selected.vehicle.value}`)
|
||||
},
|
||||
},
|
||||
]
|
||||
)
|
||||
}
|
||||
>
|
||||
Show Picker using params
|
||||
</IonButton>
|
||||
{value && (
|
||||
<div>Selected Value: {value}</div>
|
||||
)}
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
```
|
||||
@@ -114,6 +114,59 @@ function presentPopover(ev) {
|
||||
### React
|
||||
|
||||
```tsx
|
||||
/* Using with useIonPopover Hook */
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
IonButton,
|
||||
IonContent,
|
||||
IonItem,
|
||||
IonList,
|
||||
IonListHeader,
|
||||
IonPage,
|
||||
useIonPopover,
|
||||
} from '@ionic/react';
|
||||
|
||||
const PopoverList: React.FC<{
|
||||
onHide: () => void;
|
||||
}> = ({ onHide }) => (
|
||||
<IonList>
|
||||
<IonListHeader>Ionic</IonListHeader>
|
||||
<IonItem button>Learn Ionic</IonItem>
|
||||
<IonItem button>Documentation</IonItem>
|
||||
<IonItem button>Showcase</IonItem>
|
||||
<IonItem button>GitHub Repo</IonItem>
|
||||
<IonItem lines="none" detail={false} button onClick={onHide}>
|
||||
Close
|
||||
</IonItem>
|
||||
</IonList>
|
||||
);
|
||||
|
||||
const PopoverExample: React.FC = () => {
|
||||
const [present, dismiss] = useIonPopover(PopoverList, { onHide: () => dismiss() });
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={(e) =>
|
||||
present({
|
||||
event: e.nativeEvent,
|
||||
})
|
||||
}
|
||||
>
|
||||
Show Popover
|
||||
</IonButton>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
```tsx
|
||||
/* Using with IonPopover Component */
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { IonPopover, IonButton } from '@ionic/react';
|
||||
|
||||
|
||||
@@ -1,4 +1,57 @@
|
||||
```tsx
|
||||
/* Using with useIonPopover Hook */
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
IonButton,
|
||||
IonContent,
|
||||
IonItem,
|
||||
IonList,
|
||||
IonListHeader,
|
||||
IonPage,
|
||||
useIonPopover,
|
||||
} from '@ionic/react';
|
||||
|
||||
const PopoverList: React.FC<{
|
||||
onHide: () => void;
|
||||
}> = ({ onHide }) => (
|
||||
<IonList>
|
||||
<IonListHeader>Ionic</IonListHeader>
|
||||
<IonItem button>Learn Ionic</IonItem>
|
||||
<IonItem button>Documentation</IonItem>
|
||||
<IonItem button>Showcase</IonItem>
|
||||
<IonItem button>GitHub Repo</IonItem>
|
||||
<IonItem lines="none" detail={false} button onClick={onHide}>
|
||||
Close
|
||||
</IonItem>
|
||||
</IonList>
|
||||
);
|
||||
|
||||
const PopoverExample: React.FC = () => {
|
||||
const [present, dismiss] = useIonPopover(PopoverList, { onHide: () => dismiss() });
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={(e) =>
|
||||
present({
|
||||
event: e.nativeEvent,
|
||||
})
|
||||
}
|
||||
>
|
||||
Show Popover
|
||||
</IonButton>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
```tsx
|
||||
/* Using with IonPopover Component */
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { IonPopover, IonButton } from '@ionic/react';
|
||||
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
@import "../../themes/ionic.globals";
|
||||
|
||||
// Progress bar
|
||||
// --------------------------------------------------
|
||||
// Host has no background by default - this will be added to the progress-buffer-bar
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// Host has no background by default - this will be added to the progress-buffer-bar
|
||||
:host {
|
||||
/**
|
||||
* @prop --background: Same as --buffer-background when using a determinate progress bar, otherwise it styles the background of the ion-progress-bar itself.
|
||||
* @prop --progress-background: Color of the progress bar
|
||||
* @prop --buffer-background: Color of the buffer bar
|
||||
*/
|
||||
--background: #{ion-color(primary, base, 0.2)};
|
||||
* @prop --background: Background of the progress track, or the buffer bar if `buffer` is set
|
||||
* @prop --progress-background: Background of the progress bar representing the current value
|
||||
* @prop --buffer-background: DEPRECATED, use `--background` instead
|
||||
*/
|
||||
--background: #{ion-color(primary, base, 0.3)};
|
||||
--progress-background: #{ion-color(primary, base)};
|
||||
--buffer-background: var(--background);
|
||||
|
||||
display: block;
|
||||
|
||||
position: relative;
|
||||
@@ -25,36 +26,38 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:host(.ion-color) {
|
||||
--progress-background: #{current-color(base)};
|
||||
--buffer-background: #{current-color(base, 0.2)};
|
||||
}
|
||||
|
||||
// indeterminate has no progress-buffer-bar, so it will be added to the host
|
||||
:host(.progress-bar-indeterminate) {
|
||||
background: var(--buffer-background);
|
||||
}
|
||||
|
||||
.progress,
|
||||
.progress-indeterminate,
|
||||
.indeterminate-bar-primary,
|
||||
.indeterminate-bar-secondary,
|
||||
.progress-buffer-bar,
|
||||
.progress-buffer-bar:before,
|
||||
.buffer-circles {
|
||||
.progress-buffer-bar {
|
||||
@include position(0, 0, 0, 0);
|
||||
|
||||
position: absolute;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.buffer-circles-container,
|
||||
.buffer-circles {
|
||||
@include position(0, 0, 0, 0);
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
// Extend a bit to overflow. The size of animated distance.
|
||||
.buffer-circles {
|
||||
/* stylelint-disable property-blacklist */
|
||||
right: -10px;
|
||||
left: -10px;
|
||||
/* stylelint-enable property-blacklist */
|
||||
}
|
||||
|
||||
// Determinate progress bar
|
||||
// --------------------------------------------------
|
||||
|
||||
.progress,
|
||||
.progress-buffer-bar {
|
||||
.progress-buffer-bar,
|
||||
.buffer-circles-container {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
transform-origin: left top;
|
||||
|
||||
@@ -72,17 +75,13 @@
|
||||
}
|
||||
|
||||
.progress-buffer-bar {
|
||||
// It's currently here because --buffer-background has an alpha
|
||||
// Otherwise the buffer circles would be seen through
|
||||
background: #fff;
|
||||
background: var(--buffer-background);
|
||||
|
||||
z-index: 1; // Make it behind the progress
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&:before {
|
||||
background: var(--buffer-background);
|
||||
|
||||
content: "";
|
||||
}
|
||||
.buffer-circles-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// MD based animation on indeterminate type
|
||||
@@ -109,7 +108,7 @@
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: -54.888891%;
|
||||
left: -54.888891%;
|
||||
/* stylelint-enable property-blacklist */
|
||||
|
||||
animation: secondary-indeterminate-translate 2s infinite linear;
|
||||
@@ -120,38 +119,33 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Buffer style
|
||||
// --------------------------------------------------
|
||||
// Progress Bar: Buffer Circles
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
.buffer-circles {
|
||||
background: radial-gradient(ellipse at center, var(--buffer-background) 0%, var(--buffer-background) 30%, transparent 30%) repeat-x 5px center;
|
||||
background-image: radial-gradient(ellipse at center, var(--buffer-background) 0%, var(--buffer-background) 30%, transparent 30%);
|
||||
|
||||
/* stylelint-disable property-blacklist */
|
||||
background-repeat: repeat-x;
|
||||
background-position: 5px center;
|
||||
background-size: 10px 10px;
|
||||
/* stylelint-enable property-blacklist */
|
||||
|
||||
z-index: 0;
|
||||
animation: buffering 450ms infinite linear;
|
||||
}
|
||||
|
||||
// Progress Bar: Reversed
|
||||
// ------------------------------------------------------------------------
|
||||
// If reversed is set to true, the animation will be reversed
|
||||
// and the bars starting at the top right
|
||||
// --------------------------------------------------
|
||||
// and the bar will start at the top right
|
||||
|
||||
:host(.progress-bar-reversed) {
|
||||
.progress,
|
||||
.progress-buffer-bar {
|
||||
/* stylelint-disable-next-line property-blacklist */
|
||||
transform-origin: right top;
|
||||
}
|
||||
|
||||
.buffer-circles,
|
||||
.indeterminate-bar-primary,
|
||||
.indeterminate-bar-secondary,
|
||||
.progress-indeterminate {
|
||||
animation-direction: reverse;
|
||||
}
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
// Progress paused
|
||||
// --------------------------------------------------
|
||||
// Progress Bar: Paused
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
:host(.progress-paused) {
|
||||
.indeterminate-bar-secondary,
|
||||
@@ -161,8 +155,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Animation Keyframes
|
||||
// --------------------------------------------------
|
||||
// Progress Bar: Color
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
:host(.ion-color) .progress-buffer-bar {
|
||||
background: #{current-color(base, 0.3)};
|
||||
}
|
||||
|
||||
:host(.ion-color) .buffer-circles {
|
||||
background-image: radial-gradient(ellipse at center, #{current-color(base, 0.3)} 0%, #{current-color(base, 0.3)} 30%, transparent 30%);
|
||||
}
|
||||
|
||||
:host(.ion-color) {
|
||||
.progress,
|
||||
.progress-indeterminate {
|
||||
background: #{current-color(base)};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Progress Bar: Animation Keyframes
|
||||
// ------------------------------------------------------------------------
|
||||
// Source: https://github.com/material-components/material-components-web/blob/master/packages/mdc-linear-progress/_keyframes.scss
|
||||
|
||||
@keyframes primary-indeterminate-translate {
|
||||
@@ -262,3 +275,4 @@
|
||||
transform: translateX(-10px);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,11 @@ import { createColorClasses } from '../../utils/theme';
|
||||
|
||||
/**
|
||||
* @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use.
|
||||
*
|
||||
* @part progress - The progress bar that shows the current value when `type` is `"determinate"` and slides back and forth when `type` is `"indeterminate"`.
|
||||
* @part stream - The animated circles that appear while buffering. This only shows when `buffer` is set and `type` is `"determinate"`.
|
||||
* @part track - The track bar behind the progress bar. If the `buffer` property is set and `type` is `"determinate"` the track will be the
|
||||
* width of the `buffer` value.
|
||||
*/
|
||||
@Component({
|
||||
tag: 'ion-progress-bar',
|
||||
@@ -77,10 +82,12 @@ export class ProgressBar implements ComponentInterface {
|
||||
}
|
||||
|
||||
const renderIndeterminate = () => {
|
||||
return [
|
||||
<div class="indeterminate-bar-primary"><span class="progress-indeterminate"></span></div>,
|
||||
<div class="indeterminate-bar-secondary"><span class="progress-indeterminate"></span></div>
|
||||
];
|
||||
return (
|
||||
<div part="track" class="progress-buffer-bar">
|
||||
<div class="indeterminate-bar-primary"><span part="progress" class="progress-indeterminate"></span></div>
|
||||
<div class="indeterminate-bar-secondary"><span part="progress" class="progress-indeterminate"></span></div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const renderProgress = (value: number, buffer: number) => {
|
||||
@@ -88,8 +95,19 @@ const renderProgress = (value: number, buffer: number) => {
|
||||
const finalBuffer = clamp(0, buffer, 1);
|
||||
|
||||
return [
|
||||
<div class="progress" style={{ transform: `scaleX(${finalValue})` }}></div>,
|
||||
finalBuffer !== 1 && <div class="buffer-circles"></div>,
|
||||
<div class="progress-buffer-bar" style={{ transform: `scaleX(${finalBuffer})` }}></div>,
|
||||
<div part="progress" class="progress" style={{ transform: `scaleX(${finalValue})` }}></div>,
|
||||
/**
|
||||
* Buffer circles with two container to move
|
||||
* the circles behind the buffer progress
|
||||
* with respecting the animation.
|
||||
* When finalBuffer === 1, we use display: none
|
||||
* instead of removing the element to avoid flickering.
|
||||
*/
|
||||
<div class={{ 'buffer-circles-container': true, 'ion-hide': finalBuffer === 1 }} style={{ transform: `translateX(${finalBuffer * 100}%)` }}>
|
||||
<div class="buffer-circles-container" style={{ transform: `translateX(-${finalBuffer * 100}%)` }}>
|
||||
<div part="stream" class="buffer-circles"></div>
|
||||
</div>
|
||||
</div>,
|
||||
<div part="track" class="progress-buffer-bar" style={{ transform: `scaleX(${finalBuffer})` }}></div>,
|
||||
];
|
||||
};
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
# ion-progress-bar
|
||||
|
||||
ion-progress-bar is a horizontal progress bar to visualize the progression of an operation and activity. You can choose between two types: `determinate` and `indeterminate`.
|
||||
Progress bars inform users about the status of ongoing processes, such as loading an app, submitting a form, or saving updates. There are two types of progress bars: `determinate` and `indeterminate`.
|
||||
|
||||
## Progress Type
|
||||
|
||||
### Determinate
|
||||
|
||||
If the percentage of an operation is known, you should use the determinate type. This is the default type and the progress is represented by the `value` property.
|
||||
Determinate is the default type. It should be used when the percentage of an operation is known. The progress is represented by setting the `value` property. This can be used to show the progress increasing from 0 to 100% of the track.
|
||||
|
||||
A buffer shows circles as animation to indicate some activity. If the `buffer` property is smaller than 1 you can show the additional buffering progress.
|
||||
If the `buffer` property is set, a buffer stream will show with animated circles to indicate activity. The value of the `buffer` property will also be represented by how much visible track there is. If the value of `buffer` is less than the `value` property, there will be no visible track. If `buffer` is equal to `1` then the buffer stream will be hidden.
|
||||
|
||||
### Indeterminate
|
||||
|
||||
If an operation is in progress and it's not necessary to indicate how long it will take.
|
||||
|
||||
If you add `reversed="true"`, you receive a query which is used to indicate pre-loading.
|
||||
The indeterminate type should be used when it is unknown how long the process will take. The progress bar is not tied to the `value`, instead it continually slides along the track until the process is complete.
|
||||
|
||||
<!-- Auto Generated Below -->
|
||||
|
||||
@@ -144,13 +142,22 @@ export default defineComponent({
|
||||
| `value` | `value` | The value determines how much of the active bar should display when the `type` is `"determinate"`. The value should be between [0, 1]. | `number` | `0` |
|
||||
|
||||
|
||||
## Shadow Parts
|
||||
|
||||
| Part | Description |
|
||||
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `"progress"` | The progress bar that shows the current value when `type` is `"determinate"` and slides back and forth when `type` is `"indeterminate"`. |
|
||||
| `"stream"` | The animated circles that appear while buffering. This only shows when `buffer` is set and `type` is `"determinate"`. |
|
||||
| `"track"` | The track bar behind the progress bar. If the `buffer` property is set and `type` is `"determinate"` the track will be the width of the `buffer` value. |
|
||||
|
||||
|
||||
## CSS Custom Properties
|
||||
|
||||
| Name | Description |
|
||||
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `--background` | Same as --buffer-background when using a determinate progress bar, otherwise it styles the background of the ion-progress-bar itself. |
|
||||
| `--buffer-background` | Color of the buffer bar |
|
||||
| `--progress-background` | Color of the progress bar |
|
||||
| Name | Description |
|
||||
| ----------------------- | ---------------------------------------------------------------------- |
|
||||
| `--background` | Background of the progress track, or the buffer bar if `buffer` is set |
|
||||
| `--buffer-background` | DEPRECATED, use `--background` instead |
|
||||
| `--progress-background` | Background of the progress bar representing the current value |
|
||||
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script> <style>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
|
||||
<style>
|
||||
.custom-bar-background {
|
||||
--buffer-background: red;
|
||||
}
|
||||
@@ -126,13 +128,26 @@
|
||||
</ion-list-header>
|
||||
<ion-progress-bar color="tertiary" buffer="0"></ion-progress-bar>
|
||||
|
||||
<ion-list-header>
|
||||
<ion-label>
|
||||
Buffer (change buffer with slider)
|
||||
</ion-label>
|
||||
</ion-list-header>
|
||||
<ion-progress-bar class="progressBarBuffer" value="0.20" buffer="0.4"></ion-progress-bar>
|
||||
<ion-progress-bar class="progressBarBuffer" value="0.20" buffer="0.4" reversed="true"></ion-progress-bar>
|
||||
|
||||
<ion-item>
|
||||
<ion-range pin="true" value="0" id="progressValueBuffer">
|
||||
<ion-label slot="start">0</ion-label>
|
||||
<ion-label slot="end">100</ion-label>
|
||||
</ion-range>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
</ion-content>
|
||||
|
||||
</ion-app>
|
||||
|
||||
<script>
|
||||
// Progress Bar Value
|
||||
const progressValue = document.getElementById('progressValue');
|
||||
const progressBar = document.getElementById('progressBar');
|
||||
|
||||
@@ -140,6 +155,13 @@
|
||||
progressBar.value = ev.detail.value / 100;
|
||||
});
|
||||
|
||||
// Progress Bar Buffer
|
||||
const progressValueBuffer = document.getElementById('progressValueBuffer');
|
||||
const progressBarBuffer = document.querySelectorAll('.progressBarBuffer');
|
||||
|
||||
progressValueBuffer.addEventListener('ionChange', function (ev) {
|
||||
progressBarBuffer.forEach(ele => ele.buffer = ev.detail.value / 100);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
10
core/src/components/progress-bar/test/standalone/e2e.ts
Normal file
10
core/src/components/progress-bar/test/standalone/e2e.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
test('progress-bar: standalone', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/progress-bar/test/standalone?ionic:_testing=true'
|
||||
});
|
||||
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
});
|
||||
@@ -12,19 +12,171 @@
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script></head>
|
||||
|
||||
<body>
|
||||
<h1>Default Progress Bar</h1>
|
||||
<h1>Default Progress Bars</h1>
|
||||
<ion-progress-bar></ion-progress-bar>
|
||||
|
||||
<h1>Default Progress Bar with 50% width</h1>
|
||||
<ion-progress-bar value="0.5"></ion-progress-bar>
|
||||
|
||||
<h1>Colorize Progress Bar</h1>
|
||||
<ion-progress-bar color="primary" value="0.5"></ion-progress-bar>
|
||||
<ion-progress-bar color="secondary" value="0.5"></ion-progress-bar>
|
||||
|
||||
<h1>Other types</h1>
|
||||
<ion-progress-bar type="indeterminate"></ion-progress-bar>
|
||||
|
||||
<hr>
|
||||
|
||||
<h1>Progress Bar: Colors</h1>
|
||||
<ion-progress-bar color="primary" value="0.5"></ion-progress-bar>
|
||||
<ion-progress-bar color="secondary" value="0.5"></ion-progress-bar>
|
||||
<ion-progress-bar color="tertiary" value="0.5"></ion-progress-bar>
|
||||
<ion-progress-bar color="success" value="0.5"></ion-progress-bar>
|
||||
<ion-progress-bar color="warning" value="0.5"></ion-progress-bar>
|
||||
<ion-progress-bar color="danger" value="0.5"></ion-progress-bar>
|
||||
<ion-progress-bar color="dark" value="0.5"></ion-progress-bar>
|
||||
<ion-progress-bar color="light" value="0.5"></ion-progress-bar>
|
||||
|
||||
<hr>
|
||||
|
||||
<h1>Default Types</h1>
|
||||
<ion-progress-bar></ion-progress-bar>
|
||||
<ion-progress-bar value="0.5"></ion-progress-bar>
|
||||
<ion-progress-bar value="0.20" buffer="0.4"></ion-progress-bar>
|
||||
<ion-progress-bar color="warning" reversed="true" value="0.20" buffer="0.4"></ion-progress-bar>
|
||||
<ion-progress-bar buffer="0"></ion-progress-bar>
|
||||
<ion-progress-bar color="danger" type="indeterminate"></ion-progress-bar>
|
||||
<ion-progress-bar type="indeterminate" reversed="true"></ion-progress-bar>
|
||||
|
||||
<hr>
|
||||
|
||||
<h1>Custom: colors by part</h1>
|
||||
<ion-progress-bar class="custom-color-parts"></ion-progress-bar>
|
||||
<ion-progress-bar class="custom-color-parts" buffer="0.9"></ion-progress-bar>
|
||||
<ion-progress-bar class="custom-color-parts" value="0.5"></ion-progress-bar>
|
||||
<ion-progress-bar class="custom-color-parts" value="0.20" buffer="0.4"></ion-progress-bar>
|
||||
<ion-progress-bar class="custom-color-parts" color="warning" reversed="true" value="0.20" buffer="0.4"></ion-progress-bar>
|
||||
<ion-progress-bar class="custom-color-parts" buffer="0"></ion-progress-bar>
|
||||
<ion-progress-bar class="custom-color-parts" color="danger" type="indeterminate"></ion-progress-bar>
|
||||
<ion-progress-bar class="custom-color-parts" type="indeterminate" reversed="true"></ion-progress-bar>
|
||||
|
||||
<hr>
|
||||
|
||||
<h1>Custom: colors by css variable</h1>
|
||||
<ion-progress-bar class="custom-color-variables"></ion-progress-bar>
|
||||
<ion-progress-bar class="custom-color-variables" value="0.5"></ion-progress-bar>
|
||||
<ion-progress-bar class="custom-color-variables" value="0.20" buffer="0.4"></ion-progress-bar>
|
||||
<ion-progress-bar class="custom-color-variables" color="warning" reversed="true" value="0.20" buffer="0.4"></ion-progress-bar>
|
||||
<ion-progress-bar class="custom-color-variables" buffer="0"></ion-progress-bar>
|
||||
<ion-progress-bar class="custom-color-variables" color="danger" type="indeterminate"></ion-progress-bar>
|
||||
<ion-progress-bar class="custom-color-variables" type="indeterminate" reversed="true"></ion-progress-bar>
|
||||
|
||||
<hr>
|
||||
|
||||
<h1>Custom border radius</h1>
|
||||
<ion-progress-bar class="custom-border-radius"></ion-progress-bar>
|
||||
<ion-progress-bar class="custom-border-radius" value="0.5"></ion-progress-bar>
|
||||
<ion-progress-bar class="custom-border-radius" type="indeterminate"></ion-progress-bar>
|
||||
|
||||
<h1>Custom transition</h1>
|
||||
<ion-progress-bar class="random-value" max="100"></ion-progress-bar>
|
||||
<ion-progress-bar class="random-value custom-transition" max="100"></ion-progress-bar>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
let randomValues = document.querySelectorAll('.random-value');
|
||||
|
||||
setInterval(() => {
|
||||
let value = Math.random();
|
||||
|
||||
for (let i = 0; i < randomValues.length; i++) {
|
||||
randomValues[i].value = value;
|
||||
}
|
||||
}, 100);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
|
||||
color: #a1a7b0;
|
||||
|
||||
margin-top: 10px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
hr {
|
||||
background: #eff1f3;
|
||||
border: none;
|
||||
|
||||
height: 1px;
|
||||
|
||||
margin-top: 18px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
ion-progress-bar {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/*
|
||||
* Custom progress bar color using parts
|
||||
* ------------------------------------------------------
|
||||
* Note: in these examples setting the background on
|
||||
* each element should override the color prop
|
||||
*/
|
||||
|
||||
/* determinate buffer / track and indeterminate track */
|
||||
.custom-color-parts::part(track) {
|
||||
background:rgb(158, 157, 36, 0.2);
|
||||
}
|
||||
|
||||
/* determinate and indeterminate progress background */
|
||||
.custom-color-parts::part(progress) {
|
||||
background: #9e9d24;
|
||||
}
|
||||
|
||||
/* buffer stream (animated circles) */
|
||||
.custom-color-parts::part(stream) {
|
||||
background-image: radial-gradient(ellipse at center, rgb(158, 157, 36, 0.2) 0%, rgb(158, 157, 36, 0.2) 30%, transparent 30%);
|
||||
}
|
||||
|
||||
/*
|
||||
* Custom progress bar color using css variables
|
||||
* ------------------------------------------------------
|
||||
* Note: in this example setting the background via
|
||||
* CSS variables should NOT override the color prop
|
||||
*/
|
||||
.custom-color-variables {
|
||||
--background: rgb(158, 157, 36, 0.2);
|
||||
--progress-background: #9e9d24;
|
||||
}
|
||||
|
||||
/*
|
||||
* Custom progress bar border radius using parts
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
.custom-border-radius {
|
||||
border-radius: 10px;
|
||||
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.custom-border-radius::part(progress) {
|
||||
border-radius: 0 50% 50% 0;
|
||||
}
|
||||
|
||||
.custom-border-radius[type="indeterminate"]::part(progress) {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/*
|
||||
* Custom transition for fast value changes
|
||||
* ------------------------------------------------------
|
||||
* The first progress bar in the example has the default
|
||||
* transition, while the second has none. This is
|
||||
* apparent because they use the same values.
|
||||
*/
|
||||
.custom-transition::part(progress) {
|
||||
transition: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -10,6 +10,7 @@ export class RadioGroup implements ComponentInterface {
|
||||
|
||||
private inputId = `ion-rg-${radioGroupIds++}`;
|
||||
private labelId = `${this.inputId}-lbl`;
|
||||
private label?: HTMLIonLabelElement | null;
|
||||
|
||||
@Element() el!: HTMLElement;
|
||||
|
||||
@@ -68,10 +69,9 @@ export class RadioGroup implements ComponentInterface {
|
||||
async connectedCallback() {
|
||||
// Get the list header if it exists and set the id
|
||||
// this is used to set aria-labelledby
|
||||
const el = this.el;
|
||||
const header = el.querySelector('ion-list-header') || el.querySelector('ion-item-divider');
|
||||
const header = this.el.querySelector('ion-list-header') || this.el.querySelector('ion-item-divider');
|
||||
if (header) {
|
||||
const label = header.querySelector('ion-label');
|
||||
const label = this.label = header.querySelector('ion-label');
|
||||
if (label) {
|
||||
this.labelId = label.id = this.name + '-lbl';
|
||||
}
|
||||
@@ -83,6 +83,8 @@ export class RadioGroup implements ComponentInterface {
|
||||
}
|
||||
|
||||
private onClick = (ev: Event) => {
|
||||
ev.preventDefault();
|
||||
|
||||
const selectedRadio = ev.target && (ev.target as HTMLElement).closest('ion-radio');
|
||||
if (selectedRadio) {
|
||||
const currentValue = this.value;
|
||||
@@ -110,12 +112,13 @@ export class RadioGroup implements ComponentInterface {
|
||||
// Only move the radio if the current focus is in the radio group
|
||||
if (ev.target && radios.includes(ev.target)) {
|
||||
const index = radios.findIndex(radio => radio === ev.target);
|
||||
const current = radios[index];
|
||||
|
||||
let next;
|
||||
|
||||
// If hitting arrow down or arrow right, move to the next radio
|
||||
// If we're on the last radio, move to the first radio
|
||||
if (['ArrowDown', 'ArrowRight'].includes(ev.key)) {
|
||||
if (['ArrowDown', 'ArrowRight'].includes(ev.code)) {
|
||||
next = (index === radios.length - 1)
|
||||
? radios[0]
|
||||
: radios[index + 1];
|
||||
@@ -123,29 +126,43 @@ export class RadioGroup implements ComponentInterface {
|
||||
|
||||
// If hitting arrow up or arrow left, move to the previous radio
|
||||
// If we're on the first radio, move to the last radio
|
||||
if (['ArrowUp', 'ArrowLeft'].includes(ev.key)) {
|
||||
if (['ArrowUp', 'ArrowLeft'].includes(ev.code)) {
|
||||
next = (index === 0)
|
||||
? radios[radios.length - 1]
|
||||
: radios[index - 1];
|
||||
}
|
||||
|
||||
if (next && radios.includes(next)) {
|
||||
next.setFocus();
|
||||
next.setFocus(ev);
|
||||
|
||||
if (!inSelectPopover) {
|
||||
this.value = next.value;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the radio group value when a user presses the
|
||||
// space bar on top of a selected radio (only applies
|
||||
// to radios in a select popover)
|
||||
if (['Space'].includes(ev.code)) {
|
||||
this.value = current.value;
|
||||
|
||||
// Prevent browsers from jumping
|
||||
// to the bottom of the screen
|
||||
ev.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { label, labelId } = this;
|
||||
const mode = getIonMode(this);
|
||||
|
||||
return (
|
||||
<Host
|
||||
role="radiogroup"
|
||||
aria-labelledby={this.labelId}
|
||||
aria-labelledby={label ? labelId : null}
|
||||
onClick={this.onClick}
|
||||
class={getIonMode(this)}
|
||||
class={mode}
|
||||
>
|
||||
</Host>
|
||||
);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user