Compare commits

..

12 Commits

Author SHA1 Message Date
Liam DeBeasi
38e97bc0b3 5.4.3 2020-11-06 12:08:44 -05:00
Liam DeBeasi
0989ea5ac8 fix(): add missing vendor prefixes to css 2020-11-06 11:56:34 -05:00
Liam DeBeasi
69928924f7 merge release-5.4.2
Release 5.4.2
2020-11-05 13:51:36 -05:00
Liam DeBeasi
5ffacb1875 merge release-5.4.1
Release 5.4.1
2020-10-22 12:33:44 -04:00
Liam DeBeasi
fe99fbf5e8 merge release-5.4.0
Release 5.4.0
2020-10-15 13:15:06 -04:00
Brandy Carney
3a4555c5a9 merge release-5.3.5
Release 5.3.5
2020-10-07 14:39:34 -04:00
Brandy Carney
e17e6cdd08 merge release-5.3.4
Release 5.3.4
2020-09-29 11:34:56 -04:00
Brandy Carney
5c15ed5161 merge release-5.3.3
Release 5.3.3
2020-09-17 19:54:30 -04:00
Brandy Carney
f0cebfb617 release-5.3.2 2020-08-27 17:57:25 -04:00
Liam DeBeasi
265f6d6e6c merge release-5.3.1
Release 5.3.1
2020-07-27 10:23:42 -04:00
Liam DeBeasi
33247065a6 merge release-5.2.3
Release 5.2.3
2020-07-23 12:17:56 -04:00
Liam DeBeasi
8cbde1a3df merge release-5.2.3
Release 5.2.3
2020-07-02 09:11:44 -04:00
77 changed files with 2559 additions and 4807 deletions

View File

@@ -185,7 +185,7 @@ jobs:
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install
command: npm install --legacy-peer-deps
working_directory: /tmp/workspace/packages/vue
- run:
command: sudo npm link
@@ -208,7 +208,7 @@ jobs:
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install
command: npm install --legacy-peer-deps
working_directory: /tmp/workspace/packages/vue-router
- run:
command: sudo npm link
@@ -442,11 +442,8 @@ workflows:
requires: [puppeteer-dependencies, build-core]
- test-core-spec:
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-treeshake:
requires: [build-core]
- test-core-screenshot:
requires: [build-core]
filters:

View File

@@ -9,8 +9,6 @@
* [Ripple Effect](#ripple-effect)
* [Example Components](#example-components)
* [References](#references)
- [Accessibility](#accessibility)
* [Checkbox](#checkbox)
- [Rendering Anchor or Button](#rendering-anchor-or-button)
* [Example Components](#example-components-1)
* [Component Structure](#component-structure-1)
@@ -364,131 +362,6 @@ 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)
- [ion-toggle](https://github.com/ionic-team/ionic/tree/master/core/src/components/toggle)
#### 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
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 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.
## Rendering Anchor or Button
Certain components can render an `<a>` or a `<button>` depending on the presence of an `href` attribute.

View File

@@ -63,9 +63,8 @@ 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 || !hasEsmEs5 || !hasCjs) {
if (!hasEsm || !hasCjs) {
throw new Error('core build is not a production build');
}
}

View File

@@ -1,42 +1,3 @@
# [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)

View File

@@ -1,12 +1,12 @@
{
"name": "@ionic/angular",
"version": "5.5.0",
"version": "5.4.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/angular",
"version": "5.5.0",
"version": "5.4.3",
"license": "MIT",
"dependencies": {
"@ionic/core": "file:../core",
@@ -43,7 +43,7 @@
}
},
"../core": {
"version": "5.4.4",
"version": "5.4.2",
"license": "MIT",
"dependencies": {
"ionicons": "^5.1.2",

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/angular",
"version": "5.5.0",
"version": "5.4.3",
"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.5.0",
"@ionic/core": "5.4.3",
"tslib": "^1.9.3"
},
"peerDependencies": {

View File

@@ -156,8 +156,8 @@ export class IonCheckbox {
}
export declare interface IonChip extends Components.IonChip {
}
@ProxyCmp({ inputs: ["color", "disabled", "mode", "outline"] })
@Component({ selector: "ion-chip", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>", inputs: ["color", "disabled", "mode", "outline"] })
@ProxyCmp({ inputs: ["color", "mode", "outline"] })
@Component({ selector: "ion-chip", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>", inputs: ["color", "mode", "outline"] })
export class IonChip {
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
@@ -661,8 +661,8 @@ export class IonSearchbar {
}
export declare interface IonSegment extends Components.IonSegment {
}
@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"] })
@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"] })
export class IonSegment {
ionChange!: EventEmitter<CustomEvent>;
protected el: HTMLElement;

View File

@@ -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 87.0.4280.20",
"pretest": "webdriver-manager update --versions.chrome 85.0.4183.87",
"test": "ng e2e --prod --webdriver-update=false",
"test.dev": "npm run sync && ng e2e",
"lint": "ng lint",

View File

@@ -248,7 +248,6 @@ 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
@@ -1015,7 +1014,6 @@ 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

View File

@@ -1,12 +1,12 @@
{
"name": "@ionic/core",
"version": "5.5.0",
"version": "5.4.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/core",
"version": "5.5.0",
"version": "5.4.3",
"license": "MIT",
"dependencies": {
"ionicons": "^5.1.2",

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/core",
"version": "5.5.0",
"version": "5.4.3",
"description": "Base components for Ionic",
"keywords": [
"ionic",
@@ -75,7 +75,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",

View File

@@ -394,7 +394,7 @@ export namespace Components {
*/
"name": string;
/**
* 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>`.
* 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>`.
*/
"value": string;
}
@@ -403,10 +403,6 @@ 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.
*/
@@ -2064,10 +2060,6 @@ 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.
*/
@@ -3709,7 +3701,7 @@ declare namespace LocalJSX {
*/
"name"?: string;
/**
* Emitted when the checkbox loses focus.
* Emitted when the toggle loses focus.
*/
"onIonBlur"?: (event: CustomEvent<void>) => void;
/**
@@ -3717,7 +3709,7 @@ declare namespace LocalJSX {
*/
"onIonChange"?: (event: CustomEvent<CheckboxChangeEventDetail>) => void;
/**
* Emitted when the checkbox has focus.
* Emitted when the toggle has focus.
*/
"onIonFocus"?: (event: CustomEvent<void>) => void;
/**
@@ -3725,7 +3717,7 @@ declare namespace LocalJSX {
*/
"onIonStyle"?: (event: CustomEvent<StyleEventDetail>) => void;
/**
* 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>`.
* 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>`.
*/
"value"?: string;
}
@@ -3734,10 +3726,6 @@ 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.
*/
@@ -5378,10 +5366,6 @@ 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.
*/

View File

@@ -67,7 +67,6 @@ export class Backdrop implements ComponentInterface {
return (
<Host
tabindex="-1"
aria-hidden="true"
class={{
[mode]: true,
'backdrop-hide': !this.visible,

View File

@@ -40,18 +40,8 @@
--checkmark-color: #{current-color(contrast)};
}
label {
button {
@include input-cover();
display: flex;
align-items: center;
opacity: 0;
}
input {
@include visually-hidden();
}
.checkbox-icon {

View File

@@ -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 { getAriaLabel, renderHiddenInput } from '../../utils/helpers';
import { findItemLabel, 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 focusEl?: HTMLElement;
private buttonEl?: HTMLElement;
@Element() el!: HTMLElement;
@@ -54,11 +54,11 @@ export class Checkbox implements ComponentInterface {
@Prop() disabled = false;
/**
* The value of the checkbox does not mean if it's checked or not, use the `checked`
* The value of the toggle 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>`.
* 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>`.
*/
@Prop() value = 'on';
@@ -68,12 +68,12 @@ export class Checkbox implements ComponentInterface {
@Event() ionChange!: EventEmitter<CheckboxChangeEventDetail>;
/**
* Emitted when the checkbox has focus.
* Emitted when the toggle has focus.
*/
@Event() ionFocus!: EventEmitter<void>;
/**
* Emitted when the checkbox loses focus.
* Emitted when the toggle loses focus.
*/
@Event() ionBlur!: EventEmitter<void>;
@@ -109,15 +109,12 @@ export class Checkbox implements ComponentInterface {
}
private setFocus() {
if (this.focusEl) {
this.focusEl.focus();
if (this.buttonEl) {
this.buttonEl.focus();
}
}
private onClick = (ev: Event) => {
ev.preventDefault();
ev.stopPropagation();
private onClick = () => {
this.setFocus();
this.checked = !this.checked;
this.indeterminate = false;
@@ -132,11 +129,14 @@ export class Checkbox implements ComponentInterface {
}
render() {
const { color, checked, disabled, el, indeterminate, inputId, name, value } = this;
const { inputId, indeterminate, disabled, checked, value, color, el } = this;
const labelId = inputId + '-lbl';
const mode = getIonMode(this);
const { label, labelId, labelText } = getAriaLabel(el, inputId);
renderHiddenInput(true, el, name, (checked ? value : ''), disabled);
const label = findItemLabel(el);
if (label) {
label.id = labelId;
}
renderHiddenInput(true, el, this.name, (checked ? value : ''), disabled);
let path = indeterminate
? <path d="M6 12L18 12" part="mark" />
@@ -151,10 +151,10 @@ export class Checkbox implements ComponentInterface {
return (
<Host
onClick={this.onClick}
aria-labelledby={label ? labelId : null}
aria-checked={`${checked}`}
aria-hidden={disabled ? 'true' : null}
role="checkbox"
aria-disabled={disabled ? 'true' : null}
aria-checked={`${checked}`}
aria-labelledby={labelId}
class={createColorClasses(color, {
[mode]: true,
'in-item': hostContext('ion-item', el),
@@ -167,18 +167,14 @@ export class Checkbox implements ComponentInterface {
<svg class="checkbox-icon" viewBox="0 0 24 24" part="container">
{path}
</svg>
<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}
/>
<button
type="button"
onFocus={this.onFocus}
onBlur={this.onBlur}
disabled={this.disabled}
ref={btnEl => this.buttonEl = btnEl}
>
</button>
</Host>
);
}

View File

@@ -228,8 +228,8 @@ export class CheckboxExample {
<ion-label>{{entry.val}}</ion-label>
<ion-checkbox
slot="end"
@update:modelValue="entry.isChecked = $event"
:modelValue="entry.isChecked">
@input="entry.checked = $event.target.value"
:value="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 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'` |
| `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'` |
## Events
| Event | Description | Type |
| ----------- | ---------------------------------------------- | ---------------------------------------- |
| `ionBlur` | Emitted when the checkbox loses focus. | `CustomEvent<void>` |
| `ionBlur` | Emitted when the toggle loses focus. | `CustomEvent<void>` |
| `ionChange` | Emitted when the checked property has changed. | `CustomEvent<CheckboxChangeEventDetail>` |
| `ionFocus` | Emitted when the checkbox has focus. | `CustomEvent<void>` |
| `ionFocus` | Emitted when the toggle has focus. | `CustomEvent<void>` |
## Shadow Parts

View File

@@ -38,7 +38,7 @@
<ion-item>
<ion-label>Secondary</ion-label>
<ion-checkbox disabled checked color="secondary"></ion-checkbox>
<ion-checkbox checked color="secondary"></ion-checkbox>
</ion-item>
<ion-item>
@@ -103,31 +103,6 @@
</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');
});
}
</script>
</body>
</html>

View File

@@ -31,22 +31,22 @@
<div class="ion-padding-start">
<!-- Default to unchecked -->
<label for="unchecked">Unchecked</label>
<input name="unchecked" id="unchecked" type="checkbox">
<input name="unchecked" type="checkbox">
<br>
<!-- Default to checked -->
<label for="checked">Checked</label>
<input name="checked" id="checked" type="checkbox" checked />
<input name="checked" type="checkbox" checked />
<br>
<!-- Default to indeterminate -->
<label for="indeterminate">Indeterminate</label>
<input name="indeterminate" id="indeterminate" type="checkbox" class="indeterminate">
<input name="indeterminate" type="checkbox" class="indeterminate">
<br>
<!-- Default to checked / indeterminate -->
<label for="both">Checked / Indeterminate</label>
<input name="both" id="both" type="checkbox" checked class="indeterminate">
<input name="both" type="checkbox" checked class="indeterminate">
<br>
</div>
@@ -81,15 +81,15 @@
</ion-label>
</ion-list-header>
<div class="ion-padding-start">
<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>
<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>
</div>
<ion-list-header>
@@ -100,20 +100,20 @@
<ul>
<li>
<ion-checkbox aria-labelledby="tall-label-0" indeterminate></ion-checkbox>
<label id="tall-label-0">Tall Things</label>
<ion-checkbox name="tall" id="tall" indeterminate></ion-checkbox>
<label for="tall">Tall Things</label>
<ul>
<li>
<ion-checkbox aria-labelledby="tall-label-1" checked></ion-checkbox>
<label id="tall-label-1">Skyscrapers</label>
<ion-checkbox name="tall-1" id="tall-1" checked></ion-checkbox>
<label for="tall-1">Skyscrapers</label>
</li>
<li>
<ion-checkbox aria-labelledby="tall-label-2"></ion-checkbox>
<label id="tall-label-2">Trees</label>
<ion-checkbox name="tall-2" id="tall-2"></ion-checkbox>
<label for="tall-2">Trees</label>
</li>
<li>
<ion-checkbox aria-labelledby="tall-label-3"></ion-checkbox>
<label id="tall-label-3">Giants</label>
<ion-checkbox name="tall-2" id="tall-2"></ion-checkbox>
<label for="tall-2">Giants</label>
</li>
</ul>
</li>

View File

@@ -13,67 +13,67 @@
<body class="ion-padding">
<h1>Default</h1>
<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>
<ion-checkbox></ion-checkbox>
<ion-checkbox checked></ion-checkbox>
<ion-checkbox disabled></ion-checkbox>
<ion-checkbox disabled checked></ion-checkbox>
<h1>Colors</h1>
<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>
<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>
<hr>
<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>
<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>
<hr>
<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>
<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>
<h1>Custom</h1>
<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>
<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>
<h1>Custom: checked</h1>
<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>
<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>
<h1>Custom: light</h1>
<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>
<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>
<h1>Custom: transition</h1>
<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>
<ion-checkbox class="custom-transition"></ion-checkbox>
<ion-checkbox class="custom-transition" checked></ion-checkbox>
<style>
.custom {

View File

@@ -22,8 +22,8 @@
<ion-label>{{entry.val}}</ion-label>
<ion-checkbox
slot="end"
@update:modelValue="entry.isChecked = $event"
:modelValue="entry.isChecked">
@input="entry.checked = $event.target.value"
:value="entry.isChecked">
</ion-checkbox>
</ion-item>
</ion-list>

View File

@@ -37,11 +37,6 @@
box-sizing: border-box;
}
:host(.chip-disabled) {
cursor: default;
opacity: .4;
pointer-events: none;
}
// Chip Colors
// ---------------------------------------------

View File

@@ -28,21 +28,14 @@ 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,
})}
>

View File

@@ -7,7 +7,7 @@ Chips represent complex entities in small blocks, such as a contact. A chip can
## Usage
### Angular
### Angular / javascript
```html
<ion-chip>
@@ -22,60 +22,6 @@ 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>
@@ -135,10 +81,6 @@ 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>
@@ -249,10 +191,6 @@ 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>
@@ -302,12 +240,11 @@ 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` |
| `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` |
| 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` |
## CSS Custom Properties

View File

@@ -17,7 +17,6 @@
<ion-app>
<ion-content>
<h3>Default</h3>
<p>
<ion-chip>
<ion-label>Default</ion-label>
@@ -196,26 +195,6 @@
</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 -->
@@ -267,6 +246,11 @@
padding-left: 8px;
}
ion-chip {
display: inline-block !important;
vertical-align: middle;
}
.wide {
--background: #d1f3ff;
--background-hover: #add8e6;

View File

@@ -11,10 +11,6 @@
<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>

View File

@@ -11,10 +11,6 @@
<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>

View File

@@ -24,10 +24,6 @@ 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>
@@ -61,4 +57,4 @@ export const ChipExamples: React.FC = () => {
);
};
```
```

View File

@@ -12,10 +12,6 @@
<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>

View File

@@ -100,9 +100,7 @@
*
* See: https://bugs.webkit.org/show_bug.cgi?id=216701
*/
z-index: 0;
will-change: scroll-position;
will-change: scroll-position, transform;
}
.scroll-y {

View File

@@ -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, inheritAttributes } from '../../utils/helpers';
import { debounceEvent, findItemLabel } 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 inheritedAttributes: { [k: string]: any } = {};
private tabindex?: string | number;
/**
* This is required for a WebKit bug which requires us to
@@ -225,7 +225,14 @@ export class Input implements ComponentInterface {
@Event() ionStyle!: EventEmitter<StyleEventDetail>;
componentWillLoad() {
this.inheritedAttributes = inheritAttributes(this.el, ['tabindex', 'title']);
// 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');
}
}
connectedCallback() {
@@ -421,13 +428,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"

View File

@@ -165,7 +165,7 @@ export class Refresher implements ComponentInterface {
private async setupiOSNativeRefresher(pullingSpinner: HTMLIonSpinnerElement, refreshingSpinner: HTMLIonSpinnerElement) {
this.elementToTransform = this.scrollEl!;
const ticks = pullingSpinner.shadowRoot!.querySelectorAll('svg');
let MAX_PULL = this.scrollEl!.clientHeight * 0.16;
const MAX_PULL = this.scrollEl!.clientHeight * 0.16;
const NUM_TICKS = ticks.length;
writeTask(() => ticks.forEach(el => el.style.setProperty('animation', 'none')));
@@ -242,7 +242,7 @@ export class Refresher implements ComponentInterface {
this.gesture = (await import('../../utils/gesture')).createGesture({
el: this.scrollEl!,
gestureName: 'refresher',
gesturePriority: 31,
gesturePriority: 10,
direction: 'y',
threshold: 5,
onStart: () => {
@@ -251,18 +251,6 @@ export class Refresher implements ComponentInterface {
if (!this.didRefresh) {
translateElement(this.elementToTransform, '0px');
}
/**
* If the content had `display: none` when
* the refresher was initialized, its clientHeight
* will be 0. When the gesture starts, the content
* will be visible, so try to get the correct
* client height again. This is most common when
* using the refresher in an ion-menu.
*/
if (MAX_PULL === 0) {
MAX_PULL = this.scrollEl!.clientHeight * 0.16;
}
},
onMove: ev => {
this.lastVelocityY = ev.velocityY;
@@ -301,7 +289,7 @@ export class Refresher implements ComponentInterface {
this.gesture = (await import('../../utils/gesture')).createGesture({
el: this.scrollEl!,
gestureName: 'refresher',
gesturePriority: 31,
gesturePriority: 10,
direction: 'y',
threshold: 5,
canStart: () => this.state !== RefresherState.Refreshing && this.state !== RefresherState.Completing && this.scrollEl!.scrollTop === 0,
@@ -417,7 +405,7 @@ export class Refresher implements ComponentInterface {
this.gesture = (await import('../../utils/gesture')).createGesture({
el: contentEl,
gestureName: 'refresher',
gesturePriority: 31,
gesturePriority: 10,
direction: 'y',
threshold: 20,
passive: false,

View File

@@ -566,14 +566,13 @@ 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` |
| `disabled` | `disabled` | If `true`, the user cannot interact with the segment. | `boolean` | `false` |
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` |
| `scrollable` | `scrollable` | 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. | `boolean` | `false` |
| `swipeGesture` | `swipe-gesture` | If `true`, users will be able to swipe between segment buttons to activate them. | `boolean` | `true` |
| `value` | `value` | the value of the segment. | `null \| string \| undefined` | `undefined` |
| 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 segment. | `boolean` | `false` |
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` |
| `scrollable` | `scrollable` | 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. | `boolean` | `false` |
| `value` | `value` | the value of the segment. | `null \| string \| undefined` | `undefined` |
## Events

View File

@@ -65,16 +65,6 @@ export class Segment implements ComponentInterface {
*/
@Prop() scrollable = false;
/**
* If `true`, users will be able to swipe between segment buttons to activate them.
*/
@Prop() swipeGesture = true;
@Watch('swipeGesture')
swipeGestureChanged() {
this.gestureChanged();
}
/**
* the value of the segment.
*/
@@ -121,8 +111,8 @@ export class Segment implements ComponentInterface {
}
private gestureChanged() {
if (this.gesture) {
this.gesture.enable(!this.scrollable && !this.disabled && this.swipeGesture);
if (this.gesture && !this.scrollable) {
this.gesture.enable(!this.disabled);
}
}
@@ -147,6 +137,7 @@ export class Segment implements ComponentInterface {
onMove: ev => this.onMove(ev),
onEnd: ev => this.onEnd(ev),
});
this.gesture.enable(!this.scrollable);
this.gestureChanged();
if (this.disabled) {
@@ -399,18 +390,9 @@ export class Segment implements ComponentInterface {
private onClick = (ev: Event) => {
const current = ev.target as HTMLIonSegmentButtonElement;
const previous = this.checked;
// If the current element is a segment then that means
// the user tried to swipe to a segment button and
// click a segment button at the same time so we should
// not update the checked segment button
if (current.tagName === 'ION-SEGMENT') {
return;
}
this.value = current.value;
if (this.scrollable || !this.swipeGesture) {
if (this.scrollable) {
if (previous) {
this.checkButton(previous, current);
} else {

View File

@@ -198,9 +198,6 @@
<div class="ion-padding">
<ion-button expand="block" color="secondary" onClick="toggleValue()">Toggle Value</ion-button>
</div>
<div class="ion-padding-horizontal">
<ion-button expand="block" color="tertiary" onClick="toggleSwipeGesture()">Toggle Swipe Gesture</ion-button>
</div>
<script>
var dynamicAttrDisable = document.getElementsByName('dynamicAttrDisable');
@@ -232,13 +229,6 @@
}
}
function toggleSwipeGesture() {
const ionSegmentElement = document.querySelectorAll('ion-segment');
for (var i = 0; i < ionSegmentElement.length; i++) {
ionSegmentElement[i].swipeGesture = !ionSegmentElement[i].swipeGesture;
}
}
async function listenForEvent() {
const ionSegmentElement = document.querySelector('ion-segment.event-tester');
ionSegmentElement.addEventListener('ionChange', (event) => {

View File

@@ -2,10 +2,9 @@
The tab component is a child component of [tabs](../tabs). Each tab can contain a top level navigation stack for an app or a single view. An app can have many tabs, all with their own independent navigation.
> Note: This component should only be used with vanilla or Stencil JavaScript projects. For Angular, React, and Vue apps you do not need to use `ion-tab` to declare your tab components.
See the [tabs documentation](../tabs/) for more details on configuring tabs.
<!-- Auto Generated Below -->

View File

@@ -307,117 +307,59 @@ will match the following tab:
### Vue
**Tabs.vue**
```html
<template>
<ion-page>
<ion-tabs @ionTabsWillChange="beforeTabChange" @ionTabsDidChange="afterTabChange">
<ion-tab-bar slot="bottom">
<ion-tab-button tab="schedule" href="/tabs/schedule">
<ion-icon :icon="calendar"></ion-icon>
<!-- Listen to before and after tab change events -->
<ion-tabs @IonTabsWillChange="beforeTabChange" @IonTabsDidChange="afterTabChange">
<ion-tab tab="schedule">
<Schedule />
</ion-tab>
<!-- Match by "app.speakers" route name -->
<ion-tab tab="speakers" :routes="'app.speakers'">
<Speakers />
</ion-tab>
<!-- Match by an array of route names -->
<ion-tab tab="map" :routes="['app.map', 'app.other.route']">
<Map />
</ion-tab>
<!-- Get matched routes with a helper method -->
<ion-tab tab="about" :routes="getMatchedRoutes">
<About />
</ion-tab>
<!-- Use v-slot:bottom with Vue ^2.6.0 -->
<template slot="bottom">
<ion-tab-bar>
<ion-tab-button tab="schedule">
<ion-icon name="calendar"></ion-icon>
<ion-label>Schedule</ion-label>
<ion-badge>6</ion-badge>
</ion-tab-button>
<ion-tab-button tab="speakers" href="/tabs/speakers">
<ion-icon :icon="personCircle"></ion-icon>
<!-- Provide a custom route to navigate to -->
<ion-tab-button tab="speakers" :to="{ name: 'app.speakers' }">
<ion-icon name="person-circle"></ion-icon>
<ion-label>Speakers</ion-label>
</ion-tab-button>
<!-- Provide extra data to route -->
<ion-tab-button tab="map" :to="{ name: 'app.map', params: { mode: 'dark' } }">
<ion-icon name="map"></ion-icon>
<ion-label>Map</ion-label>
</ion-tab-button>
<!-- Provide custom click handler -->
<ion-tab-button tab="about" @click="goToAboutTab">
<ion-icon name="information-circle"></ion-icon>
<ion-label>About</ion-label>
</ion-tab-button>
</ion-tab-bar>
</ion-tabs>
</ion-page>
</template>
</ion-tabs>
</template>
<script>
import { defineComponent } from 'vue';
import {
IonIcon,
IonLabel,
IonPage,
IonTabBar,
IonTabButton,
IonTabs
} from '@ionic/vue';
import { calendar, personCircle } from 'ionicons/icons';
export default defineComponent({
components: { IonIcon, IonLabel, IonPage, IonTabBar, IonTabButton, IonTabs },
setup() {
const beforeTabChange = () => {
// do something before tab change
}
const afterTabChange = () => {
// do something after tab change
}
return {
calendar,
personCircle,
beforeTabChange,
afterTabChange
}
}
});
</script>
```
**Schedule.vue**
```html
<template>
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Schedule</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">Schedule Tab</ion-content>
</ion-page>
</template>
<script>
import { defineComponent } from 'vue';
import {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar
} from '@ionic/vue';
export default defineComponent({
components: { IonContent, IonHeader, IonPage, IonTitle, IonToolbar }
});
</script>
```
**Speakers.vue**
```html
<template>
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Speakers</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">Speakers Tab</ion-content>
</ion-page>
</template>
<script>
import { defineComponent } from 'vue';
import {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar
} from '@ionic/vue';
export default defineComponent({
components: { IonContent, IonHeader, IonPage, IonTitle, IonToolbar }
});
</script>
```

View File

@@ -1,112 +1,54 @@
**Tabs.vue**
```html
<template>
<ion-page>
<ion-tabs @ionTabsWillChange="beforeTabChange" @ionTabsDidChange="afterTabChange">
<ion-tab-bar slot="bottom">
<ion-tab-button tab="schedule" href="/tabs/schedule">
<ion-icon :icon="calendar"></ion-icon>
<!-- Listen to before and after tab change events -->
<ion-tabs @IonTabsWillChange="beforeTabChange" @IonTabsDidChange="afterTabChange">
<ion-tab tab="schedule">
<Schedule />
</ion-tab>
<!-- Match by "app.speakers" route name -->
<ion-tab tab="speakers" :routes="'app.speakers'">
<Speakers />
</ion-tab>
<!-- Match by an array of route names -->
<ion-tab tab="map" :routes="['app.map', 'app.other.route']">
<Map />
</ion-tab>
<!-- Get matched routes with a helper method -->
<ion-tab tab="about" :routes="getMatchedRoutes">
<About />
</ion-tab>
<!-- Use v-slot:bottom with Vue ^2.6.0 -->
<template slot="bottom">
<ion-tab-bar>
<ion-tab-button tab="schedule">
<ion-icon name="calendar"></ion-icon>
<ion-label>Schedule</ion-label>
<ion-badge>6</ion-badge>
</ion-tab-button>
<ion-tab-button tab="speakers" href="/tabs/speakers">
<ion-icon :icon="personCircle"></ion-icon>
<!-- Provide a custom route to navigate to -->
<ion-tab-button tab="speakers" :to="{ name: 'app.speakers' }">
<ion-icon name="person-circle"></ion-icon>
<ion-label>Speakers</ion-label>
</ion-tab-button>
<!-- Provide extra data to route -->
<ion-tab-button tab="map" :to="{ name: 'app.map', params: { mode: 'dark' } }">
<ion-icon name="map"></ion-icon>
<ion-label>Map</ion-label>
</ion-tab-button>
<!-- Provide custom click handler -->
<ion-tab-button tab="about" @click="goToAboutTab">
<ion-icon name="information-circle"></ion-icon>
<ion-label>About</ion-label>
</ion-tab-button>
</ion-tab-bar>
</ion-tabs>
</ion-page>
</template>
</ion-tabs>
</template>
<script>
import { defineComponent } from 'vue';
import {
IonIcon,
IonLabel,
IonPage,
IonTabBar,
IonTabButton,
IonTabs
} from '@ionic/vue';
import { calendar, personCircle } from 'ionicons/icons';
export default defineComponent({
components: { IonIcon, IonLabel, IonPage, IonTabBar, IonTabButton, IonTabs },
setup() {
const beforeTabChange = () => {
// do something before tab change
}
const afterTabChange = () => {
// do something after tab change
}
return {
calendar,
personCircle,
beforeTabChange,
afterTabChange
}
}
});
</script>
```
**Schedule.vue**
```html
<template>
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Schedule</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">Schedule Tab</ion-content>
</ion-page>
</template>
<script>
import { defineComponent } from 'vue';
import {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar
} from '@ionic/vue';
export default defineComponent({
components: { IonContent, IonHeader, IonPage, IonTitle, IonToolbar }
});
</script>
```
**Speakers.vue**
```html
<template>
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Speakers</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">Speakers Tab</ion-content>
</ion-page>
</template>
<script>
import { defineComponent } from 'vue';
import {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar
} from '@ionic/vue';
export default defineComponent({
components: { IonContent, IonHeader, IonPage, IonTitle, IonToolbar }
});
</script>
```

View File

@@ -253,11 +253,11 @@ export class TextareaExample {
</template>
<script>
import { IonItem, IonLabel, IonTextarea } from '@ionic/vue';
import { IonItem, IonLabe, IonTextarea } from '@ionic/vue';
import { defineComponent } from 'vue';
export default defineComponent({
components: { IonItem, IonLabel, IonTextarea }
components: { IonItem, IonLabe, IonTextarea }
});
</script>
```

View File

@@ -2,7 +2,7 @@ import { Build, Component, ComponentInterface, Element, Event, EventEmitter, Hos
import { getIonMode } from '../../global/ionic-global';
import { Color, StyleEventDetail, TextareaChangeEventDetail } from '../../interface';
import { debounceEvent, findItemLabel, inheritAttributes, raf } from '../../utils/helpers';
import { debounceEvent, findItemLabel, raf } from '../../utils/helpers';
import { createColorClasses } from '../../utils/theme';
/**
@@ -22,7 +22,6 @@ export class Textarea implements ComponentInterface {
private inputId = `ion-textarea-${textareaIds++}`;
private didBlurAfterEdit = false;
private textareaWrapper?: HTMLElement;
private inheritedAttributes: { [k: string]: any } = {};
/**
* This is required for a WebKit bug which requires us to
@@ -213,10 +212,6 @@ export class Textarea implements ComponentInterface {
}
}
componentWillLoad() {
this.inheritedAttributes = inheritAttributes(this.el, ['title']);
}
componentDidLoad() {
raf(() => this.runAutoGrow());
}
@@ -384,7 +379,6 @@ export class Textarea implements ComponentInterface {
onBlur={this.onBlur}
onFocus={this.onFocus}
onKeyDown={this.onKeyDown}
{...this.inheritedAttributes}
>
{value}
</textarea>

View File

@@ -38,11 +38,11 @@
</template>
<script>
import { IonItem, IonLabel, IonTextarea } from '@ionic/vue';
import { IonItem, IonLabe, IonTextarea } from '@ionic/vue';
import { defineComponent } from 'vue';
export default defineComponent({
components: { IonItem, IonLabel, IonTextarea }
components: { IonItem, IonLabe, IonTextarea }
});
</script>
```

View File

@@ -18,7 +18,7 @@
<ion-toolbar>
<ion-title>Toggle - Basic</ion-title>
<ion-buttons slot="primary">
<ion-toggle aria-label="Toggle"></ion-toggle>
<ion-toggle></ion-toggle>
</ion-buttons>
</ion-toolbar>
</ion-header>
@@ -86,7 +86,7 @@
</p>
<p>
<ion-toggle aria-label="Stand-alone toggle" id="standAloneChecked"></ion-toggle>
<ion-toggle id="standAloneChecked"></ion-toggle>
Stand-alone toggle:
<span id="standAloneCheckedSpan"></span>
</p>

View File

@@ -25,53 +25,53 @@
<ion-content class="ion-padding-horizontal">
<h1>Default</h1>
<ion-toggle aria-label="Default"></ion-toggle>
<ion-toggle aria-label="Default" checked></ion-toggle>
<ion-toggle aria-label="Default Danger" color="danger"></ion-toggle>
<ion-toggle aria-label="Default Danger" color="danger" checked></ion-toggle>
<ion-toggle aria-label="Default Tertiary" color="tertiary" class="toggle-activated"></ion-toggle>
<ion-toggle aria-label="Default Tertiary Activated" color="tertiary" checked class="toggle-activated"></ion-toggle>
<ion-toggle></ion-toggle>
<ion-toggle checked></ion-toggle>
<ion-toggle color="danger"></ion-toggle>
<ion-toggle color="danger" checked></ion-toggle>
<ion-toggle color="tertiary" class="toggle-activated"></ion-toggle>
<ion-toggle color="tertiary" checked class="toggle-activated"></ion-toggle>
<h1>Custom Widths</h1>
<ion-toggle aria-label="Secondary Small Width" color="secondary" class="width-small"></ion-toggle>
<ion-toggle aria-label="Secondary Small Width" color="secondary" checked class="width-small"></ion-toggle>
<ion-toggle aria-label="Secondary Large Width" color="secondary" class="width-large"></ion-toggle>
<ion-toggle aria-label="Secondary Large Width" color="secondary" checked class="width-large"></ion-toggle>
<ion-toggle aria-label="Tertiary Large Width Activated" color="tertiary" class="width-large toggle-activated"></ion-toggle>
<ion-toggle aria-label="Tertiary Large Width Activated" color="tertiary" checked class="width-large toggle-activated"></ion-toggle>
<ion-toggle color="secondary" class="width-small"></ion-toggle>
<ion-toggle color="secondary" checked class="width-small"></ion-toggle>
<ion-toggle color="secondary" class="width-large"></ion-toggle>
<ion-toggle color="secondary" checked class="width-large"></ion-toggle>
<ion-toggle color="tertiary" class="width-large toggle-activated"></ion-toggle>
<ion-toggle color="tertiary" checked class="width-large toggle-activated"></ion-toggle>
<h1>Custom Heights</h1>
<div style="display: flex; flex-flow: column; float: left;">
<ion-toggle aria-label="Small Height" class="height-small"></ion-toggle>
<ion-toggle aria-label="Small Height" checked class="height-small"></ion-toggle>
<ion-toggle class="height-small"></ion-toggle>
<ion-toggle checked class="height-small"></ion-toggle>
</div>
<ion-toggle aria-label="Large Height" class="height-large"></ion-toggle>
<ion-toggle aria-label="Large Height" checked class="height-large"></ion-toggle>
<ion-toggle aria-label="Large Height" class="handle-height-large"></ion-toggle>
<ion-toggle aria-label="Large Height" checked class="handle-height-large"></ion-toggle>
<ion-toggle aria-label="Large Height Activated" checked class="handle-height-large toggle-activated"></ion-toggle>
<ion-toggle class="height-large"></ion-toggle>
<ion-toggle checked class="height-large"></ion-toggle>
<ion-toggle class="handle-height-large"></ion-toggle>
<ion-toggle checked class="handle-height-large"></ion-toggle>
<ion-toggle checked class="handle-height-large toggle-activated"></ion-toggle>
<h1>Dynamic Sizes</h1>
<ion-toggle aria-label="Tertiary Small Width Small Height" color="tertiary" class="dynamic-small width-small height-small"></ion-toggle>
<ion-toggle aria-label="Tertiary Small Width Small Height" color="tertiary" checked class="dynamic-small width-small height-small"></ion-toggle>
<ion-toggle aria-label="Tertiary Large Width Large Height" color="tertiary" class="dynamic-large width-large height-large"></ion-toggle>
<ion-toggle aria-label="Tertiary Large Width Large Height" color="tertiary" checked class="dynamic-large width-large height-large"></ion-toggle>
<ion-toggle color="tertiary" class="dynamic-small width-small height-small"></ion-toggle>
<ion-toggle color="tertiary" checked class="dynamic-small width-small height-small"></ion-toggle>
<ion-toggle color="tertiary" class="dynamic-large width-large height-large"></ion-toggle>
<ion-toggle color="tertiary" checked class="dynamic-large width-large height-large"></ion-toggle>
<h1>Complex Custom Toggles</h1>
<ion-toggle aria-label="Custom" mode="ios" class="all-custom"></ion-toggle>
<ion-toggle aria-label="Custom" mode="ios" checked class="all-custom"></ion-toggle>
<ion-toggle mode="ios" class="all-custom"></ion-toggle>
<ion-toggle mode="ios" checked class="all-custom"></ion-toggle>
<ion-toggle aria-label="Custom Overflow" class="custom-overflow"></ion-toggle>
<ion-toggle aria-label="Custom Overflow" checked class="custom-overflow"></ion-toggle>
<ion-toggle class="custom-overflow"></ion-toggle>
<ion-toggle checked class="custom-overflow"></ion-toggle>
<ion-toggle aria-label="Custom Spacing iOS" mode="ios" color="dark" class="custom-spacing"></ion-toggle>
<ion-toggle aria-label="Custom Spacing iOS" mode="ios" color="dark" checked class="custom-spacing"></ion-toggle>
<ion-toggle mode="ios" color="dark" class="custom-spacing"></ion-toggle>
<ion-toggle mode="ios" color="dark" checked class="custom-spacing"></ion-toggle>
<ion-toggle aria-label="Custom Spacing MD" mode="md" color="dark" class="custom-spacing"></ion-toggle>
<ion-toggle aria-label="Custom Spacing MD" mode="md" color="dark" checked class="custom-spacing"></ion-toggle>
<ion-toggle mode="md" color="dark" class="custom-spacing"></ion-toggle>
<ion-toggle mode="md" color="dark" checked class="custom-spacing"></ion-toggle>
<ion-toggle aria-label="Custom Icon iOS" mode="ios" class="icon-custom"></ion-toggle>
<ion-toggle aria-label="Custom Icon iOS" mode="ios" checked class="icon-custom"></ion-toggle>
<ion-toggle mode="ios" class="icon-custom"></ion-toggle>
<ion-toggle mode="ios" checked class="icon-custom"></ion-toggle>
</ion-content>
</ion-app>

View File

@@ -13,23 +13,23 @@
<body>
<!-- Default -->
<ion-toggle aria-label="Default Toggle"></ion-toggle>
<ion-toggle></ion-toggle>
<!-- Colors -->
<ion-toggle aria-label="Primary Toggle" checked color="primary"></ion-toggle>
<ion-toggle aria-label="Secondary Toggle" checked color="secondary"></ion-toggle>
<ion-toggle aria-label="Tertiary Toggle" checked color="tertiary"></ion-toggle>
<ion-toggle aria-label="Success Toggle" checked color="success"></ion-toggle>
<ion-toggle aria-label="Warning Toggle" checked color="warning"></ion-toggle>
<ion-toggle aria-label="Danger Toggle" checked color="danger"></ion-toggle>
<ion-toggle aria-label="Light Toggle" checked color="light"></ion-toggle>
<ion-toggle aria-label="Medium Toggle" checked color="medium"></ion-toggle>
<ion-toggle aria-label="Dark Toggle" checked color="dark"></ion-toggle>
<ion-toggle aria-label="Custom Toggle" checked class="custom"></ion-toggle>
<ion-toggle checked color="primary"></ion-toggle>
<ion-toggle checked color="secondary"></ion-toggle>
<ion-toggle checked color="tertiary"></ion-toggle>
<ion-toggle checked color="success"></ion-toggle>
<ion-toggle checked color="warning"></ion-toggle>
<ion-toggle checked color="danger"></ion-toggle>
<ion-toggle checked color="light"></ion-toggle>
<ion-toggle checked color="medium"></ion-toggle>
<ion-toggle checked color="dark"></ion-toggle>
<ion-toggle checked class="custom"></ion-toggle>
<!-- Disabled -->
<ion-toggle aria-label="Disabled Default Toggle" checked disabled></ion-toggle>
<ion-toggle aria-label="Disabled Secondary Toggle" checked disabled color="secondary"></ion-toggle>
<ion-toggle checked disabled></ion-toggle>
<ion-toggle checked disabled color="secondary"></ion-toggle>
<style>
.custom {

View File

@@ -45,18 +45,8 @@
pointer-events: none;
}
label {
button {
@include input-cover();
display: flex;
align-items: center;
opacity: 0;
}
input {
@include visually-hidden();
}
// Toggle Background Track: Unchecked

View File

@@ -2,7 +2,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Host, Prop
import { getIonMode } from '../../global/ionic-global';
import { Color, Gesture, GestureDetail, StyleEventDetail, ToggleChangeEventDetail } from '../../interface';
import { getAriaLabel, renderHiddenInput } from '../../utils/helpers';
import { findItemLabel, renderHiddenInput } from '../../utils/helpers';
import { hapticSelection } from '../../utils/native/haptic';
import { createColorClasses, hostContext } from '../../utils/theme';
@@ -24,7 +24,7 @@ export class Toggle implements ComponentInterface {
private inputId = `ion-tg-${toggleIds++}`;
private gesture?: Gesture;
private focusEl?: HTMLElement;
private buttonEl?: HTMLElement;
private lastDrag = 0;
@Element() el!: HTMLElement;
@@ -156,15 +156,12 @@ export class Toggle implements ComponentInterface {
}
private setFocus() {
if (this.focusEl) {
this.focusEl.focus();
if (this.buttonEl) {
this.buttonEl.focus();
}
}
private onClick = (ev: Event) => {
ev.preventDefault();
ev.stopPropagation();
private onClick = () => {
if (this.lastDrag + 300 < Date.now()) {
this.checked = !this.checked;
}
@@ -179,20 +176,23 @@ export class Toggle implements ComponentInterface {
}
render() {
const { activated, color, checked, disabled, el, inputId, name } = this;
const { inputId, disabled, checked, activated, color, el } = this;
const mode = getIonMode(this);
const { label, labelId, labelText } = getAriaLabel(el, inputId);
const labelId = inputId + '-lbl';
const label = findItemLabel(el);
const value = this.getValue();
renderHiddenInput(true, el, name, (checked ? value : ''), disabled);
if (label) {
label.id = labelId;
}
renderHiddenInput(true, el, this.name, (checked ? value : ''), disabled);
return (
<Host
onClick={this.onClick}
aria-labelledby={label ? labelId : null}
role="checkbox"
aria-disabled={disabled ? 'true' : null}
aria-checked={`${checked}`}
aria-hidden={disabled ? 'true' : null}
role="switch"
aria-labelledby={labelId}
class={createColorClasses(color, {
[mode]: true,
'in-item': hostContext('ion-item', el),
@@ -207,19 +207,15 @@ export class Toggle implements ComponentInterface {
<div class="toggle-inner" part="handle" />
</div>
</div>
<label htmlFor={inputId}>
{labelText}
</label>
<input
type="checkbox"
role="switch"
aria-checked={`${checked}`}
<button
type="button"
onFocus={this.onFocus}
onBlur={this.onBlur}
disabled={disabled}
id={inputId}
onFocus={() => this.onFocus()}
onBlur={() => this.onBlur()}
ref={focusEl => this.focusEl = focusEl}
/>
ref={btnEl => this.buttonEl = btnEl}
aria-hidden="true"
>
</button>
</Host>
);
}

View File

@@ -21,31 +21,6 @@
}
}
@mixin visually-hidden() {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
border: 0;
outline: 0;
clip: rect(0 0 0 0);
opacity: 0;
overflow: hidden;
-webkit-appearance: none;
-moz-appearance: none;
}
@mixin text-inherit() {
font-family: inherit;
font-size: inherit;
@@ -534,4 +509,4 @@
transform: $rtl-translate $extra;
}
}
}
}

View File

@@ -5,32 +5,6 @@ import { Side } from '../interface';
declare const __zone_symbol__requestAnimationFrame: any;
declare const requestAnimationFrame: any;
/**
* Elements inside of web components sometimes need to inherit global attributes
* set on the host. For example, the inner input in `ion-input` should inherit
* the `title` attribute that developers set directly on `ion-input`. This
* helper function should be called in componentWillLoad and assigned to a variable
* that is later used in the render function.
*
* This does not need to be reactive as changing attributes on the host element
* does not trigger a re-render.
*/
export const inheritAttributes = (el: HTMLElement, attributes: string[] = []) => {
const attributeObject: { [k: string]: any } = {};
attributes.forEach(attr => {
if (el.hasAttribute(attr)) {
const value = el.getAttribute(attr);
if (value !== null) {
attributeObject[attr] = el.getAttribute(attr);
}
el.removeAttribute(attr);
}
});
return attributeObject;
}
export const addEventListener = (el: any, eventName: string, callback: any, opts?: any) => {
if (typeof (window as any) !== 'undefined') {
const win = window as any;
@@ -96,7 +70,7 @@ export const hasShadowDom = (el: HTMLElement) => {
return !!el.shadowRoot && !!(el as any).attachShadow;
};
export const findItemLabel = (componentEl: HTMLElement): HTMLIonLabelElement | null => {
export const findItemLabel = (componentEl: HTMLElement) => {
const itemEl = componentEl.closest('ion-item');
if (itemEl) {
return itemEl.querySelector('ion-label');
@@ -104,58 +78,6 @@ export const findItemLabel = (componentEl: HTMLElement): HTMLIonLabelElement | n
return null;
};
/**
* This method is used for Ionic's input components that use Shadow DOM. In
* order to properly label the inputs to work with screen readers, we need
* to get the text content of the label outside of the shadow root and pass
* it to the input inside of the shadow root.
*
* Referencing label elements by id from outside of the component is
* impossible due to the shadow boundary, read more here:
* https://developer.salesforce.com/blogs/2020/01/accessibility-for-web-components.html
*
* @param componentEl The shadow element that needs the aria label
* @param inputId The unique identifier for the input
*/
export const getAriaLabel = (componentEl: HTMLElement, inputId: string): { label: Element | null, labelId: string, labelText: string | null | undefined } => {
let labelText;
// If the user provides their own label via the aria-labelledby attr
// we should use that instead of looking for an ion-label
const labelledBy = componentEl.getAttribute('aria-labelledby');
const labelId = labelledBy !== null
? labelledBy
: inputId + '-lbl';
const label = labelledBy !== null
? document.querySelector(`#${labelledBy}`)
: findItemLabel(componentEl);
if (label) {
if (labelledBy === null) {
label.id = labelId;
}
labelText = label.textContent;
label.setAttribute('aria-hidden', 'true');
}
return { label, labelId, labelText };
};
/**
* This method is used to add a hidden input to a host element that contains
* a Shadow DOM. It does not add the input inside of the Shadow root which
* allows it to be picked up inside of forms. It should contain the same
* values as the host element.
*
* @param always Add a hidden input even if the container does not use Shadow
* @param container The element where the input will be added
* @param name The name of the input
* @param value The value of the input
* @param disabled If true, the input is disabled
*/
export const renderHiddenInput = (always: boolean, container: HTMLElement, name: string, value: string | undefined | null, disabled: boolean) => {
if (always || hasShadowDom(container)) {
let input = container.querySelector('input.aux-input') as HTMLInputElement | null;

View File

@@ -1,39 +0,0 @@
import { inheritAttributes } from '../helpers';
describe('inheritAttributes()', () => {
it('should create an attribute inheritance object', () => {
const el = document.createElement('div');
el.setAttribute('tabindex', '20');
el.setAttribute('title', 'myTitle');
const attributeObject = inheritAttributes(el, ['tabindex', 'title']);
expect(attributeObject).toEqual({
tabindex: '20',
title: 'myTitle'
});
});
it('should not inherit attributes that are not defined on the element', () => {
const el = document.createElement('div');
el.setAttribute('tabindex', '20');
const attributeObject = inheritAttributes(el, ['tabindex', 'title']);
expect(attributeObject).toEqual({
tabindex: '20'
});
});
it('should not inherit attributes that are not defined on the input array', () => {
const el = document.createElement('div');
el.setAttribute('tabindex', '20');
el.setAttribute('title', 'myTitle');
const attributeObject = inheritAttributes(el, ['title']);
expect(attributeObject).toEqual({
title: 'myTitle'
});
});
});

View File

@@ -171,7 +171,6 @@ export const config: Config = {
]
}
],
buildEs5: 'prod',
extras: {
cssVarsShim: true,
dynamicImportShim: true,

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/docs",
"version": "5.5.0",
"version": "5.4.3",
"description": "Pre-packaged API documentation for the Ionic docs.",
"main": "core.json",
"types": "core.d.ts",

View File

@@ -1,12 +1,12 @@
{
"name": "@ionic/angular-server",
"version": "5.5.0",
"version": "5.4.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/angular-server",
"version": "5.5.0",
"version": "5.4.3",
"license": "MIT",
"devDependencies": {
"@angular/animations": "8.2.13",
@@ -32,7 +32,7 @@
}
},
"../../core": {
"version": "5.4.4",
"version": "5.4.2",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/angular-server",
"version": "5.5.0",
"version": "5.4.3",
"description": "Angular SSR Module for Ionic",
"keywords": [
"ionic",
@@ -49,7 +49,7 @@
"@angular/core": "8.2.13",
"@angular/platform-browser": "8.2.13",
"@angular/platform-server": "8.2.13",
"@ionic/core": "5.5.0",
"@ionic/core": "5.4.3",
"ng-packagr": "5.7.1",
"tslint": "^5.12.1",
"tslint-ionic-rules": "0.0.21",

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/react-router",
"version": "5.5.0",
"version": "5.4.3",
"description": "React Router wrapper for @ionic/react",
"keywords": [
"ionic",
@@ -39,16 +39,16 @@
"tslib": "*"
},
"peerDependencies": {
"@ionic/core": "5.5.0",
"@ionic/react": "5.5.0",
"@ionic/core": "5.4.3",
"@ionic/react": "5.4.3",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-router": "^5.0.1",
"react-router-dom": "^5.0.1"
},
"devDependencies": {
"@ionic/core": "5.5.0",
"@ionic/react": "5.5.0",
"@ionic/core": "5.4.3",
"@ionic/react": "5.4.3",
"@rollup/plugin-node-resolve": "^8.1.0",
"@testing-library/jest-dom": "^5.11.0",
"@testing-library/react": "^10.4.9",

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/react",
"version": "5.5.0",
"version": "5.4.3",
"description": "React specific wrapper for @ionic/core",
"keywords": [
"ionic",
@@ -39,7 +39,7 @@
"css/"
],
"dependencies": {
"@ionic/core": "5.5.0",
"@ionic/core": "5.4.3",
"ionicons": "^5.1.2",
"tslib": "*"
},

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/vue-router",
"version": "5.5.0",
"version": "5.4.3",
"description": "Vue Router integration for @ionic/vue",
"scripts": {
"test.spec": "jest",
@@ -45,12 +45,12 @@
"@ionic/vue": "5.4.1",
"@types/jest": "^26.0.13",
"@types/node": "^14.10.1",
"jest": "^26.6.1",
"jest": "^26.4.2",
"rimraf": "^3.0.2",
"rollup": "^2.32.1",
"rollup": "^2.29.0",
"rollup-plugin-terser": "^7.0.2",
"ts-jest": "^26.4.3",
"typescript": "^4.0.5",
"ts-jest": "^26.3.0",
"typescript": "^3.9.7",
"vue": "^3.0.0",
"vue-router": "^4.0.0-beta.11"
},

View File

@@ -42,7 +42,6 @@ export interface ViewItem {
exact: boolean;
registerCallback?: () => void;
vueComponentRef: Ref;
params?: { [k: string]: any };
}
export interface ViewStacks {

View File

@@ -88,8 +88,7 @@ export const createViewStacks = () => {
vueComponentRef: shallowRef(),
ionRoute: false,
mount: false,
exact: routeInfo.pathname === matchedRoute.path,
params: routeInfo.params
exact: routeInfo.pathname === matchedRoute.path
};
}

View File

@@ -1,12 +1,12 @@
{
"name": "@ionic/vue",
"version": "5.5.0",
"version": "5.4.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/vue",
"version": "5.5.0",
"version": "5.4.3",
"license": "MIT",
"dependencies": {
"@ionic/core": "file:../../core",
@@ -14,17 +14,16 @@
},
"devDependencies": {
"@stencil/core": "^1.17.0",
"change-case": "^4.1.1",
"rimraf": "^3.0.2",
"rollup": "^2.32.1",
"rollup": "^2.28.1",
"rollup-plugin-terser": "^7.0.2",
"typescript": "^4.0.5",
"typescript": "^3.9.7",
"vue": "^3.0.0",
"vue-router": "^4.0.0-beta.11"
}
},
"../../core": {
"version": "5.4.4",
"version": "5.4.2",
"license": "MIT",
"dependencies": {
"ionicons": "^5.1.2",
@@ -241,27 +240,6 @@
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"dev": true
},
"node_modules/camel-case": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.1.tgz",
"integrity": "sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q==",
"dev": true,
"dependencies": {
"pascal-case": "^3.1.1",
"tslib": "^1.10.0"
}
},
"node_modules/capital-case": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.3.tgz",
"integrity": "sha512-OlUSJpUr7SY0uZFOxcwnDOU7/MpHlKTZx2mqnDYQFrDudXLFm0JJ9wr/l4csB+rh2Ug0OPuoSO53PqiZBqno9A==",
"dev": true,
"dependencies": {
"no-case": "^3.0.3",
"tslib": "^1.10.0",
"upper-case-first": "^2.0.1"
}
},
"node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -276,26 +254,6 @@
"node": ">=4"
}
},
"node_modules/change-case": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.1.tgz",
"integrity": "sha512-qRlUWn/hXnX1R1LBDF/RelJLiqNjKjUqlmuBVSEIyye8kq49CXqkZWKmi8XeUAdDXWFOcGLUMZ+aHn3Q5lzUXw==",
"dev": true,
"dependencies": {
"camel-case": "^4.1.1",
"capital-case": "^1.0.3",
"constant-case": "^3.0.3",
"dot-case": "^3.0.3",
"header-case": "^2.0.3",
"no-case": "^3.0.3",
"param-case": "^3.0.3",
"pascal-case": "^3.1.1",
"path-case": "^3.0.3",
"sentence-case": "^3.0.3",
"snake-case": "^3.0.3",
"tslib": "^1.10.0"
}
},
"node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -323,33 +281,12 @@
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
"node_modules/constant-case": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.3.tgz",
"integrity": "sha512-FXtsSnnrFYpzDmvwDGQW+l8XK3GV1coLyBN0eBz16ZUzGaZcT2ANVCJmLeuw2GQgxKHQIe9e0w2dzkSfaRlUmA==",
"dev": true,
"dependencies": {
"no-case": "^3.0.3",
"tslib": "^1.10.0",
"upper-case": "^2.0.1"
}
},
"node_modules/csstype": {
"version": "2.6.13",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.13.tgz",
"integrity": "sha512-ul26pfSQTZW8dcOnD2iiJssfXw0gdNVX9IJDH/X3K5DGPfj+fUYe3kB+swUY6BF3oZDxaID3AJt+9/ojSAE05A==",
"dev": true
},
"node_modules/dot-case": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.3.tgz",
"integrity": "sha512-7hwEmg6RiSQfm/GwPL4AAWXKy3YNNZA3oFv2Pdiey0mwkRCPZ9x6SZbkLcn8Ma5PYeVokzoD4Twv2n7LKp5WeA==",
"dev": true,
"dependencies": {
"no-case": "^3.0.3",
"tslib": "^1.10.0"
}
},
"node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@@ -410,16 +347,6 @@
"node": ">=4"
}
},
"node_modules/header-case": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.3.tgz",
"integrity": "sha512-LChe/V32mnUQnTwTxd3aAlNMk8ia9tjCDb/LjYtoMrdAPApxLB+azejUk5ERZIZdIqvinwv6BAUuFXH/tQPdZA==",
"dev": true,
"dependencies": {
"capital-case": "^1.0.3",
"tslib": "^1.10.0"
}
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -488,15 +415,6 @@
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
"dev": true
},
"node_modules/lower-case": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz",
"integrity": "sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ==",
"dev": true,
"dependencies": {
"tslib": "^1.10.0"
}
},
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -515,16 +433,6 @@
"node": "*"
}
},
"node_modules/no-case": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.3.tgz",
"integrity": "sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw==",
"dev": true,
"dependencies": {
"lower-case": "^2.0.1",
"tslib": "^1.10.0"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -534,36 +442,6 @@
"wrappy": "1"
}
},
"node_modules/param-case": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.3.tgz",
"integrity": "sha512-VWBVyimc1+QrzappRs7waeN2YmoZFCGXWASRYX1/rGHtXqEcrGEIDm+jqIwFa2fRXNgQEwrxaYuIrX0WcAguTA==",
"dev": true,
"dependencies": {
"dot-case": "^3.0.3",
"tslib": "^1.10.0"
}
},
"node_modules/pascal-case": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.1.tgz",
"integrity": "sha512-XIeHKqIrsquVTQL2crjq3NfJUxmdLasn3TYOU0VBM+UX2a6ztAWBlJQBePLGY7VHW8+2dRadeIPK5+KImwTxQA==",
"dev": true,
"dependencies": {
"no-case": "^3.0.3",
"tslib": "^1.10.0"
}
},
"node_modules/path-case": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.3.tgz",
"integrity": "sha512-UMFU6UETFpCNWbIWNczshPrnK/7JAXBP2NYw80ojElbQ2+JYxdqWDBkvvqM93u4u6oLmuJ/tPOf2tM8KtXv4eg==",
"dev": true,
"dependencies": {
"dot-case": "^3.0.3",
"tslib": "^1.10.0"
}
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -595,9 +473,9 @@
}
},
"node_modules/rollup": {
"version": "2.33.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.33.1.tgz",
"integrity": "sha512-uY4O/IoL9oNW8MMcbA5hcOaz6tZTMIh7qJHx/tzIJm+n1wLoY38BLn6fuy7DhR57oNFLMbDQtDeJoFURt5933w==",
"version": "2.28.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.28.1.tgz",
"integrity": "sha512-DOtVoqOZt3+FjPJWLU8hDIvBjUylc9s6IZvy76XklxzcLvAQLtVAG/bbhsMhcWnYxC0TKKcf1QQ/tg29zeID0Q==",
"dev": true,
"dependencies": {
"fsevents": "~2.1.2"
@@ -630,17 +508,6 @@
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true
},
"node_modules/sentence-case": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.3.tgz",
"integrity": "sha512-ZPr4dgTcNkEfcGOMFQyDdJrTU9uQO1nb1cjf+nuzb6FxgMDgKddZOM29qEsB7jvsZSMruLRcL2KfM4ypKpa0LA==",
"dev": true,
"dependencies": {
"no-case": "^3.0.3",
"tslib": "^1.10.0",
"upper-case-first": "^2.0.1"
}
},
"node_modules/serialize-javascript": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
@@ -650,16 +517,6 @@
"randombytes": "^2.1.0"
}
},
"node_modules/snake-case": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.3.tgz",
"integrity": "sha512-WM1sIXEO+rsAHBKjGf/6R1HBBcgbncKS08d2Aqec/mrDSpU80SiOU41hO7ny6DToHSyrlwTYzQBIK1FPSx4Y3Q==",
"dev": true,
"dependencies": {
"dot-case": "^3.0.3",
"tslib": "^1.10.0"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -717,16 +574,10 @@
"node": ">=4"
}
},
"node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
},
"node_modules/typescript": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz",
"integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==",
"version": "3.9.7",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz",
"integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
@@ -736,24 +587,6 @@
"node": ">=4.2.0"
}
},
"node_modules/upper-case": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.1.tgz",
"integrity": "sha512-laAsbea9SY5osxrv7S99vH9xAaJKrw5Qpdh4ENRLcaxipjKsiaBwiAsxfa8X5mObKNTQPsupSq0J/VIxsSJe3A==",
"dev": true,
"dependencies": {
"tslib": "^1.10.0"
}
},
"node_modules/upper-case-first": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.1.tgz",
"integrity": "sha512-105J8XqQ+9RxW3l9gHZtgve5oaiR9TIwvmZAMAIZWRHe00T21cdvewKORTlOJf/zXW6VukuTshM+HXZNWz7N5w==",
"dev": true,
"dependencies": {
"tslib": "^1.10.0"
}
},
"node_modules/vue": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.0.0.tgz",
@@ -969,27 +802,6 @@
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"dev": true
},
"camel-case": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.1.tgz",
"integrity": "sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q==",
"dev": true,
"requires": {
"pascal-case": "^3.1.1",
"tslib": "^1.10.0"
}
},
"capital-case": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.3.tgz",
"integrity": "sha512-OlUSJpUr7SY0uZFOxcwnDOU7/MpHlKTZx2mqnDYQFrDudXLFm0JJ9wr/l4csB+rh2Ug0OPuoSO53PqiZBqno9A==",
"dev": true,
"requires": {
"no-case": "^3.0.3",
"tslib": "^1.10.0",
"upper-case-first": "^2.0.1"
}
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -1001,26 +813,6 @@
"supports-color": "^5.3.0"
}
},
"change-case": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.1.tgz",
"integrity": "sha512-qRlUWn/hXnX1R1LBDF/RelJLiqNjKjUqlmuBVSEIyye8kq49CXqkZWKmi8XeUAdDXWFOcGLUMZ+aHn3Q5lzUXw==",
"dev": true,
"requires": {
"camel-case": "^4.1.1",
"capital-case": "^1.0.3",
"constant-case": "^3.0.3",
"dot-case": "^3.0.3",
"header-case": "^2.0.3",
"no-case": "^3.0.3",
"param-case": "^3.0.3",
"pascal-case": "^3.1.1",
"path-case": "^3.0.3",
"sentence-case": "^3.0.3",
"snake-case": "^3.0.3",
"tslib": "^1.10.0"
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -1048,33 +840,12 @@
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
"constant-case": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.3.tgz",
"integrity": "sha512-FXtsSnnrFYpzDmvwDGQW+l8XK3GV1coLyBN0eBz16ZUzGaZcT2ANVCJmLeuw2GQgxKHQIe9e0w2dzkSfaRlUmA==",
"dev": true,
"requires": {
"no-case": "^3.0.3",
"tslib": "^1.10.0",
"upper-case": "^2.0.1"
}
},
"csstype": {
"version": "2.6.13",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.13.tgz",
"integrity": "sha512-ul26pfSQTZW8dcOnD2iiJssfXw0gdNVX9IJDH/X3K5DGPfj+fUYe3kB+swUY6BF3oZDxaID3AJt+9/ojSAE05A==",
"dev": true
},
"dot-case": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.3.tgz",
"integrity": "sha512-7hwEmg6RiSQfm/GwPL4AAWXKy3YNNZA3oFv2Pdiey0mwkRCPZ9x6SZbkLcn8Ma5PYeVokzoD4Twv2n7LKp5WeA==",
"dev": true,
"requires": {
"no-case": "^3.0.3",
"tslib": "^1.10.0"
}
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@@ -1120,16 +891,6 @@
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"header-case": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.3.tgz",
"integrity": "sha512-LChe/V32mnUQnTwTxd3aAlNMk8ia9tjCDb/LjYtoMrdAPApxLB+azejUk5ERZIZdIqvinwv6BAUuFXH/tQPdZA==",
"dev": true,
"requires": {
"capital-case": "^1.0.3",
"tslib": "^1.10.0"
}
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -1191,15 +952,6 @@
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
"dev": true
},
"lower-case": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz",
"integrity": "sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ==",
"dev": true,
"requires": {
"tslib": "^1.10.0"
}
},
"merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -1215,16 +967,6 @@
"brace-expansion": "^1.1.7"
}
},
"no-case": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.3.tgz",
"integrity": "sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw==",
"dev": true,
"requires": {
"lower-case": "^2.0.1",
"tslib": "^1.10.0"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -1234,36 +976,6 @@
"wrappy": "1"
}
},
"param-case": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.3.tgz",
"integrity": "sha512-VWBVyimc1+QrzappRs7waeN2YmoZFCGXWASRYX1/rGHtXqEcrGEIDm+jqIwFa2fRXNgQEwrxaYuIrX0WcAguTA==",
"dev": true,
"requires": {
"dot-case": "^3.0.3",
"tslib": "^1.10.0"
}
},
"pascal-case": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.1.tgz",
"integrity": "sha512-XIeHKqIrsquVTQL2crjq3NfJUxmdLasn3TYOU0VBM+UX2a6ztAWBlJQBePLGY7VHW8+2dRadeIPK5+KImwTxQA==",
"dev": true,
"requires": {
"no-case": "^3.0.3",
"tslib": "^1.10.0"
}
},
"path-case": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.3.tgz",
"integrity": "sha512-UMFU6UETFpCNWbIWNczshPrnK/7JAXBP2NYw80ojElbQ2+JYxdqWDBkvvqM93u4u6oLmuJ/tPOf2tM8KtXv4eg==",
"dev": true,
"requires": {
"dot-case": "^3.0.3",
"tslib": "^1.10.0"
}
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -1289,9 +1001,9 @@
}
},
"rollup": {
"version": "2.33.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.33.1.tgz",
"integrity": "sha512-uY4O/IoL9oNW8MMcbA5hcOaz6tZTMIh7qJHx/tzIJm+n1wLoY38BLn6fuy7DhR57oNFLMbDQtDeJoFURt5933w==",
"version": "2.28.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.28.1.tgz",
"integrity": "sha512-DOtVoqOZt3+FjPJWLU8hDIvBjUylc9s6IZvy76XklxzcLvAQLtVAG/bbhsMhcWnYxC0TKKcf1QQ/tg29zeID0Q==",
"dev": true,
"requires": {
"fsevents": "~2.1.2"
@@ -1315,17 +1027,6 @@
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true
},
"sentence-case": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.3.tgz",
"integrity": "sha512-ZPr4dgTcNkEfcGOMFQyDdJrTU9uQO1nb1cjf+nuzb6FxgMDgKddZOM29qEsB7jvsZSMruLRcL2KfM4ypKpa0LA==",
"dev": true,
"requires": {
"no-case": "^3.0.3",
"tslib": "^1.10.0",
"upper-case-first": "^2.0.1"
}
},
"serialize-javascript": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
@@ -1335,16 +1036,6 @@
"randombytes": "^2.1.0"
}
},
"snake-case": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.3.tgz",
"integrity": "sha512-WM1sIXEO+rsAHBKjGf/6R1HBBcgbncKS08d2Aqec/mrDSpU80SiOU41hO7ny6DToHSyrlwTYzQBIK1FPSx4Y3Q==",
"dev": true,
"requires": {
"dot-case": "^3.0.3",
"tslib": "^1.10.0"
}
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -1387,36 +1078,12 @@
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
"dev": true
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
},
"typescript": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz",
"integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==",
"version": "3.9.7",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz",
"integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==",
"dev": true
},
"upper-case": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.1.tgz",
"integrity": "sha512-laAsbea9SY5osxrv7S99vH9xAaJKrw5Qpdh4ENRLcaxipjKsiaBwiAsxfa8X5mObKNTQPsupSq0J/VIxsSJe3A==",
"dev": true,
"requires": {
"tslib": "^1.10.0"
}
},
"upper-case-first": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.1.tgz",
"integrity": "sha512-105J8XqQ+9RxW3l9gHZtgve5oaiR9TIwvmZAMAIZWRHe00T21cdvewKORTlOJf/zXW6VukuTshM+HXZNWz7N5w==",
"dev": true,
"requires": {
"tslib": "^1.10.0"
}
},
"vue": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.0.0.tgz",

View File

@@ -1,17 +1,15 @@
{
"name": "@ionic/vue",
"version": "5.5.0",
"version": "5.4.3",
"description": "Vue specific wrapper for @ionic/core",
"scripts": {
"lint": "echo add linter",
"test": "jest",
"build": "npm run clean && npm run copy && npm run copy.overlays && npm run compile && npm run bundle && npm run build.vetur && npm run build.web-types",
"build": "npm run clean && npm run copy && npm run copy.overlays && npm run compile && npm run bundle",
"bundle": "rollup --config rollup.config.js",
"clean": "rimraf dist dist-transpiled",
"compile": "npm run tsc",
"tsc": "tsc -p .",
"build.web-types": "node ./scripts/build-web-types.js",
"build.vetur": "node ./scripts/build-vetur.js",
"copy": "node ./scripts/copy-css.js",
"copy.overlays": "node ./scripts/copy-overlays.js"
},
@@ -48,21 +46,15 @@
"homepage": "https://github.com/ionic-team/ionic#readme",
"devDependencies": {
"@stencil/core": "^1.17.0",
"change-case": "^4.1.1",
"rimraf": "^3.0.2",
"rollup": "^2.32.1",
"rollup": "^2.28.1",
"rollup-plugin-terser": "^7.0.2",
"typescript": "^4.0.5",
"typescript": "^3.9.7",
"vue": "^3.0.0",
"vue-router": "^4.0.0-beta.11"
},
"dependencies": {
"@ionic/core": "5.5.0",
"@ionic/core": "5.4.3",
"ionicons": "^5.1.2"
},
"vetur": {
"tags": "dist/vetur/tags.json",
"attributes": "dist/vetur/attributes.json"
},
"web-types": "dist/web-types.json"
}
}

View File

@@ -1,43 +0,0 @@
const DocsJson = require('@ionic/core/dist/docs.json');
const fs = require('fs');
const { paramCase } = require('change-case');
const generateTags = () => {
const tagsObject = {};
DocsJson.components.forEach(component => {
tagsObject[component.tag] = {
description: component.docs,
attributes: component.props.map(prop => paramCase(prop.name))
}
});
fs.writeFileSync('./dist/vetur/tags.json', JSON.stringify(tagsObject, null, 2));
}
const generateAttributes = () => {
const attributesObject = {};
DocsJson.components.forEach(component => {
component.props.forEach(prop => {
attributesObject[`${component.tag}/${paramCase(prop.name)}`] = {
type: prop.type,
description: prop.docs,
options: prop.values.filter(option => option.value !== undefined).map(option => option.value)
}
});
});
fs.writeFileSync('./dist/vetur/attributes.json', JSON.stringify(attributesObject, null, 2));
}
const main = async () => {
if (!fs.existsSync('./dist/vetur')) {
fs.mkdirSync('./dist/vetur');
}
generateTags();
generateAttributes();
}
main();

View File

@@ -1,78 +0,0 @@
const fs = require("fs")
const docs = require("@ionic/core/dist/docs.json")
const { pascalCase } = require('change-case')
const components = []
for (const component of docs.components) {
if (!component.usage.vue) continue
const attributes = []
const slots = []
const events = []
const componentName = pascalCase(component.tag)
const docUrl = "https://ionicframework.com/docs/api/" + component.tag.substr(4)
for (const prop of component.props || []) {
attributes.push({
name: prop.attr || prop.name,
description: prop.docs,
required: prop.required,
default: prop.default,
value: {
kind: "expression",
type: prop.type
}
})
}
for (const event of component.events || []) {
let eventName = event.event;
if (eventName.toLowerCase().startsWith(componentName.toLowerCase())) {
eventName = "on" + eventName.substr(componentName.length);
}
events.push({
name: eventName,
description: event.docs,
arguments: [{
name: "detail",
type: event.detail
}]
})
}
for (const slot of component.slots || []) {
slots.push({
name: slot.name === "" ? "default" : slot.name,
description: slot.docs
})
}
components.push({
name: componentName,
"doc-url": docUrl,
description: component.docs,
source: {
module: "@ionic/core/" + component.filePath.replace("./src/", "dist/types/").replace(".tsx", ".d.ts"),
symbol: componentName.substr(3)
},
attributes,
slots,
events
})
}
const webTypes = {
$schema: "http://json.schemastore.org/web-types",
framework: "vue",
name: "@ionic/vue",
version: require("../package.json").version,
contributions: {
html: {
"types-syntax": "typescript",
"description-markup": "markdown",
tags: components
}
}
}
fs.writeFileSync("dist/web-types.json", JSON.stringify(webTypes, null, 2))

View File

@@ -10,9 +10,9 @@ import {
InjectionKey,
onUnmounted
} from 'vue';
import { AnimationBuilder, LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_ENTER, LIFECYCLE_WILL_LEAVE } from '@ionic/core';
import { AnimationBuilder } from '@ionic/core';
import { useRoute } from 'vue-router';
import { fireLifecycle, generateId } from '../utils';
import { fireLifecycle, generateId, LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_ENTER, LIFECYCLE_WILL_LEAVE } from '../utils';
let viewDepthKey: InjectionKey<0> = Symbol(0);
export const IonRouterOutlet = defineComponent({
@@ -194,13 +194,13 @@ export const IonRouterOutlet = defineComponent({
if (enteringViewItem === leavingViewItem) return;
fireLifecycle(enteringViewItem.vueComponent, enteringViewItem.vueComponentRef, LIFECYCLE_WILL_ENTER);
fireLifecycle(enteringViewItem.vueComponentRef, LIFECYCLE_WILL_ENTER);
if (leavingViewItem) {
let animationBuilder = routerAnimation;
const leavingEl = leavingViewItem.ionPageElement;
fireLifecycle(leavingViewItem.vueComponent, leavingViewItem.vueComponentRef, LIFECYCLE_WILL_LEAVE);
fireLifecycle(leavingViewItem.vueComponentRef, LIFECYCLE_WILL_LEAVE);
/**
* If we are going back from a page that
@@ -242,7 +242,7 @@ export const IonRouterOutlet = defineComponent({
}
}
fireLifecycle(leavingViewItem.vueComponent, leavingViewItem.vueComponentRef, LIFECYCLE_DID_LEAVE);
fireLifecycle(leavingViewItem.vueComponentRef, LIFECYCLE_DID_LEAVE);
} else {
/**
* If there is no leaving element, just show
@@ -253,7 +253,7 @@ export const IonRouterOutlet = defineComponent({
requestAnimationFrame(() => enteringEl.classList.remove('ion-page-invisible'));
}
fireLifecycle(enteringViewItem.vueComponent, enteringViewItem.vueComponentRef, LIFECYCLE_DID_ENTER);
fireLifecycle(enteringViewItem.vueComponentRef, LIFECYCLE_DID_ENTER);
components.value = viewStacks.getChildrenToRender(id);
}
@@ -355,36 +355,15 @@ export const IonRouterOutlet = defineComponent({
{ ref: 'ionRouterOutlet' },
// TODO types
components && components.map((c: any) => {
let props = {
ref: c.vueComponentRef,
key: c.pathname,
isInOutlet: true,
registerIonPage: (ionPageEl: HTMLElement) => registerIonPage(c, ionPageEl)
}
/**
* IonRouterOutlet does not support named outlets.
*/
if (c.matchedRoute?.props?.default) {
const matchedRoute = c.matchedRoute;
const routePropsOption = matchedRoute.props.default;
const routeProps = routePropsOption
? routePropsOption === true
? c.params
: typeof routePropsOption === 'function'
? routePropsOption(matchedRoute)
: routePropsOption
: null
props = {
...props,
...routeProps
}
}
return h(
c.vueComponent,
props
);
{
ref: c.vueComponentRef,
key: c.pathname,
isInOutlet: true,
registerIonPage: (ionPageEl: HTMLElement) => registerIonPage(c, ionPageEl)
}
)
})
)
}

View File

@@ -27,8 +27,7 @@ export const IonTabBar = defineComponent({
* to a tab from another tab, we can correctly
* show any child pages if necessary.
*/
const children = (currentInstance.subTree.children || []) as VNode[];
children.forEach((child: VNode) => {
(currentInstance.subTree.children as VNode[]).forEach((child: VNode) => {
if (child.type && (child.type as any).name === 'IonTabButton') {
tabState.tabs[child.props.tab] = {
originalHref: child.props.href,
@@ -46,7 +45,7 @@ export const IonTabBar = defineComponent({
});
const checkActiveTab = (currentRoute: any) => {
const childNodes = (currentInstance.subTree.children || []) as VNode[];
const childNodes = currentInstance.subTree.children as VNode[];
const { tabs, activeTab: prevActiveTab } = tabState;
const tabKeys = Object.keys(tabs);
const activeTab = tabKeys

View File

@@ -1,50 +1,10 @@
import { h, defineComponent, VNode } from 'vue';
import { h, defineComponent } from 'vue';
import { IonRouterOutlet } from './IonRouterOutlet';
export const IonTabs = defineComponent({
name: 'IonTabs',
render() {
const { $slots: slots } = this;
const slottedContent = slots.default && slots.default();
let childrenToRender = [
h('div', {
class: 'tabs-inner',
style: {
'position': 'relative',
'flex': '1',
'contain': 'layout size style'
}
}, [
h(IonRouterOutlet, { tabs: true })
])
];
/**
* If ion-tab-bar has slot="top" it needs to be
* rendered before `.tabs-inner` otherwise it will
* not show above the tab content.
*/
if (slottedContent && slottedContent.length > 0) {
const topSlottedTabBar = slottedContent.find((child: VNode) => {
const isTabBar = child.type && (child.type as any).name === 'IonTabBar';
const hasTopSlot = child.props?.slot === 'top';
return isTabBar && hasTopSlot;
});
if (topSlottedTabBar) {
childrenToRender = [
...slottedContent,
...childrenToRender
];
} else {
childrenToRender = [
...childrenToRender,
...slottedContent
]
}
}
return h(
'ion-tabs',
{
@@ -62,7 +22,23 @@ export const IonTabs = defineComponent({
'z-index': '0'
}
},
childrenToRender
[
h(
'div',
{
class: 'tabs-inner',
style: {
'position': 'relative',
'flex': '1',
'contain': 'layout size style'
}
},
[
h(IonRouterOutlet, { tabs: true })
]
),
...slots.default && slots.default()
]
)
}
});

View File

@@ -1,10 +0,0 @@
import { LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_ENTER, LIFECYCLE_WILL_LEAVE } from '@ionic/core';
declare module '@vue/runtime-core' {
export interface ComponentCustomOptions {
[LIFECYCLE_DID_ENTER]?: () => void;
[LIFECYCLE_DID_LEAVE]?: () => void;
[LIFECYCLE_WILL_ENTER]?: () => void;
[LIFECYCLE_WILL_LEAVE]?: () => void;
}
}

View File

@@ -23,8 +23,6 @@ export {
popoverController
} from './controllers';
export * from './globalExtensions';
export {
// Overlay Controllers
alertController,

View File

@@ -109,8 +109,7 @@ export const IonCheckbox = /*@__PURE__*/ defineContainer<JSX.IonCheckbox>('ion-c
export const IonChip = /*@__PURE__*/ defineContainer<JSX.IonChip>('ion-chip', [
'color',
'outline',
'disabled'
'outline'
]);
@@ -574,7 +573,6 @@ export const IonSegment = /*@__PURE__*/ defineContainer<JSX.IonSegment>('ion-seg
'color',
'disabled',
'scrollable',
'swipeGesture',
'value',
'ionChange',
'ionSelect',

View File

@@ -1,7 +1,9 @@
import { Ref, ComponentPublicInstance } from 'vue';
import { LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_ENTER, LIFECYCLE_WILL_LEAVE } from '@ionic/core';
import { Ref } from 'vue';
type LIFECYCLE_EVENTS = typeof LIFECYCLE_WILL_ENTER | typeof LIFECYCLE_DID_ENTER | typeof LIFECYCLE_WILL_LEAVE | typeof LIFECYCLE_DID_LEAVE;
export const LIFECYCLE_WILL_ENTER = 'ionViewWillEnter';
export const LIFECYCLE_DID_ENTER = 'ionViewDidEnter';
export const LIFECYCLE_WILL_LEAVE = 'ionViewWillLeave';
export const LIFECYCLE_DID_LEAVE = 'ionViewDidLeave';
const ids: { [k: string]: number } = { main: 0 };
@@ -12,13 +14,8 @@ export const generateId = (type = 'main') => {
};
// TODO types
export const fireLifecycle = (vueComponent: any, vueInstance: Ref<ComponentPublicInstance>, lifecycle: LIFECYCLE_EVENTS) => {
if (vueComponent?.[lifecycle]) {
vueComponent[lifecycle].bind(vueInstance?.value)();
}
const instance = vueInstance?.value as any;
if (instance?.[lifecycle]) {
instance[lifecycle]();
export const fireLifecycle = (vueComponentRef: Ref<any>, lifecycle: string) => {
if (vueComponentRef && vueComponentRef.value && vueComponentRef.value[lifecycle]) {
vueComponentRef.value[lifecycle]();
}
}

View File

@@ -1,14 +1,6 @@
module.exports = {
preset: "@vue/cli-plugin-unit-jest/presets/typescript-and-babel",
preset: '@vue/cli-plugin-unit-jest/presets/typescript-and-babel',
transform: {
"^.+\\.vue$": "vue-jest"
},
transformIgnorePatterns: ["node_modules/(?!@ionic/vue)"],
globals: {
"ts-jest": {
diagnostics: {
warnOnly: true
}
}
'^.+\\.vue$': 'vue-jest'
}
};
}

View File

@@ -1306,30 +1306,27 @@
}
},
"@ionic/core": {
"version": "5.4.0-dev.202010150003.c5ab562",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.4.0-dev.202010150003.c5ab562.tgz",
"integrity": "sha512-DPqomq+XGWeCV9d1ot2gYqw23yTReG8A4HAeKo5RZ7/HXtRVkMSqGIwcyGVg/hnD4Xem7UfEFdEfvnykLEKBRA==",
"version": "5.4.0-rc.1",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.4.0-rc.1.tgz",
"integrity": "sha512-8/Mcz86GZEA8Q9x86MOpWU+v4PQBmMfjja1LNVlGN+OIh2oIVBbY6EL6+dw9o6lcHoMY4bUHHpgyPvpYDNXfJw==",
"requires": {
"ionicons": "^5.1.2",
"tslib": "^1.10.0"
}
},
"@ionic/vue": {
"version": "5.4.0-dev.202010150003.c5ab562",
"resolved": "https://registry.npmjs.org/@ionic/vue/-/vue-5.4.0-dev.202010150003.c5ab562.tgz",
"integrity": "sha512-l5MC9N3Tk3O0VJZHRO5rXwbglD9/A4WMIqB5Zcls8J9AuDKzh6V+EaZ9if2C4AKSfZAN/flQ/kwNGyYScR0m6w==",
"version": "5.4.0-rc.1",
"resolved": "https://registry.npmjs.org/@ionic/vue/-/vue-5.4.0-rc.1.tgz",
"integrity": "sha512-DqlZIE61Awm3D1jNX6ADbGcfQWKhRZty1EZVjnJ9xPnPLm4h/yKUU2jRUwfN5ia7pYIvKoz+FAqwBgGxO4G8UQ==",
"requires": {
"@ionic/core": "5.4.0-dev.202010150003.c5ab562",
"@ionic/core": "5.4.0-rc.1",
"ionicons": "^5.1.2"
}
},
"@ionic/vue-router": {
"version": "5.4.0-dev.202010150003.c5ab562",
"resolved": "https://registry.npmjs.org/@ionic/vue-router/-/vue-router-5.4.0-dev.202010150003.c5ab562.tgz",
"integrity": "sha512-PqC8i/x5jSMpkOSjNQSGuTK5gBjiUOvdsGikA8HQ7wAbejQ71suDVuoDowlZBsTmLbPsuU07N9D1q23/QqvPVw==",
"requires": {
"path-to-regexp": "^6.2.0"
}
"version": "5.4.0-rc.1",
"resolved": "https://registry.npmjs.org/@ionic/vue-router/-/vue-router-5.4.0-rc.1.tgz",
"integrity": "sha512-Yq+yPJdaCdHZTEefes7ry9fEUyMkxPvxPjzc/e4vgDz1a3/UISM3SGfxP4qRv2RtRs5nbPv0tb1DZuPbMOYT9g=="
},
"@jest/console": {
"version": "24.9.0",
@@ -2855,95 +2852,6 @@
"tslint": "^5.20.1",
"webpack": "^4.0.0",
"yorkie": "^2.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"optional": true
},
"fork-ts-checker-webpack-plugin-v5": {
"version": "npm:fork-ts-checker-webpack-plugin@5.2.0",
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.0.tgz",
"integrity": "sha512-NEKcI0+osT5bBFZ1SFGzJMQETjQWZrSvMO1g0nAR/w0t328Z41eN8BJEIZyFCl2HsuiJpa9AN474Nh2qLVwGLQ==",
"dev": true,
"optional": true,
"requires": {
"@babel/code-frame": "^7.8.3",
"@types/json-schema": "^7.0.5",
"chalk": "^4.1.0",
"cosmiconfig": "^6.0.0",
"deepmerge": "^4.2.2",
"fs-extra": "^9.0.0",
"memfs": "^3.1.2",
"minimatch": "^3.0.4",
"schema-utils": "2.7.0",
"semver": "^7.3.2",
"tapable": "^1.0.0"
}
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"optional": true
},
"schema-utils": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
"dev": true,
"optional": true,
"requires": {
"@types/json-schema": "^7.0.4",
"ajv": "^6.12.2",
"ajv-keywords": "^3.4.1"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"@vue/cli-plugin-unit-jest": {
@@ -3114,17 +3022,6 @@
"unique-filename": "^1.1.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
@@ -3205,18 +3102,6 @@
"supports-color": "^7.0.0"
}
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@@ -3321,18 +3206,6 @@
"webpack-sources": "^1.4.3"
}
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.0.0-beta.8",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.0.0-beta.8.tgz",
"integrity": "sha512-oouKUQWWHbSihqSD7mhymGPX1OQ4hedzAHyvm8RdyHh6m3oIvoRF+NM45i/bhNOlo8jCnuJhaSUf/6oDjv978g==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
}
},
"wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
@@ -8292,6 +8165,95 @@
}
}
},
"fork-ts-checker-webpack-plugin-v5": {
"version": "npm:fork-ts-checker-webpack-plugin@5.2.0",
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.0.tgz",
"integrity": "sha512-NEKcI0+osT5bBFZ1SFGzJMQETjQWZrSvMO1g0nAR/w0t328Z41eN8BJEIZyFCl2HsuiJpa9AN474Nh2qLVwGLQ==",
"dev": true,
"optional": true,
"requires": {
"@babel/code-frame": "^7.8.3",
"@types/json-schema": "^7.0.5",
"chalk": "^4.1.0",
"cosmiconfig": "^6.0.0",
"deepmerge": "^4.2.2",
"fs-extra": "^9.0.0",
"memfs": "^3.1.2",
"minimatch": "^3.0.4",
"schema-utils": "2.7.0",
"semver": "^7.3.2",
"tapable": "^1.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"optional": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"optional": true
},
"schema-utils": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
"dev": true,
"optional": true,
"requires": {
"@types/json-schema": "^7.0.4",
"ajv": "^6.12.2",
"ajv-keywords": "^3.4.1"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"form-data": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
@@ -12339,11 +12301,6 @@
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
"dev": true
},
"path-to-regexp": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.0.tgz",
"integrity": "sha512-f66KywYG6+43afgE/8j/GoiNyygk/bnoCbps++3ErRKsIYkGGupyv07R2Ok5m9i67Iqc+T2g1eAUGUPzWhYTyg=="
},
"path-type": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
@@ -15868,6 +15825,87 @@
}
}
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.0.0-beta.8",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.0.0-beta.8.tgz",
"integrity": "sha512-oouKUQWWHbSihqSD7mhymGPX1OQ4hedzAHyvm8RdyHh6m3oIvoRF+NM45i/bhNOlo8jCnuJhaSUf/6oDjv978g==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"optional": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"optional": true
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"vue-router": {
"version": "4.0.0-beta.11",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.0-beta.11.tgz",

View File

@@ -12,8 +12,8 @@
"sync": "sh ./scripts/sync.sh"
},
"dependencies": {
"@ionic/vue": "^5.4.1",
"@ionic/vue-router": "^5.4.1",
"@ionic/vue": "^5.4.0-rc.1",
"@ionic/vue-router": "^5.4.0-rc.1",
"core-js": "^3.6.5",
"vue": "^3.0.0-0",
"vue-router": "^4.0.0-0"

View File

@@ -2,7 +2,7 @@
<ion-page data-pageid="tabs-secondary">
<ion-content>
<ion-tabs id="tabs">
<ion-tab-bar slot="top">
<ion-tab-bar slot="bottom">
<ion-tab-button tab="tab1-secondary" href="/tabs-secondary/tab1">
<ion-icon :icon="triangle" />
<ion-label>Tab 1</ion-label>

View File

@@ -1,107 +0,0 @@
import { mount, flushPromises } from '@vue/test-utils';
import { createRouter, createWebHistory } from '@ionic/vue-router';
import { IonicVue, IonApp, IonRouterOutlet, IonPage } from '@ionic/vue';
import { defineComponent } from 'vue';
const App = {
components: { IonApp, IonRouterOutlet },
template: '<ion-app><ion-router-outlet /></ion-app>',
}
const BasePage = {
template: '<ion-page :data-pageid="name"></ion-page>',
components: { IonPage },
}
const Page1 = {
...BasePage,
data() {
return {
name: 'page1'
}
},
ionViewWillEnter: jest.fn(),
ionViewDidEnter: jest.fn(),
ionViewWillLeave: jest.fn(),
ionViewDidLeave: jest.fn(),
}
const Page2 = defineComponent({
...BasePage,
setup() {
return {
name: 'page2'
}
},
ionViewWillEnter: jest.fn(),
ionViewDidEnter: jest.fn(),
ionViewWillLeave: jest.fn(),
ionViewDidLeave: jest.fn(),
});
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes: [
{ path: '/', component: Page1 },
{ path: '/2', component: Page2 }
]
});
describe('Lifecycle Events', () => {
it('Triggers lifecycle events', async () => {
// Initial render
router.push('/');
await router.isReady();
const wrapper = mount(App, {
global: {
plugins: [router, IonicVue]
}
});
// Page 1 lifecycle hooks
expect(Page1.ionViewWillEnter).toHaveBeenCalledWith();
expect(Page1.ionViewWillEnter.mock.instances[0]).toEqual(expect.objectContaining({ name: 'page1' }))
expect(Page1.ionViewDidEnter).toHaveBeenCalled();
expect(Page1.ionViewDidEnter.mock.instances[0]).toEqual(expect.objectContaining({ name: 'page1' }))
expect(Page1.ionViewWillLeave).not.toHaveBeenCalled();
expect(Page1.ionViewDidLeave).not.toHaveBeenCalled();
expect(wrapper.html()).toContain('page1');
// Page 2 lifecycle hooks
expect(Page2.ionViewWillEnter).not.toHaveBeenCalled();
expect(Page2.ionViewDidEnter).not.toHaveBeenCalled();
expect(Page2.ionViewWillLeave).not.toHaveBeenCalled();
expect(Page2.ionViewDidLeave).not.toHaveBeenCalled();
// Navigate to 2nd page
router.push('/2');
jest.resetAllMocks();
await flushPromises();
(HTMLElement.prototype as HTMLIonRouterOutletElement).commit = jest.fn();
await new Promise((r) => setTimeout(r, 100));
// Page 1 lifecycle hooks
expect(Page1.ionViewDidEnter).not.toHaveBeenCalled();
expect(Page1.ionViewWillEnter).not.toHaveBeenCalled();
expect(Page1.ionViewWillLeave).toHaveBeenCalled();
expect(Page1.ionViewWillLeave.mock.instances[0]).toEqual(expect.objectContaining({ name: 'page1' }))
expect(Page1.ionViewDidLeave).toHaveBeenCalled();
expect(Page1.ionViewDidLeave.mock.instances[0]).toEqual(expect.objectContaining({ name: 'page1' }))
// Page 2 lifecycle hooks
expect(Page2.ionViewWillEnter).toHaveBeenCalled();
expect((Page2.ionViewWillEnter as jest.Mock).mock.instances[0]).toEqual(expect.objectContaining({ name: 'page2' }))
expect(Page2.ionViewDidEnter).toHaveBeenCalled();
expect((Page2.ionViewDidEnter as jest.Mock).mock.instances[0]).toEqual(expect.objectContaining({ name: 'page2' }))
expect(Page2.ionViewWillLeave).not.toHaveBeenCalled();
expect(Page2.ionViewDidLeave).not.toHaveBeenCalled();
expect(wrapper.html()).toContain('page2');
});
});

View File

@@ -1,123 +0,0 @@
import { mount } from '@vue/test-utils';
import { createRouter, createWebHistory } from '@ionic/vue-router';
import { IonicVue, IonApp, IonRouterOutlet, IonPage, IonTabs, IonTabBar } from '@ionic/vue';
const App = {
components: { IonApp, IonRouterOutlet },
template: '<ion-app><ion-router-outlet /></ion-app>',
}
const BasePage = {
template: '<ion-page :data-pageid="name"></ion-page>',
components: { IonPage },
}
describe('Routing', () => {
it('should pass no props', async () => {
const Page1 = {
...BasePage,
props: {
title: { type: String, default: 'Default Title' }
}
};
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes: [
{ path: '/', component: Page1 }
]
});
router.push('/');
await router.isReady();
const wrapper = mount(App, {
global: {
plugins: [router, IonicVue]
}
});
const cmp = wrapper.findComponent(Page1);
expect(cmp.props()).toEqual({ title: 'Default Title' });
});
it('should pass route props as an object', async () => {
const Page1 = {
...BasePage,
props: {
title: { type: String, default: 'Default Title' }
}
};
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes: [
{ path: '/', component: Page1, props: { title: 'Page 1 Title' } }
]
});
router.push('/');
await router.isReady();
const wrapper = mount(App, {
global: {
plugins: [router, IonicVue]
}
});
const cmp = wrapper.findComponent(Page1);
expect(cmp.props()).toEqual({ title: 'Page 1 Title' });
});
it('should pass route props as a function', async () => {
const Page1 = {
...BasePage,
props: {
title: { type: String, default: 'Default Title' }
}
};
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes: [
{ path: '/myPath', component: Page1, props: function(route) { return { title: `${route.path} Title` } } }
]
});
router.push('/myPath');
await router.isReady();
const wrapper = mount(App, {
global: {
plugins: [router, IonicVue]
}
});
const cmp = wrapper.findComponent(Page1);
expect(cmp.props()).toEqual({ title: '/myPath Title' });
});
it('should pass route params as props', async () => {
const Page1 = {
...BasePage,
props: {
title: { type: String, default: 'Default Title' }
}
};
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes: [
{ path: '/:title', component: Page1, props: true }
]
});
router.push('/myPath');
await router.isReady();
const wrapper = mount(App, {
global: {
plugins: [router, IonicVue]
}
});
const cmp = wrapper.findComponent(Page1);
expect(cmp.props()).toEqual({ title: 'myPath' });
});
});

View File

@@ -1,105 +0,0 @@
import { mount } from '@vue/test-utils';
import { createRouter, createWebHistory } from '@ionic/vue-router';
import { IonicVue, IonApp, IonRouterOutlet, IonPage, IonTabs, IonTabBar } from '@ionic/vue';
const App = {
components: { IonApp, IonRouterOutlet },
template: '<ion-app><ion-router-outlet /></ion-app>',
}
describe('ion-tab-bar', () => {
it('should render in the top slot', async () => {
const Tabs = {
components: { IonPage, IonTabs, IonTabBar },
template: `
<ion-page>
<ion-tabs>
<ion-tab-bar slot="top"></ion-tab-bar>
</ion-tabs>
</ion-page>
`,
}
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes: [
{ path: '/', component: Tabs }
]
});
router.push('/');
await router.isReady();
const wrapper = mount(App, {
global: {
plugins: [router, IonicVue]
}
});
const innerHTML = wrapper.find('ion-tabs').html();
expect(innerHTML).toContain(`<ion-tab-bar slot="top"></ion-tab-bar><div class="tabs-inner" style="position: relative; flex: 1; contain: layout size style;">`);
});
it('should render in the bottom slot', async () => {
const Tabs = {
components: { IonPage, IonTabs, IonTabBar },
template: `
<ion-page>
<ion-tabs>
<ion-tab-bar slot="bottom"></ion-tab-bar>
</ion-tabs>
</ion-page>
`,
}
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes: [
{ path: '/', component: Tabs }
]
});
router.push('/');
await router.isReady();
const wrapper = mount(App, {
global: {
plugins: [router, IonicVue]
}
});
const innerHTML = wrapper.find('ion-tabs').html();
expect(innerHTML).toContain(`<div class="tabs-inner" style="position: relative; flex: 1; contain: layout size style;"><ion-router-outlet tabs="true"></ion-router-outlet></div><ion-tab-bar slot="bottom"></ion-tab-bar>`);
});
it('should render in the default slot', async () => {
const Tabs = {
components: { IonPage, IonTabs, IonTabBar },
template: `
<ion-page>
<ion-tabs>
<ion-tab-bar></ion-tab-bar>
</ion-tabs>
</ion-page>
`,
}
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes: [
{ path: '/', component: Tabs }
]
});
router.push('/');
await router.isReady();
const wrapper = mount(App, {
global: {
plugins: [router, IonicVue]
}
});
const innerHTML = wrapper.find('ion-tabs').html();
expect(innerHTML).toContain(`<div class="tabs-inner" style="position: relative; flex: 1; contain: layout size style;"><ion-router-outlet tabs="true"></ion-router-outlet></div><ion-tab-bar></ion-tab-bar></ion-tabs>`)
});
});

View File

@@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "es2019",
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
@@ -11,11 +11,21 @@
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": ["webpack-env", "jest"],
"types": [
"webpack-env",
"jest"
],
"paths": {
"@/*": ["src/*"]
"@/*": [
"src/*"
]
},
"lib": ["esnext", "dom", "dom.iterable", "scripthost"]
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
@@ -24,5 +34,7 @@
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": ["node_modules"]
"exclude": [
"node_modules"
]
}