Compare commits

..

1 Commits

Author SHA1 Message Date
Liam DeBeasi
8430b132b6 test 2023-09-29 13:27:31 -04:00
465 changed files with 2434 additions and 8861 deletions

View File

@@ -8,12 +8,7 @@ on:
# at 6:00 UTC (6:00 am UTC)
- cron: '00 06 * * 1-5'
workflow_dispatch:
inputs:
npm_release_tag:
required: true
type: string
description: What version should be pulled from NPM?
default: nightly
# allows for manual invocations in the GitHub UI
# When pushing a new commit we should
# cancel the previous test run to not
@@ -29,7 +24,7 @@ jobs:
- uses: actions/checkout@v3
- uses: ./.github/workflows/actions/build-core-stencil-prerelease
with:
stencil-version: ${{ inputs.npm_release_tag || 'nightly' }}
stencil-version: nightly
test-core-clean-build:
needs: [build-core-with-stencil-nightly]
@@ -52,7 +47,7 @@ jobs:
- uses: actions/checkout@v3
- uses: ./.github/workflows/actions/test-core-spec
with:
stencil-version: ${{ inputs.npm_release_tag || 'nightly' }}
stencil-version: nightly
test-core-screenshot:
strategy:

View File

@@ -3,18 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [7.4.3](https://github.com/ionic-team/ionic-framework/compare/v7.4.2...v7.4.3) (2023-10-04)
### Bug Fixes
* **fab-button:** position is correct with custom sizes ([#28195](https://github.com/ionic-team/ionic-framework/issues/28195)) ([eb41b55](https://github.com/ionic-team/ionic-framework/commit/eb41b556b57c97139b9c36dc3e3be3711d8afaca)), closes [#22564](https://github.com/ionic-team/ionic-framework/issues/22564)
* **range:** knob positions are correct on initial render with custom elements build ([#28257](https://github.com/ionic-team/ionic-framework/issues/28257)) ([ac2c8e6](https://github.com/ionic-team/ionic-framework/commit/ac2c8e6c22da4d0d8224def24ddef56ee9d26246)), closes [#25444](https://github.com/ionic-team/ionic-framework/issues/25444)
## [7.4.2](https://github.com/ionic-team/ionic-framework/compare/v7.4.1...v7.4.2) (2023-09-27)

View File

@@ -3,18 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [7.4.3](https://github.com/ionic-team/ionic-framework/compare/v7.4.2...v7.4.3) (2023-10-04)
### Bug Fixes
* **fab-button:** position is correct with custom sizes ([#28195](https://github.com/ionic-team/ionic-framework/issues/28195)) ([eb41b55](https://github.com/ionic-team/ionic-framework/commit/eb41b556b57c97139b9c36dc3e3be3711d8afaca)), closes [#22564](https://github.com/ionic-team/ionic-framework/issues/22564)
* **range:** knob positions are correct on initial render with custom elements build ([#28257](https://github.com/ionic-team/ionic-framework/issues/28257)) ([ac2c8e6](https://github.com/ionic-team/ionic-framework/commit/ac2c8e6c22da4d0d8224def24ddef56ee9d26246)), closes [#25444](https://github.com/ionic-team/ionic-framework/issues/25444)
## [7.4.2](https://github.com/ionic-team/ionic-framework/compare/v7.4.1...v7.4.2) (2023-09-27)

View File

@@ -394,7 +394,7 @@ ion-datetime,prop,disabled,boolean,false,false,false
ion-datetime,prop,doneText,string,'Done',false,false
ion-datetime,prop,firstDayOfWeek,number,0,false,false
ion-datetime,prop,highlightedDates,((dateIsoString: string) => DatetimeHighlightStyle | undefined) | DatetimeHighlight[] | undefined,undefined,false,false
ion-datetime,prop,hourCycle,"h11" | "h12" | "h23" | "h24" | undefined,undefined,false,false
ion-datetime,prop,hourCycle,"h12" | "h23" | undefined,undefined,false,false
ion-datetime,prop,hourValues,number | number[] | string | undefined,undefined,false,false
ion-datetime,prop,isDateEnabled,((dateIsoString: string) => boolean) | undefined,undefined,false,false
ion-datetime,prop,locale,string,'default',false,false
@@ -1445,7 +1445,6 @@ ion-toast,prop,leaveAnimation,((baseEl: any, opts?: any) => Animation) | undefin
ion-toast,prop,message,IonicSafeString | string | undefined,undefined,false,false
ion-toast,prop,mode,"ios" | "md",undefined,false,false
ion-toast,prop,position,"bottom" | "middle" | "top",'bottom',false,false
ion-toast,prop,positionAnchor,HTMLElement | string | undefined,undefined,false,false
ion-toast,prop,translucent,boolean,false,false,false
ion-toast,prop,trigger,string | undefined,undefined,false,false
ion-toast,method,dismiss,dismiss(data?: any, role?: string) => Promise<boolean>

590
core/package-lock.json generated
View File

@@ -1,21 +1,21 @@
{
"name": "@ionic/core",
"version": "7.4.3",
"version": "7.4.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/core",
"version": "7.4.3",
"version": "7.4.2",
"license": "MIT",
"dependencies": {
"@stencil/core": "^4.4.0",
"ionicons": "^7.1.2",
"@stencil/core": "^4.3.0",
"ionicons": "7.1.0",
"tslib": "^2.1.0"
},
"devDependencies": {
"@axe-core/playwright": "^4.7.3",
"@capacitor/core": "^5.4.2",
"@capacitor/core": "^5.4.1",
"@capacitor/haptics": "^5.0.6",
"@capacitor/keyboard": "^5.0.6",
"@capacitor/status-bar": "^5.0.6",
@@ -27,12 +27,12 @@
"@rollup/plugin-virtual": "^2.0.3",
"@stencil/angular-output-target": "^0.8.2",
"@stencil/react-output-target": "^0.5.3",
"@stencil/sass": "^3.0.6",
"@stencil/sass": "^3.0.5",
"@stencil/vue-output-target": "^0.8.6",
"@types/jest": "^27.5.2",
"@types/node": "^14.6.0",
"@typescript-eslint/eslint-plugin": "^6.7.2",
"@typescript-eslint/parser": "^6.7.2",
"@typescript-eslint/eslint-plugin": "^5.17.0",
"@typescript-eslint/parser": "^5.17.0",
"clean-css-cli": "^5.6.1",
"domino": "^2.1.6",
"eslint": "^7.32.0",
@@ -47,7 +47,8 @@
"sass": "^1.26.10",
"serve": "^14.0.1",
"stylelint": "^13.13.1",
"stylelint-order": "^4.1.0"
"stylelint-order": "^4.1.0",
"typescript": "^4.0.5"
}
},
"custom-rules": {
@@ -606,9 +607,9 @@
"dev": true
},
"node_modules/@capacitor/core": {
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.4.2.tgz",
"integrity": "sha512-XbR1vldJFzBWHeoGPpgfNy3Zhjf0NxXdHEaGNANWVBg0ZWG2gwFr1dcRALUUQtbwrEEkCCNiLYg4YiQPRk7SEQ==",
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.4.1.tgz",
"integrity": "sha512-QG9gORuxw2WNcVpLHT1W3LzACOJvFWRuHcz4b9edzxehSELqiSQ4DoGWLp4PuNBBp2oV/fGA4FMNmfZ1jIAAWg==",
"dev": true,
"dependencies": {
"tslib": "^2.1.0"
@@ -641,30 +642,6 @@
"@capacitor/core": "^5.0.0"
}
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
"integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
"dev": true,
"dependencies": {
"eslint-visitor-keys": "^3.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"peerDependencies": {
"eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
}
},
"node_modules/@eslint-community/regexpp": {
"version": "4.8.1",
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.1.tgz",
"integrity": "sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ==",
"dev": true,
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
"node_modules/@eslint/eslintrc": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz",
@@ -1653,9 +1630,9 @@
}
},
"node_modules/@stencil/core": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.4.0.tgz",
"integrity": "sha512-YlLyCqGBsMEuZb3XTO/STT0TX9eSwjoVhCJgtjVfQOF+ebIMVlojTh40CmDveWiWbth687cbr6S2heeussV8Sg==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.3.0.tgz",
"integrity": "sha512-WYjftKg5fuqO7mf3nTl1aCRurkeMmfEF38WcBG4VLF6UPQ+MA76/koedGR2LGhATGByx+pbxR4iRxAr2Bspc9A==",
"bin": {
"stencil": "bin/stencil"
},
@@ -1674,9 +1651,9 @@
}
},
"node_modules/@stencil/sass": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@stencil/sass/-/sass-3.0.6.tgz",
"integrity": "sha512-d+k8qCvDgzI/vIV7M5QavIS6b75X4yn+YQH80KA/L0oL/hKD8KeF3wMOd7P1nDhGLIGRNw5tBDbe0GeIsGVU7A==",
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@stencil/sass/-/sass-3.0.5.tgz",
"integrity": "sha512-9nyllMXOEvHywo6fP2iwXgnq32A+OOUE36Aq7iUjzwT3wdr04qsvupO1JNIyRvmvCDF15hOKXztrZH1/wDu2Zg==",
"dev": true,
"engines": {
"node": ">=12.0.0",
@@ -1808,9 +1785,9 @@
}
},
"node_modules/@types/json-schema": {
"version": "7.0.13",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz",
"integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==",
"version": "7.0.11",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
"dev": true
},
"node_modules/@types/json5": {
@@ -1867,12 +1844,6 @@
"@types/node": "*"
}
},
"node_modules/@types/semver": {
"version": "7.5.2",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.2.tgz",
"integrity": "sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw==",
"dev": true
},
"node_modules/@types/stack-utils": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
@@ -1892,33 +1863,31 @@
"dev": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.2.tgz",
"integrity": "sha512-ooaHxlmSgZTM6CHYAFRlifqh1OAr3PAQEwi7lhYhaegbnXrnh7CDcHmc3+ihhbQC7H0i4JF0psI5ehzkF6Yl6Q==",
"version": "5.17.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz",
"integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==",
"dev": true,
"dependencies": {
"@eslint-community/regexpp": "^4.5.1",
"@typescript-eslint/scope-manager": "6.7.2",
"@typescript-eslint/type-utils": "6.7.2",
"@typescript-eslint/utils": "6.7.2",
"@typescript-eslint/visitor-keys": "6.7.2",
"debug": "^4.3.4",
"graphemer": "^1.4.0",
"ignore": "^5.2.4",
"natural-compare": "^1.4.0",
"semver": "^7.5.4",
"ts-api-utils": "^1.0.1"
"@typescript-eslint/scope-manager": "5.17.0",
"@typescript-eslint/type-utils": "5.17.0",
"@typescript-eslint/utils": "5.17.0",
"debug": "^4.3.2",
"functional-red-black-tree": "^1.0.1",
"ignore": "^5.1.8",
"regexpp": "^3.2.0",
"semver": "^7.3.5",
"tsutils": "^3.21.0"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha",
"eslint": "^7.0.0 || ^8.0.0"
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
},
"peerDependenciesMeta": {
"typescript": {
@@ -1926,6 +1895,30 @@
}
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": {
"version": "5.17.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz",
"integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==",
"dev": true,
"dependencies": {
"@types/json-schema": "^7.0.9",
"@typescript-eslint/scope-manager": "5.17.0",
"@typescript-eslint/types": "5.17.0",
"@typescript-eslint/typescript-estree": "5.17.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -1944,9 +1937,9 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
@@ -2098,26 +2091,25 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.2.tgz",
"integrity": "sha512-KA3E4ox0ws+SPyxQf9iSI25R6b4Ne78ORhNHeVKrPQnoYsb9UhieoiRoJgrzgEeKGOXhcY1i8YtOeCHHTDa6Fw==",
"version": "5.17.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz",
"integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==",
"dev": true,
"dependencies": {
"@typescript-eslint/scope-manager": "6.7.2",
"@typescript-eslint/types": "6.7.2",
"@typescript-eslint/typescript-estree": "6.7.2",
"@typescript-eslint/visitor-keys": "6.7.2",
"debug": "^4.3.4"
"@typescript-eslint/scope-manager": "5.17.0",
"@typescript-eslint/types": "5.17.0",
"@typescript-eslint/typescript-estree": "5.17.0",
"debug": "^4.3.2"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^7.0.0 || ^8.0.0"
"eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
},
"peerDependenciesMeta": {
"typescript": {
@@ -2143,16 +2135,16 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.2.tgz",
"integrity": "sha512-bgi6plgyZjEqapr7u2mhxGR6E8WCzKNUFWNh6fkpVe9+yzRZeYtDTbsIBzKbcxI+r1qVWt6VIoMSNZ4r2A+6Yw==",
"version": "5.17.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz",
"integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.7.2",
"@typescript-eslint/visitor-keys": "6.7.2"
"@typescript-eslint/types": "5.17.0",
"@typescript-eslint/visitor-keys": "5.17.0"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
@@ -2160,25 +2152,24 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.2.tgz",
"integrity": "sha512-36F4fOYIROYRl0qj95dYKx6kybddLtsbmPIYNK0OBeXv2j9L5nZ17j9jmfy+bIDHKQgn2EZX+cofsqi8NPATBQ==",
"version": "5.17.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz",
"integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==",
"dev": true,
"dependencies": {
"@typescript-eslint/typescript-estree": "6.7.2",
"@typescript-eslint/utils": "6.7.2",
"debug": "^4.3.4",
"ts-api-utils": "^1.0.1"
"@typescript-eslint/utils": "5.17.0",
"debug": "^4.3.2",
"tsutils": "^3.21.0"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^7.0.0 || ^8.0.0"
"eslint": "*"
},
"peerDependenciesMeta": {
"typescript": {
@@ -2186,6 +2177,30 @@
}
}
},
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": {
"version": "5.17.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz",
"integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==",
"dev": true,
"dependencies": {
"@types/json-schema": "^7.0.9",
"@typescript-eslint/scope-manager": "5.17.0",
"@typescript-eslint/types": "5.17.0",
"@typescript-eslint/typescript-estree": "5.17.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/@typescript-eslint/type-utils/node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -2204,12 +2219,12 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.2.tgz",
"integrity": "sha512-flJYwMYgnUNDAN9/GAI3l8+wTmvTYdv64fcH8aoJK76Y+1FCZ08RtI5zDerM/FYT5DMkAc+19E4aLmd5KqdFyg==",
"version": "5.17.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz",
"integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==",
"dev": true,
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
@@ -2217,21 +2232,21 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.2.tgz",
"integrity": "sha512-kiJKVMLkoSciGyFU0TOY0fRxnp9qq1AzVOHNeN1+B9erKFCJ4Z8WdjAkKQPP+b1pWStGFqezMLltxO+308dJTQ==",
"version": "5.17.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz",
"integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.7.2",
"@typescript-eslint/visitor-keys": "6.7.2",
"debug": "^4.3.4",
"globby": "^11.1.0",
"@typescript-eslint/types": "5.17.0",
"@typescript-eslint/visitor-keys": "5.17.0",
"debug": "^4.3.2",
"globby": "^11.0.4",
"is-glob": "^4.0.3",
"semver": "^7.5.4",
"ts-api-utils": "^1.0.1"
"semver": "^7.3.5",
"tsutils": "^3.21.0"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
@@ -2261,49 +2276,9 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@typescript-eslint/utils": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.2.tgz",
"integrity": "sha512-ZCcBJug/TS6fXRTsoTkgnsvyWSiXwMNiPzBUani7hDidBdj1779qwM1FIAmpH4lvlOZNF3EScsxxuGifjpLSWQ==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@types/json-schema": "^7.0.12",
"@types/semver": "^7.5.0",
"@typescript-eslint/scope-manager": "6.7.2",
"@typescript-eslint/types": "6.7.2",
"@typescript-eslint/typescript-estree": "6.7.2",
"semver": "^7.5.4"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^7.0.0 || ^8.0.0"
}
},
"node_modules/@typescript-eslint/utils/node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
@@ -2316,16 +2291,16 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.2.tgz",
"integrity": "sha512-uVw9VIMFBUTz8rIeaUT3fFe8xIUx8r4ywAdlQv1ifH+6acn/XF8Y6rwJ7XNmkNMDrTW+7+vxFFPIF40nJCVsMQ==",
"version": "5.17.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz",
"integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.7.2",
"eslint-visitor-keys": "^3.4.1"
"@typescript-eslint/types": "5.17.0",
"eslint-visitor-keys": "^3.0.0"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
@@ -4179,15 +4154,12 @@
}
},
"node_modules/eslint-visitor-keys": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
"integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint/node_modules/@babel/code-frame": {
@@ -5009,12 +4981,6 @@
"integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==",
"dev": true
},
"node_modules/graphemer": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
"dev": true
},
"node_modules/hard-rejection": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz",
@@ -5211,9 +5177,9 @@
}
},
"node_modules/ignore": {
"version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
"integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
"dev": true,
"engines": {
"node": ">= 4"
@@ -5328,9 +5294,9 @@
}
},
"node_modules/ionicons": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-7.1.2.tgz",
"integrity": "sha512-zZ4njAqSP39H8RRvZhJvkHsv7cBjYE/VfInH218Osf2UVxJITSOutTTd25MW+tAXKN5fheYzclUXUsF55JHUDg==",
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-7.1.0.tgz",
"integrity": "sha512-iE4GuEdEHARJpp0sWL7WJZCzNCf5VxpNRhAjW0fLnZPnNL5qZOJUcfup2Z2Ty7Jk8Q5hacrHfGEB1lCwOdXqGg==",
"dependencies": {
"@stencil/core": "^2.18.0"
}
@@ -9758,18 +9724,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/ts-api-utils": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz",
"integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==",
"dev": true,
"engines": {
"node": ">=16.13.0"
},
"peerDependencies": {
"typescript": ">=4.2.0"
}
},
"node_modules/tsconfig-paths": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
@@ -9872,17 +9826,16 @@
}
},
"node_modules/typescript": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz",
"integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==",
"dev": true,
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
"node": ">=4.2.0"
}
},
"node_modules/unbox-primitive": {
@@ -10849,9 +10802,9 @@
"dev": true
},
"@capacitor/core": {
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.4.2.tgz",
"integrity": "sha512-XbR1vldJFzBWHeoGPpgfNy3Zhjf0NxXdHEaGNANWVBg0ZWG2gwFr1dcRALUUQtbwrEEkCCNiLYg4YiQPRk7SEQ==",
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.4.1.tgz",
"integrity": "sha512-QG9gORuxw2WNcVpLHT1W3LzACOJvFWRuHcz4b9edzxehSELqiSQ4DoGWLp4PuNBBp2oV/fGA4FMNmfZ1jIAAWg==",
"dev": true,
"requires": {
"tslib": "^2.1.0"
@@ -10878,21 +10831,6 @@
"dev": true,
"requires": {}
},
"@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
"integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
"dev": true,
"requires": {
"eslint-visitor-keys": "^3.3.0"
}
},
"@eslint-community/regexpp": {
"version": "4.8.1",
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.1.tgz",
"integrity": "sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ==",
"dev": true
},
"@eslint/eslintrc": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz",
@@ -11598,9 +11536,9 @@
"requires": {}
},
"@stencil/core": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.4.0.tgz",
"integrity": "sha512-YlLyCqGBsMEuZb3XTO/STT0TX9eSwjoVhCJgtjVfQOF+ebIMVlojTh40CmDveWiWbth687cbr6S2heeussV8Sg=="
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.3.0.tgz",
"integrity": "sha512-WYjftKg5fuqO7mf3nTl1aCRurkeMmfEF38WcBG4VLF6UPQ+MA76/koedGR2LGhATGByx+pbxR4iRxAr2Bspc9A=="
},
"@stencil/react-output-target": {
"version": "0.5.3",
@@ -11610,9 +11548,9 @@
"requires": {}
},
"@stencil/sass": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@stencil/sass/-/sass-3.0.6.tgz",
"integrity": "sha512-d+k8qCvDgzI/vIV7M5QavIS6b75X4yn+YQH80KA/L0oL/hKD8KeF3wMOd7P1nDhGLIGRNw5tBDbe0GeIsGVU7A==",
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@stencil/sass/-/sass-3.0.5.tgz",
"integrity": "sha512-9nyllMXOEvHywo6fP2iwXgnq32A+OOUE36Aq7iUjzwT3wdr04qsvupO1JNIyRvmvCDF15hOKXztrZH1/wDu2Zg==",
"dev": true,
"requires": {}
},
@@ -11729,9 +11667,9 @@
}
},
"@types/json-schema": {
"version": "7.0.13",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz",
"integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==",
"version": "7.0.11",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
"dev": true
},
"@types/json5": {
@@ -11788,12 +11726,6 @@
"@types/node": "*"
}
},
"@types/semver": {
"version": "7.5.2",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.2.tgz",
"integrity": "sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw==",
"dev": true
},
"@types/stack-utils": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
@@ -11813,24 +11745,36 @@
"dev": true
},
"@typescript-eslint/eslint-plugin": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.2.tgz",
"integrity": "sha512-ooaHxlmSgZTM6CHYAFRlifqh1OAr3PAQEwi7lhYhaegbnXrnh7CDcHmc3+ihhbQC7H0i4JF0psI5ehzkF6Yl6Q==",
"version": "5.17.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz",
"integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==",
"dev": true,
"requires": {
"@eslint-community/regexpp": "^4.5.1",
"@typescript-eslint/scope-manager": "6.7.2",
"@typescript-eslint/type-utils": "6.7.2",
"@typescript-eslint/utils": "6.7.2",
"@typescript-eslint/visitor-keys": "6.7.2",
"debug": "^4.3.4",
"graphemer": "^1.4.0",
"ignore": "^5.2.4",
"natural-compare": "^1.4.0",
"semver": "^7.5.4",
"ts-api-utils": "^1.0.1"
"@typescript-eslint/scope-manager": "5.17.0",
"@typescript-eslint/type-utils": "5.17.0",
"@typescript-eslint/utils": "5.17.0",
"debug": "^4.3.2",
"functional-red-black-tree": "^1.0.1",
"ignore": "^5.1.8",
"regexpp": "^3.2.0",
"semver": "^7.3.5",
"tsutils": "^3.21.0"
},
"dependencies": {
"@typescript-eslint/utils": {
"version": "5.17.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz",
"integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.9",
"@typescript-eslint/scope-manager": "5.17.0",
"@typescript-eslint/types": "5.17.0",
"@typescript-eslint/typescript-estree": "5.17.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0"
}
},
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -11841,9 +11785,9 @@
}
},
"semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
"dev": true,
"requires": {
"lru-cache": "^6.0.0"
@@ -11933,16 +11877,15 @@
}
},
"@typescript-eslint/parser": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.2.tgz",
"integrity": "sha512-KA3E4ox0ws+SPyxQf9iSI25R6b4Ne78ORhNHeVKrPQnoYsb9UhieoiRoJgrzgEeKGOXhcY1i8YtOeCHHTDa6Fw==",
"version": "5.17.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz",
"integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==",
"dev": true,
"requires": {
"@typescript-eslint/scope-manager": "6.7.2",
"@typescript-eslint/types": "6.7.2",
"@typescript-eslint/typescript-estree": "6.7.2",
"@typescript-eslint/visitor-keys": "6.7.2",
"debug": "^4.3.4"
"@typescript-eslint/scope-manager": "5.17.0",
"@typescript-eslint/types": "5.17.0",
"@typescript-eslint/typescript-estree": "5.17.0",
"debug": "^4.3.2"
},
"dependencies": {
"debug": {
@@ -11957,27 +11900,40 @@
}
},
"@typescript-eslint/scope-manager": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.2.tgz",
"integrity": "sha512-bgi6plgyZjEqapr7u2mhxGR6E8WCzKNUFWNh6fkpVe9+yzRZeYtDTbsIBzKbcxI+r1qVWt6VIoMSNZ4r2A+6Yw==",
"version": "5.17.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz",
"integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==",
"dev": true,
"requires": {
"@typescript-eslint/types": "6.7.2",
"@typescript-eslint/visitor-keys": "6.7.2"
"@typescript-eslint/types": "5.17.0",
"@typescript-eslint/visitor-keys": "5.17.0"
}
},
"@typescript-eslint/type-utils": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.2.tgz",
"integrity": "sha512-36F4fOYIROYRl0qj95dYKx6kybddLtsbmPIYNK0OBeXv2j9L5nZ17j9jmfy+bIDHKQgn2EZX+cofsqi8NPATBQ==",
"version": "5.17.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz",
"integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==",
"dev": true,
"requires": {
"@typescript-eslint/typescript-estree": "6.7.2",
"@typescript-eslint/utils": "6.7.2",
"debug": "^4.3.4",
"ts-api-utils": "^1.0.1"
"@typescript-eslint/utils": "5.17.0",
"debug": "^4.3.2",
"tsutils": "^3.21.0"
},
"dependencies": {
"@typescript-eslint/utils": {
"version": "5.17.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz",
"integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.9",
"@typescript-eslint/scope-manager": "5.17.0",
"@typescript-eslint/types": "5.17.0",
"@typescript-eslint/typescript-estree": "5.17.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0"
}
},
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -11990,24 +11946,24 @@
}
},
"@typescript-eslint/types": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.2.tgz",
"integrity": "sha512-flJYwMYgnUNDAN9/GAI3l8+wTmvTYdv64fcH8aoJK76Y+1FCZ08RtI5zDerM/FYT5DMkAc+19E4aLmd5KqdFyg==",
"version": "5.17.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz",
"integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.2.tgz",
"integrity": "sha512-kiJKVMLkoSciGyFU0TOY0fRxnp9qq1AzVOHNeN1+B9erKFCJ4Z8WdjAkKQPP+b1pWStGFqezMLltxO+308dJTQ==",
"version": "5.17.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz",
"integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "6.7.2",
"@typescript-eslint/visitor-keys": "6.7.2",
"debug": "^4.3.4",
"globby": "^11.1.0",
"@typescript-eslint/types": "5.17.0",
"@typescript-eslint/visitor-keys": "5.17.0",
"debug": "^4.3.2",
"globby": "^11.0.4",
"is-glob": "^4.0.3",
"semver": "^7.5.4",
"ts-api-utils": "^1.0.1"
"semver": "^7.3.5",
"tsutils": "^3.21.0"
},
"dependencies": {
"debug": {
@@ -12020,35 +11976,9 @@
}
},
"semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"requires": {
"lru-cache": "^6.0.0"
}
}
}
},
"@typescript-eslint/utils": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.2.tgz",
"integrity": "sha512-ZCcBJug/TS6fXRTsoTkgnsvyWSiXwMNiPzBUani7hDidBdj1779qwM1FIAmpH4lvlOZNF3EScsxxuGifjpLSWQ==",
"dev": true,
"requires": {
"@eslint-community/eslint-utils": "^4.4.0",
"@types/json-schema": "^7.0.12",
"@types/semver": "^7.5.0",
"@typescript-eslint/scope-manager": "6.7.2",
"@typescript-eslint/types": "6.7.2",
"@typescript-eslint/typescript-estree": "6.7.2",
"semver": "^7.5.4"
},
"dependencies": {
"semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
"dev": true,
"requires": {
"lru-cache": "^6.0.0"
@@ -12057,13 +11987,13 @@
}
},
"@typescript-eslint/visitor-keys": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.2.tgz",
"integrity": "sha512-uVw9VIMFBUTz8rIeaUT3fFe8xIUx8r4ywAdlQv1ifH+6acn/XF8Y6rwJ7XNmkNMDrTW+7+vxFFPIF40nJCVsMQ==",
"version": "5.17.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz",
"integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==",
"dev": true,
"requires": {
"@typescript-eslint/types": "6.7.2",
"eslint-visitor-keys": "^3.4.1"
"@typescript-eslint/types": "5.17.0",
"eslint-visitor-keys": "^3.0.0"
}
},
"@zeit/schemas": {
@@ -13612,9 +13542,9 @@
}
},
"eslint-visitor-keys": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
"integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
"dev": true
},
"espree": {
@@ -14064,12 +13994,6 @@
"integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==",
"dev": true
},
"graphemer": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
"dev": true
},
"hard-rejection": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz",
@@ -14212,9 +14136,9 @@
}
},
"ignore": {
"version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
"integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
"dev": true
},
"import-fresh": {
@@ -14297,9 +14221,9 @@
}
},
"ionicons": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-7.1.2.tgz",
"integrity": "sha512-zZ4njAqSP39H8RRvZhJvkHsv7cBjYE/VfInH218Osf2UVxJITSOutTTd25MW+tAXKN5fheYzclUXUsF55JHUDg==",
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-7.1.0.tgz",
"integrity": "sha512-iE4GuEdEHARJpp0sWL7WJZCzNCf5VxpNRhAjW0fLnZPnNL5qZOJUcfup2Z2Ty7Jk8Q5hacrHfGEB1lCwOdXqGg==",
"requires": {
"@stencil/core": "^2.18.0"
},
@@ -17576,13 +17500,6 @@
"integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==",
"dev": true
},
"ts-api-utils": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz",
"integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==",
"dev": true,
"requires": {}
},
"tsconfig-paths": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
@@ -17665,11 +17582,10 @@
}
},
"typescript": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
"dev": true,
"peer": true
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz",
"integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==",
"dev": true
},
"unbox-primitive": {
"version": "1.0.1",

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/core",
"version": "7.4.3",
"version": "7.4.2",
"description": "Base components for Ionic",
"keywords": [
"ionic",
@@ -31,13 +31,13 @@
"loader/"
],
"dependencies": {
"@stencil/core": "^4.4.0",
"ionicons": "^7.1.2",
"@stencil/core": "^4.3.0",
"ionicons": "7.1.0",
"tslib": "^2.1.0"
},
"devDependencies": {
"@axe-core/playwright": "^4.7.3",
"@capacitor/core": "^5.4.2",
"@capacitor/core": "^5.4.1",
"@capacitor/haptics": "^5.0.6",
"@capacitor/keyboard": "^5.0.6",
"@capacitor/status-bar": "^5.0.6",
@@ -49,12 +49,12 @@
"@rollup/plugin-virtual": "^2.0.3",
"@stencil/angular-output-target": "^0.8.2",
"@stencil/react-output-target": "^0.5.3",
"@stencil/sass": "^3.0.6",
"@stencil/sass": "^3.0.5",
"@stencil/vue-output-target": "^0.8.6",
"@types/jest": "^27.5.2",
"@types/node": "^14.6.0",
"@typescript-eslint/eslint-plugin": "^6.7.2",
"@typescript-eslint/parser": "^6.7.2",
"@typescript-eslint/eslint-plugin": "^5.17.0",
"@typescript-eslint/parser": "^5.17.0",
"clean-css-cli": "^5.6.1",
"domino": "^2.1.6",
"eslint": "^7.32.0",
@@ -69,7 +69,8 @@
"sass": "^1.26.10",
"serve": "^14.0.1",
"stylelint": "^13.13.1",
"stylelint-order": "^4.1.0"
"stylelint-order": "^4.1.0",
"typescript": "^4.0.5"
},
"scripts": {
"build": "npm run clean && npm run build.css && stencil build --es5 --docs-json dist/docs.json",

View File

@@ -15,7 +15,7 @@ import { RouteID, RouterDirection, RouterEventDetail, RouteWrite } from "./compo
import { BreadcrumbCollapsedClickEventDetail } from "./components/breadcrumb/breadcrumb-interface";
import { CheckboxChangeEventDetail } from "./components/checkbox/checkbox-interface";
import { ScrollBaseDetail, ScrollDetail } from "./components/content/content-interface";
import { DatetimeChangeEventDetail, DatetimeHighlight, DatetimeHighlightCallback, DatetimeHourCycle, DatetimePresentation, TitleSelectedDatesFormatter } from "./components/datetime/datetime-interface";
import { DatetimeChangeEventDetail, DatetimeHighlight, DatetimeHighlightCallback, DatetimePresentation, TitleSelectedDatesFormatter } from "./components/datetime/datetime-interface";
import { SpinnerTypes } from "./components/spinner/spinner-configs";
import { InputChangeEventDetail, InputInputEventDetail } from "./components/input/input-interface";
import { CounterFormatter } from "./components/item/item-interface";
@@ -39,7 +39,7 @@ import { SelectChangeEventDetail, SelectCompareFn, SelectInterface } from "./com
import { SelectPopoverOption } from "./components/select-popover/select-popover-interface";
import { TabBarChangedEventDetail, TabButtonClickEventDetail, TabButtonLayout } from "./components/tab-bar/tab-bar-interface";
import { TextareaChangeEventDetail, TextareaInputEventDetail } from "./components/textarea/textarea-interface";
import { ToastButton, ToastDismissOptions, ToastLayout, ToastPosition, ToastPresentOptions } from "./components/toast/toast-interface";
import { ToastButton, ToastLayout, ToastPosition } from "./components/toast/toast-interface";
import { ToggleChangeEventDetail } from "./components/toggle/toggle-interface";
export { AccordionGroupChangeEventDetail } from "./components/accordion-group/accordion-group-interface";
export { AnimationBuilder, AutocompleteTypes, Color, ComponentProps, ComponentRef, FrameworkDelegate, StyleEventDetail, TextFieldTypes } from "./interface";
@@ -51,7 +51,7 @@ export { RouteID, RouterDirection, RouterEventDetail, RouteWrite } from "./compo
export { BreadcrumbCollapsedClickEventDetail } from "./components/breadcrumb/breadcrumb-interface";
export { CheckboxChangeEventDetail } from "./components/checkbox/checkbox-interface";
export { ScrollBaseDetail, ScrollDetail } from "./components/content/content-interface";
export { DatetimeChangeEventDetail, DatetimeHighlight, DatetimeHighlightCallback, DatetimeHourCycle, DatetimePresentation, TitleSelectedDatesFormatter } from "./components/datetime/datetime-interface";
export { DatetimeChangeEventDetail, DatetimeHighlight, DatetimeHighlightCallback, DatetimePresentation, TitleSelectedDatesFormatter } from "./components/datetime/datetime-interface";
export { SpinnerTypes } from "./components/spinner/spinner-configs";
export { InputChangeEventDetail, InputInputEventDetail } from "./components/input/input-interface";
export { CounterFormatter } from "./components/item/item-interface";
@@ -75,7 +75,7 @@ export { SelectChangeEventDetail, SelectCompareFn, SelectInterface } from "./com
export { SelectPopoverOption } from "./components/select-popover/select-popover-interface";
export { TabBarChangedEventDetail, TabButtonClickEventDetail, TabButtonLayout } from "./components/tab-bar/tab-bar-interface";
export { TextareaChangeEventDetail, TextareaInputEventDetail } from "./components/textarea/textarea-interface";
export { ToastButton, ToastDismissOptions, ToastLayout, ToastPosition, ToastPresentOptions } from "./components/toast/toast-interface";
export { ToastButton, ToastLayout, ToastPosition } from "./components/toast/toast-interface";
export { ToggleChangeEventDetail } from "./components/toggle/toggle-interface";
export namespace Components {
interface IonAccordion {
@@ -865,7 +865,7 @@ export namespace Components {
/**
* The hour cycle of the `ion-datetime`. If no value is set, this is specified by the current locale.
*/
"hourCycle"?: DatetimeHourCycle;
"hourCycle"?: 'h23' | 'h12';
/**
* Values used to create the list of selectable hours. By default the hour values range from `0` to `23` for 24-hour, or `1` to `12` for 12-hour. However, to control exactly which hours to display, the `hourValues` input can take a number, an array of numbers, or a string of comma separated numbers.
*/
@@ -3157,13 +3157,9 @@ export namespace Components {
"onWillDismiss": <T = any>() => Promise<OverlayEventDetail<T>>;
"overlayIndex": number;
/**
* The starting position of the toast on the screen. Can be tweaked further using the `positionAnchor` property.
* The position of the toast on the screen.
*/
"position": ToastPosition;
/**
* The element to anchor the toast's position to. Can be set as a direct reference or the ID of the element. With `position="bottom"`, the toast will sit above the chosen element. With `position="top"`, the toast will sit below the chosen element. With `position="middle"`, the value of `positionAnchor` is ignored.
*/
"positionAnchor"?: HTMLElement | string;
/**
* Present the toast overlay after it has been created.
*/
@@ -4893,7 +4889,7 @@ declare namespace LocalJSX {
/**
* The hour cycle of the `ion-datetime`. If no value is set, this is specified by the current locale.
*/
"hourCycle"?: DatetimeHourCycle;
"hourCycle"?: 'h23' | 'h12';
/**
* Values used to create the list of selectable hours. By default the hour values range from `0` to `23` for 24-hour, or `1` to `12` for 12-hour. However, to control exactly which hours to display, the `hourValues` input can take a number, an array of numbers, or a string of comma separated numbers.
*/
@@ -6980,7 +6976,6 @@ declare namespace LocalJSX {
*/
"mode"?: "ios" | "md";
"onIonTabBarChanged"?: (event: IonTabBarCustomEvent<TabBarChangedEventDetail>) => void;
"onIonTabBarLoaded"?: (event: IonTabBarCustomEvent<void>) => void;
/**
* The selected tab component
*/
@@ -7312,13 +7307,9 @@ declare namespace LocalJSX {
"onWillPresent"?: (event: IonToastCustomEvent<void>) => void;
"overlayIndex": number;
/**
* The starting position of the toast on the screen. Can be tweaked further using the `positionAnchor` property.
* The position of the toast on the screen.
*/
"position"?: ToastPosition;
/**
* The element to anchor the toast's position to. Can be set as a direct reference or the ID of the element. With `position="bottom"`, the toast will sit above the chosen element. With `position="top"`, the toast will sit below the chosen element. With `position="middle"`, the value of `positionAnchor` is ignored.
*/
"positionAnchor"?: HTMLElement | string;
/**
* If `true`, the toast will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility).
*/

View File

@@ -228,15 +228,6 @@ export class Alert implements ComponentInterface, OverlayInterface {
onKeydown(ev: any) {
const inputTypes = new Set(this.processedInputs.map((i) => i.type));
/**
* Based on keyboard navigation requirements, the
* checkbox should not respond to the enter keydown event.
*/
if (inputTypes.has('checkbox') && ev.key === 'Enter') {
ev.preventDefault();
return;
}
// The only inputs we want to navigate between using arrow keys are the radios
// ignore the keydown event if it is not on a radio button
if (

View File

@@ -76,22 +76,5 @@ configs({ directions: ['ltr'] }).forEach(({ config, title }) => {
await expect(alertButton).toHaveAttribute('aria-labelledby', 'close-label');
await expect(alertButton).toHaveAttribute('aria-label', 'close button');
});
test('should not toggle the checkbox when pressing the Enter key', async ({ page }) => {
const didPresent = await page.spyOnEvent('ionAlertDidPresent');
const button = page.locator('#checkbox');
await button.click();
await didPresent.next();
const alertCheckbox = page.locator('ion-alert .alert-checkbox');
const ariaChecked = await alertCheckbox.getAttribute('aria-checked');
await expect(alertCheckbox).toHaveAttribute('aria-checked', ariaChecked!);
await alertCheckbox.press('Enter');
await expect(alertCheckbox).toHaveAttribute('aria-checked', ariaChecked!);
});
});
});

View File

@@ -25,7 +25,6 @@
<ion-button id="noMessage" expand="block" onclick="presentNoMessage()">No Message</ion-button>
<ion-button id="customAria" expand="block" onclick="presentCustomAria()">Custom Aria</ion-button>
<ion-button id="ariaLabelButton" expand="block" onclick="presentAriaLabelButton()">Aria Label Button</ion-button>
<ion-button id="checkbox" expand="block" onclick="presentAlertCheckbox()">Checkbox</ion-button>
</main>
<script>
@@ -95,21 +94,6 @@
],
});
}
function presentAlertCheckbox() {
openAlert({
header: 'Checkbox',
inputs: [
{
type: 'checkbox',
label: 'Checkbox 1',
value: 'value1',
checked: true,
},
],
buttons: ['OK'],
});
}
</script>
</body>
</html>

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

After

Width:  |  Height:  |  Size: 134 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 123 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 65 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -5,7 +5,7 @@
* @prop --background: Background of the chip
* @prop --color: Color of the chip
*/
--background: #{rgba($text-color-rgb, .12)};
--background: blue;
--color: #{rgba($text-color-rgb, .87)};
@include border-radius(16px);

View File

@@ -1,6 +1,6 @@
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Build, Component, Element, Event, Host, Listen, Method, Prop, forceUpdate, h, readTask } from '@stencil/core';
import { componentOnReady, hasLazyBuild } from '@utils/helpers';
import { componentOnReady } from '@utils/helpers';
import { isPlatform } from '@utils/platform';
import { isRTL } from '@utils/rtl';
import { createColorClasses, hostContext } from '@utils/theme';
@@ -34,9 +34,6 @@ export class Content implements ComponentInterface {
private isMainContent = true;
private resizeTimeout: ReturnType<typeof setTimeout> | null = null;
private tabsElement: HTMLElement | null = null;
private tabsLoadCallback?: () => void;
// Detail is used in a hot loop in the scroll event, by allocating it here
// V8 will be able to inline any read/write to it since it's a monomorphic class.
// https://mrale.ph/blog/2015/01/11/whats-up-with-monomorphism.html
@@ -118,61 +115,15 @@ export class Content implements ComponentInterface {
connectedCallback() {
this.isMainContent = this.el.closest('ion-menu, ion-popover, ion-modal') === null;
/**
* The fullscreen content offsets need to be
* computed after the tab bar has loaded. Since
* lazy evaluation means components are not hydrated
* at the same time, we need to wait for the ionTabBarLoaded
* event to fire. This does not impact dist-custom-elements
* because there is no hydration there.
*/
if (hasLazyBuild(this.el)) {
/**
* We need to cache the reference to the tabs.
* If just the content is unmounted then we won't
* be able to query for the closest tabs on disconnectedCallback
* since the content has been removed from the DOM tree.
*/
const closestTabs = (this.tabsElement = this.el.closest('ion-tabs'));
if (closestTabs !== null) {
/**
* When adding and removing the event listener
* we need to make sure we pass the same function reference
* otherwise the event listener will not be removed properly.
* We can't only pass `this.resize` because "this" in the function
* context becomes a reference to IonTabs instead of IonContent.
*
* Additionally, we listen for ionTabBarLoaded on the IonTabs
* instance rather than the IonTabBar instance. It's possible for
* a tab bar to be conditionally rendered/mounted. Since ionTabBarLoaded
* bubbles, we can catch any instances of child tab bars loading by listening
* on IonTabs.
*/
this.tabsLoadCallback = () => this.resize();
closestTabs.addEventListener('ionTabBarLoaded', this.tabsLoadCallback);
}
}
}
disconnectedCallback() {
this.onScrollEnd();
}
if (hasLazyBuild(this.el)) {
/**
* The event listener and tabs caches need to
* be cleared otherwise this will create a memory
* leak where the IonTabs instance can never be
* garbage collected.
*/
const { tabsElement, tabsLoadCallback } = this;
if (tabsElement !== null && tabsLoadCallback !== undefined) {
tabsElement.removeEventListener('ionTabBarLoaded', tabsLoadCallback);
}
this.tabsElement = null;
this.tabsLoadCallback = undefined;
}
@Listen('appload', { target: 'window' })
onAppLoad() {
this.resize();
}
/**

View File

@@ -9,7 +9,7 @@ import type { Color } from '../../interface';
import type { DatetimePresentation } from '../datetime/datetime-interface';
import { getToday } from '../datetime/utils/data';
import { getMonthAndYear, getMonthDayAndYear, getLocalizedDateTime, getLocalizedTime } from '../datetime/utils/format';
import { getHourCycle } from '../datetime/utils/helpers';
import { is24Hour } from '../datetime/utils/helpers';
import { parseDate } from '../datetime/utils/parse';
/**
* @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use.
@@ -218,7 +218,7 @@ export class DatetimeButton implements ComponentInterface {
* warning in the console.
*/
const firstParsedDatetime = parsedDatetimes[0];
const computedHourCycle = getHourCycle(locale, hourCycle);
const use24Hour = is24Hour(locale, hourCycle);
this.dateText = this.timeText = undefined;
@@ -226,7 +226,7 @@ export class DatetimeButton implements ComponentInterface {
case 'date-time':
case 'time-date':
const dateText = getMonthDayAndYear(locale, firstParsedDatetime);
const timeText = getLocalizedTime(locale, firstParsedDatetime, computedHourCycle);
const timeText = getLocalizedTime(locale, firstParsedDatetime, use24Hour);
if (preferWheel) {
this.dateText = `${dateText} ${timeText}`;
} else {
@@ -250,7 +250,7 @@ export class DatetimeButton implements ComponentInterface {
}
break;
case 'time':
this.timeText = getLocalizedTime(locale, firstParsedDatetime, computedHourCycle);
this.timeText = getLocalizedTime(locale, firstParsedDatetime, use24Hour);
break;
case 'month-year':
this.dateText = getMonthAndYear(locale, firstParsedDatetime);

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -34,5 +34,3 @@ export type DatetimeHighlightStyle =
export type DatetimeHighlight = { date: string } & DatetimeHighlightStyle;
export type DatetimeHighlightCallback = (dateIsoString: string) => DatetimeHighlightStyle | undefined;
export type DatetimeHourCycle = 'h11' | 'h12' | 'h23' | 'h24';

View File

@@ -19,7 +19,6 @@ import type {
DatetimeHighlight,
DatetimeHighlightStyle,
DatetimeHighlightCallback,
DatetimeHourCycle,
} from './datetime-interface';
import { isSameDay, warnIfValueOutOfBounds, isBefore, isAfter } from './utils/comparison';
import {
@@ -34,7 +33,7 @@ import {
getCombinedDateColumnData,
} from './utils/data';
import { formatValue, getLocalizedTime, getMonthAndDay, getMonthAndYear } from './utils/format';
import { isLocaleDayPeriodRTL, isMonthFirstLocale, getNumDaysInMonth, getHourCycle } from './utils/helpers';
import { is24Hour, isLocaleDayPeriodRTL, isMonthFirstLocale, getNumDaysInMonth } from './utils/helpers';
import {
calculateHourFromAMPM,
convertDataToISO,
@@ -423,7 +422,7 @@ export class Datetime implements ComponentInterface {
* The hour cycle of the `ion-datetime`. If no value is set, this is
* specified by the current locale.
*/
@Prop() hourCycle?: DatetimeHourCycle;
@Prop() hourCycle?: 'h23' | 'h12';
/**
* If `cover`, the `ion-datetime` will expand to cover the full width of its container.
@@ -2238,7 +2237,7 @@ export class Datetime implements ComponentInterface {
private renderTimeOverlay() {
const { hourCycle, isTimePopoverOpen, locale } = this;
const computedHourCycle = getHourCycle(locale, hourCycle);
const use24Hour = is24Hour(locale, hourCycle);
const activePart = this.getActivePartsWithFallback();
return [
@@ -2271,7 +2270,7 @@ export class Datetime implements ComponentInterface {
}
}}
>
{getLocalizedTime(locale, activePart, computedHourCycle)}
{getLocalizedTime(locale, activePart, use24Hour)}
</button>,
<ion-popover
alignment="center"

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -1,125 +1,4 @@
import {
generateMonths,
getDaysOfWeek,
generateTime,
getToday,
getCombinedDateColumnData,
getTimeColumnsData,
} from '../utils/data';
// The minutes are the same across all hour cycles, so we don't check those
describe('getTimeColumnsData()', () => {
it('should generate formatted h12 hours and AM/PM data data', () => {
const refParts = { month: 5, year: 2021, day: 1, hour: 4, minute: 30 };
const results = getTimeColumnsData('en-US', refParts, 'h12');
expect(results.hoursData).toEqual([
{ text: '12', value: 0 },
{ text: '1', value: 1 },
{ text: '2', value: 2 },
{ text: '3', value: 3 },
{ text: '4', value: 4 },
{ text: '5', value: 5 },
{ text: '6', value: 6 },
{ text: '7', value: 7 },
{ text: '8', value: 8 },
{ text: '9', value: 9 },
{ text: '10', value: 10 },
{ text: '11', value: 11 },
]);
expect(results.dayPeriodData).toEqual([
{ text: 'AM', value: 'am' },
{ text: 'PM', value: 'pm' },
]);
});
it('should generate formatted h23 hours and AM/PM data data', () => {
const refParts = { month: 5, year: 2021, day: 1, hour: 4, minute: 30 };
const results = getTimeColumnsData('en-US', refParts, 'h23');
expect(results.hoursData).toEqual([
{ text: '00', value: 0 },
{ text: '01', value: 1 },
{ text: '02', value: 2 },
{ text: '03', value: 3 },
{ text: '04', value: 4 },
{ text: '05', value: 5 },
{ text: '06', value: 6 },
{ text: '07', value: 7 },
{ text: '08', value: 8 },
{ text: '09', value: 9 },
{ text: '10', value: 10 },
{ text: '11', value: 11 },
{ text: '12', value: 12 },
{ text: '13', value: 13 },
{ text: '14', value: 14 },
{ text: '15', value: 15 },
{ text: '16', value: 16 },
{ text: '17', value: 17 },
{ text: '18', value: 18 },
{ text: '19', value: 19 },
{ text: '20', value: 20 },
{ text: '21', value: 21 },
{ text: '22', value: 22 },
{ text: '23', value: 23 },
]);
expect(results.dayPeriodData).toEqual([]);
});
it('should generate formatted h11 hours and AM/PM data data', () => {
const refParts = { month: 5, year: 2021, day: 1, hour: 4, minute: 30 };
const results = getTimeColumnsData('en-US', refParts, 'h11');
expect(results.hoursData).toEqual([
{ text: '0', value: 0 },
{ text: '1', value: 1 },
{ text: '2', value: 2 },
{ text: '3', value: 3 },
{ text: '4', value: 4 },
{ text: '5', value: 5 },
{ text: '6', value: 6 },
{ text: '7', value: 7 },
{ text: '8', value: 8 },
{ text: '9', value: 9 },
{ text: '10', value: 10 },
{ text: '11', value: 11 },
]);
expect(results.dayPeriodData).toEqual([
{ text: 'AM', value: 'am' },
{ text: 'PM', value: 'pm' },
]);
});
it('should generate formatted h24 hours and AM/PM data data', () => {
const refParts = { month: 5, year: 2021, day: 1, hour: 4, minute: 30 };
const results = getTimeColumnsData('en-US', refParts, 'h24');
expect(results.hoursData).toEqual([
{ text: '01', value: 1 },
{ text: '02', value: 2 },
{ text: '03', value: 3 },
{ text: '04', value: 4 },
{ text: '05', value: 5 },
{ text: '06', value: 6 },
{ text: '07', value: 7 },
{ text: '08', value: 8 },
{ text: '09', value: 9 },
{ text: '10', value: 10 },
{ text: '11', value: 11 },
{ text: '12', value: 12 },
{ text: '13', value: 13 },
{ text: '14', value: 14 },
{ text: '15', value: 15 },
{ text: '16', value: 16 },
{ text: '17', value: 17 },
{ text: '18', value: 18 },
{ text: '19', value: 19 },
{ text: '20', value: 20 },
{ text: '21', value: 21 },
{ text: '22', value: 22 },
{ text: '23', value: 23 },
{ text: '24', value: 0 },
]);
expect(results.dayPeriodData).toEqual([]);
});
});
import { generateMonths, getDaysOfWeek, generateTime, getToday, getCombinedDateColumnData } from '../utils/data';
describe('generateMonths()', () => {
it('should generate correct month data', () => {
@@ -162,7 +41,7 @@ describe('generateTime()', () => {
hour: 5,
minute: 43,
};
const { hours, minutes } = generateTime('en-US', today);
const { hours, minutes } = generateTime(today);
expect(hours.length).toEqual(12);
expect(minutes.length).toEqual(60);
@@ -182,7 +61,7 @@ describe('generateTime()', () => {
hour: 2,
minute: 40,
};
const { hours, minutes } = generateTime('en-US', today, 'h12', min);
const { hours, minutes } = generateTime(today, 'h12', min);
expect(hours.length).toEqual(10);
expect(minutes.length).toEqual(60);
@@ -202,7 +81,7 @@ describe('generateTime()', () => {
hour: 2,
minute: 40,
};
const { hours, minutes } = generateTime('en-US', today, 'h12', min);
const { hours, minutes } = generateTime(today, 'h12', min);
expect(hours.length).toEqual(12);
expect(minutes.length).toEqual(60);
@@ -222,7 +101,7 @@ describe('generateTime()', () => {
hour: 7,
minute: 44,
};
const { hours, minutes } = generateTime('en-US', today, 'h12', undefined, max);
const { hours, minutes } = generateTime(today, 'h12', undefined, max);
expect(hours.length).toEqual(8);
expect(minutes.length).toEqual(45);
@@ -242,7 +121,7 @@ describe('generateTime()', () => {
hour: 2,
minute: 40,
};
const { hours, minutes } = generateTime('en-US', today, 'h12', undefined, max);
const { hours, minutes } = generateTime(today, 'h12', undefined, max);
expect(hours.length).toEqual(12);
expect(minutes.length).toEqual(60);
@@ -262,7 +141,7 @@ describe('generateTime()', () => {
hour: 2,
minute: 40,
};
const { hours, minutes } = generateTime('en-US', today, 'h12', min);
const { hours, minutes } = generateTime(today, 'h12', min);
expect(hours.length).toEqual(0);
expect(minutes.length).toEqual(0);
@@ -282,7 +161,7 @@ describe('generateTime()', () => {
hour: 2,
minute: 40,
};
const { hours, minutes } = generateTime('en-US', today, 'h12', undefined, max);
const { hours, minutes } = generateTime(today, 'h12', undefined, max);
expect(hours.length).toEqual(0);
expect(minutes.length).toEqual(0);
@@ -306,7 +185,7 @@ describe('generateTime()', () => {
year: 2021,
};
const { hours, minutes } = generateTime('en-US', today, 'h12', min, max);
const { hours, minutes } = generateTime(today, 'h12', min, max);
expect(hours.length).toEqual(12);
expect(minutes.length).toEqual(60);
@@ -320,7 +199,7 @@ describe('generateTime()', () => {
minute: 43,
};
const { hours, minutes } = generateTime('en-US', today, 'h12', undefined, undefined, [1, 2, 3], [10, 15, 20]);
const { hours, minutes } = generateTime(today, 'h12', undefined, undefined, [1, 2, 3], [10, 15, 20]);
expect(hours).toStrictEqual([1, 2, 3]);
expect(minutes).toStrictEqual([10, 15, 20]);
@@ -350,7 +229,7 @@ describe('generateTime()', () => {
minute: 14,
};
const { am, pm } = generateTime('en-US', today, 'h12', min, max);
const { am, pm } = generateTime(today, 'h12', min, max);
expect(am).toBe(true);
expect(pm).toBe(true);
@@ -374,7 +253,7 @@ describe('generateTime()', () => {
minute: 50,
};
const { hours } = generateTime('en-US', refValue, 'h23', minParts);
const { hours } = generateTime(refValue, 'h23', minParts);
expect(hours).toStrictEqual([19, 20, 21, 22, 23]);
});
@@ -397,7 +276,7 @@ describe('generateTime()', () => {
minute: 30,
};
const { hours, minutes } = generateTime('en-US', refValue, 'h23', minParts);
const { hours, minutes } = generateTime(refValue, 'h23', minParts);
expect(hours).toStrictEqual([19, 20, 21, 22, 23]);
expect(minutes.length).toEqual(60);
@@ -429,7 +308,7 @@ describe('generateTime()', () => {
minute: 40,
};
const { hours } = generateTime('en-US', refValue, 'h23', minParts, maxParts);
const { hours } = generateTime(refValue, 'h23', minParts, maxParts);
expect(hours).toStrictEqual([19, 20]);
});
@@ -451,7 +330,7 @@ describe('generateTime()', () => {
minute: 2,
};
const { minutes } = generateTime('en-US', refValue, 'h23', undefined, maxParts);
const { minutes } = generateTime(refValue, 'h23', undefined, maxParts);
expect(minutes).toStrictEqual([0, 1, 2]);
});
@@ -473,7 +352,7 @@ describe('generateTime()', () => {
minute: 2,
};
const { minutes } = generateTime('en-US', refValue, 'h23', undefined, maxParts);
const { minutes } = generateTime(refValue, 'h23', undefined, maxParts);
expect(minutes.length).toEqual(60);
});

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -56,17 +56,11 @@ describe('getMonthAndDay()', () => {
describe('getFormattedHour()', () => {
it('should only add padding if using 24 hour time', () => {
expect(getFormattedHour(1, 'h11')).toEqual('1');
expect(getFormattedHour(1, 'h12')).toEqual('1');
expect(getFormattedHour(1, 'h23')).toEqual('01');
expect(getFormattedHour(1, 'h24')).toEqual('01');
});
expect(getFormattedHour(0, true)).toEqual('00');
expect(getFormattedHour(0, false)).toEqual('12');
it('should return correct hour value for hour cycle', () => {
expect(getFormattedHour(0, 'h11')).toEqual('0');
expect(getFormattedHour(0, 'h12')).toEqual('12');
expect(getFormattedHour(0, 'h23')).toEqual('00');
expect(getFormattedHour(0, 'h24')).toEqual('24');
expect(getFormattedHour(10, true)).toEqual('10');
expect(getFormattedHour(10, false)).toEqual('10');
});
});
@@ -117,7 +111,7 @@ describe('getLocalizedTime', () => {
minute: 40,
};
expect(getLocalizedTime('en-US', datetimeParts, 'h12')).toEqual('1:40 PM');
expect(getLocalizedTime('en-US', datetimeParts, false)).toEqual('1:40 PM');
});
it('should localize the time to AM', () => {
@@ -129,7 +123,7 @@ describe('getLocalizedTime', () => {
minute: 40,
};
expect(getLocalizedTime('en-US', datetimeParts, 'h12')).toEqual('9:40 AM');
expect(getLocalizedTime('en-US', datetimeParts, false)).toEqual('9:40 AM');
});
it('should avoid Chromium bug when using 12 hour time in a 24 hour locale', () => {
@@ -141,7 +135,7 @@ describe('getLocalizedTime', () => {
minute: 0,
};
expect(getLocalizedTime('en-GB', datetimeParts, 'h12')).toEqual('12:00 am');
expect(getLocalizedTime('en-GB', datetimeParts, false)).toEqual('12:00 am');
});
it('should parse time-only values correctly', () => {
const datetimeParts = {
@@ -149,7 +143,7 @@ describe('getLocalizedTime', () => {
minute: 40,
};
expect(getLocalizedTime('en-US', datetimeParts, 'h12')).toEqual('10:40 PM');
expect(getLocalizedTime('en-US', datetimeParts, 'h23')).toEqual('22:40');
expect(getLocalizedTime('en-US', datetimeParts, false)).toEqual('10:40 PM');
expect(getLocalizedTime('en-US', datetimeParts, true)).toEqual('22:40');
});
});

View File

@@ -1,4 +1,4 @@
import { isLeapYear, getNumDaysInMonth, is24Hour, isMonthFirstLocale, getHourCycle } from '../utils/helpers';
import { isLeapYear, getNumDaysInMonth, is24Hour, isMonthFirstLocale } from '../utils/helpers';
describe('daysInMonth()', () => {
it('should return correct days in month for month and year', () => {
@@ -37,29 +37,14 @@ describe('isLeapYear()', () => {
describe('is24Hour()', () => {
it('should return true if the locale uses 24 hour time', () => {
expect(is24Hour('h11')).toBe(false);
expect(is24Hour('h12')).toBe(false);
expect(is24Hour('h23')).toBe(true);
expect(is24Hour('h24')).toBe(true);
});
});
describe('getHourCycle()', () => {
it('should return the correct hour cycle', () => {
expect(getHourCycle('en-US')).toBe('h12');
expect(getHourCycle('en-US', 'h23')).toBe('h23');
expect(getHourCycle('en-US', 'h12')).toBe('h12');
expect(getHourCycle('en-US-u-hc-h23')).toBe('h23');
expect(getHourCycle('en-GB')).toBe('h23');
expect(getHourCycle('en-GB', 'h23')).toBe('h23');
expect(getHourCycle('en-GB', 'h12')).toBe('h12');
expect(getHourCycle('en-GB-u-hc-h12')).toBe('h12');
expect(getHourCycle('en-GB', 'h11')).toBe('h11');
expect(getHourCycle('en-GB-u-hc-h11')).toBe('h11');
expect(getHourCycle('en-GB', 'h24')).toBe('h24');
expect(getHourCycle('en-GB-u-hc-h24')).toBe('h24');
expect(is24Hour('en-US')).toBe(false);
expect(is24Hour('en-US', 'h23')).toBe(true);
expect(is24Hour('en-US', 'h12')).toBe(false);
expect(is24Hour('en-US-u-hc-h23')).toBe(true);
expect(is24Hour('en-GB')).toBe(true);
expect(is24Hour('en-GB', 'h23')).toBe(true);
expect(is24Hour('en-GB', 'h12')).toBe(false);
expect(is24Hour('en-GB-u-hc-h12')).toBe(false);
});
});

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -25,27 +25,5 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
const timeButton = page.locator('ion-datetime .time-body');
await expect(timeButton).toHaveText('4:30 PM');
});
test('should set the h11 hour cycle correctly', async ({ page }) => {
await page.setContent(
`
<ion-datetime hour-cycle="h11" value="2022-01-01T00:30:00"></ion-datetime>
`,
config
);
const timeButton = page.locator('ion-datetime .time-body');
await expect(timeButton).toHaveText('0:30 AM');
});
test('should set the h24 hour cycle correctly', async ({ page }) => {
await page.setContent(
`
<ion-datetime hour-cycle="h24" value="2022-01-01T00:30:00"></ion-datetime>
`,
config
);
const timeButton = page.locator('ion-datetime .time-body');
await expect(timeButton).toHaveText('24:30');
});
});
});

View File

@@ -51,14 +51,6 @@
<h2>h12 Hour cycle</h2>
<ion-datetime hour-cycle="h12"></ion-datetime>
</div>
<div class="grid-item">
<h2>h11 Hour cycle</h2>
<ion-datetime hour-cycle="h11"></ion-datetime>
</div>
<div class="grid-item">
<h2>h24 Hour cycle</h2>
<ion-datetime hour-cycle="h24"></ion-datetime>
</div>
<div class="grid-item">
<h2>h23 Hour cycle (Extension Tag)</h2>
<ion-datetime locale="en-US-u-hc-h23"></ion-datetime>

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,6 +1,6 @@
import type { Mode } from '../../../interface';
import type { PickerColumnItem } from '../../picker-column-internal/picker-column-internal-interfaces';
import type { DatetimeParts, DatetimeHourCycle } from '../datetime-interface';
import type { DatetimeParts } from '../datetime-interface';
import { isAfter, isBefore, isSameDay } from './comparison';
import {
@@ -11,7 +11,7 @@ import {
getTodayLabel,
getYear,
} from './format';
import { getNumDaysInMonth, is24Hour, getHourCycle } from './helpers';
import { getNumDaysInMonth, is24Hour } from './helpers';
import { getNextMonth, getPreviousMonth, getInternalHourValue } from './manipulation';
/**
@@ -44,19 +44,9 @@ const minutes = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
];
// h11 hour system uses 0-11. Midnight starts at 0:00am.
const hour11 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
// h12 hour system uses 0-12. Midnight starts at 12:00am.
const hour12 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
// h23 hour system uses 0-23. Midnight starts at 0:00.
const hour23 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23];
// h24 hour system uses 1-24. Midnight starts at 24:00.
const hour24 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0];
/**
* Given a locale and a mode,
* return an array with formatted days
@@ -135,42 +125,21 @@ export const getDaysOfMonth = (month: number, year: number, firstDayOfWeek: numb
return days;
};
/**
* Returns an array of pre-defined hour
* values based on the provided hourCycle.
*/
const getHourData = (hourCycle: DatetimeHourCycle) => {
switch (hourCycle) {
case 'h11':
return hour11;
case 'h12':
return hour12;
case 'h23':
return hour23;
case 'h24':
return hour24;
default:
throw new Error(`Invalid hour cycle "${hourCycle}"`);
}
};
/**
* Given a local, reference datetime parts and option
* max/min bound datetime parts, calculate the acceptable
* hour and minute values according to the bounds and locale.
*/
export const generateTime = (
locale: string,
refParts: DatetimeParts,
hourCycle: DatetimeHourCycle = 'h12',
hourCycle: 'h12' | 'h23' = 'h12',
minParts?: DatetimeParts,
maxParts?: DatetimeParts,
hourValues?: number[],
minuteValues?: number[]
) => {
const computedHourCycle = getHourCycle(locale, hourCycle);
const use24Hour = is24Hour(computedHourCycle);
let processedHours = getHourData(computedHourCycle);
const use24Hour = hourCycle === 'h23';
let processedHours = use24Hour ? hour23 : hour12;
let processedMinutes = minutes;
let isAMAllowed = true;
let isPMAllowed = true;
@@ -571,18 +540,16 @@ export const getCombinedDateColumnData = (
export const getTimeColumnsData = (
locale: string,
refParts: DatetimeParts,
hourCycle?: DatetimeHourCycle,
hourCycle?: 'h23' | 'h12',
minParts?: DatetimeParts,
maxParts?: DatetimeParts,
allowedHourValues?: number[],
allowedMinuteValues?: number[]
): { [key: string]: PickerColumnItem[] } => {
const computedHourCycle = getHourCycle(locale, hourCycle);
const use24Hour = is24Hour(computedHourCycle);
const use24Hour = is24Hour(locale, hourCycle);
const { hours, minutes, am, pm } = generateTime(
locale,
refParts,
computedHourCycle,
use24Hour ? 'h23' : 'h12',
minParts,
maxParts,
allowedHourValues,
@@ -591,7 +558,7 @@ export const getTimeColumnsData = (
const hoursItems = hours.map((hour) => {
return {
text: getFormattedHour(hour, computedHourCycle),
text: getFormattedHour(hour, use24Hour),
value: getInternalHourValue(hour, use24Hour, refParts.ampm),
};
});

View File

@@ -1,6 +1,5 @@
import type { DatetimeParts, DatetimeHourCycle } from '../datetime-interface';
import type { DatetimeParts } from '../datetime-interface';
import { is24Hour } from './helpers';
import { convertDataToISO } from './manipulation';
const getFormattedDayPeriod = (dayPeriod?: string) => {
@@ -11,7 +10,7 @@ const getFormattedDayPeriod = (dayPeriod?: string) => {
return dayPeriod.toUpperCase();
};
export const getLocalizedTime = (locale: string, refParts: DatetimeParts, hourCycle: DatetimeHourCycle): string => {
export const getLocalizedTime = (locale: string, refParts: DatetimeParts, use24Hour: boolean): string => {
const timeParts: Pick<DatetimeParts, 'hour' | 'minute'> = {
hour: refParts.hour,
minute: refParts.minute,
@@ -35,7 +34,7 @@ export const getLocalizedTime = (locale: string, refParts: DatetimeParts, hourCy
* We use hourCycle here instead of hour12 due to:
* https://bugs.chromium.org/p/chromium/issues/detail?id=1347316&q=hour12&can=2
*/
hourCycle,
hourCycle: use24Hour ? 'h23' : 'h12',
/**
* Setting Z at the end indicates that this
* date string is in the UTC time zone. This
@@ -84,36 +83,20 @@ export const addTimePadding = (value: number): string => {
* 12 hour times it ensures that
* hour 0 is formatted as '12'.
*/
export const getFormattedHour = (hour: number, hourCycle: DatetimeHourCycle): string => {
/**
* Midnight for h11 starts at 0:00am
* Midnight for h12 starts at 12:00am
* Midnight for h23 starts at 00:00
* Midnight for h24 starts at 24:00
*/
if (hour === 0) {
switch (hourCycle) {
case 'h11':
return '0';
case 'h12':
return '12';
case 'h23':
return '00';
case 'h24':
return '24';
default:
throw new Error(`Invalid hour cycle "${hourCycle}"`);
}
}
const use24Hour = is24Hour(hourCycle);
/**
* h23 and h24 use 24 hour times.
*/
export const getFormattedHour = (hour: number, use24Hour: boolean): string => {
if (use24Hour) {
return addTimePadding(hour);
}
/**
* If using 12 hour
* format, make sure hour
* 0 is formatted as '12'.
*/
if (hour === 0) {
return '12';
}
return hour.toString();
};

View File

@@ -1,4 +1,10 @@
import type { DatetimeHourCycle } from '../datetime-interface';
/**
* Typescript 4.x does not recognize hourCycle as a valid option.
* See https://github.com/microsoft/TypeScript/issues/34399.
*/
interface DatetimeFormatOptions extends Intl.ResolvedDateTimeFormatOptions {
hourCycle?: 'h11' | 'h12' | 'h23' | 'h24';
}
/**
* Determines if given year is a
@@ -10,19 +16,13 @@ export const isLeapYear = (year: number) => {
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
};
/**
* Determines the hour cycle for a user.
* If the hour cycle is explicitly defined, just use that.
* Otherwise, we try to derive it from either the specified
* locale extension tags or from Intl.DateTimeFormat directly.
*/
export const getHourCycle = (locale: string, hourCycle?: DatetimeHourCycle) => {
export const is24Hour = (locale: string, hourCycle?: 'h23' | 'h12') => {
/**
* If developer has explicitly enabled 24-hour time
* If developer has explicitly enabled h23 time
* then return early and do not look at the system default.
*/
if (hourCycle !== undefined) {
return hourCycle;
return hourCycle === 'h23';
}
/**
@@ -32,9 +32,9 @@ export const getHourCycle = (locale: string, hourCycle?: DatetimeHourCycle) => {
* option into the locale string. Example: `en-US-u-hc-h23`
*/
const formatted = new Intl.DateTimeFormat(locale, { hour: 'numeric' });
const options = formatted.resolvedOptions();
const options = formatted.resolvedOptions() as DatetimeFormatOptions;
if (options.hourCycle !== undefined) {
return options.hourCycle;
return options.hourCycle === 'h23';
}
/**
@@ -50,34 +50,7 @@ export const getHourCycle = (locale: string, hourCycle?: DatetimeHourCycle) => {
throw new Error('Hour value not found from DateTimeFormat');
}
/**
* Midnight for h11 starts at 0:00am
* Midnight for h12 starts at 12:00am
* Midnight for h23 starts at 00:00
* Midnight for h24 starts at 24:00
*/
switch (hour.value) {
case '0':
return 'h11';
case '12':
return 'h12';
case '00':
return 'h23';
case '24':
return 'h24';
default:
throw new Error(`Invalid hour cycle "${hourCycle}"`);
}
};
/**
* Determine if the hour cycle uses a 24-hour format.
* Returns true for h23 and h24. Returns false otherwise.
* If you don't know the hourCycle, use getHourCycle above
* and pass the result into this function.
*/
export const is24Hour = (hourCycle: DatetimeHourCycle) => {
return hourCycle === 'h23' || hourCycle === 'h24';
return hour.value === '00';
};
/**

View File

@@ -107,28 +107,3 @@
.header-collapse-condense-inactive.header-collapse-condense ion-toolbar.in-toolbar ion-buttons.buttons-collapse {
visibility: hidden;
}
/**
* The main header is only hidden once the collapsible large
* title is configured. As a result, if the main header loads
* before the collapsible large title is configured then the
* main header will be visible briefly before being hidden
* by the collapsible large title.
*
* The following selector ensures that any main header
* on a page with a collapsible large title is hidden
* before the collapsible large title is configured.
* Once the collapsible large title is configured the main
* header will have the ".header-collapse-main" class, and
* this selector will no longer apply.
*
* The :has(...) part of the selector ensures a couple things:
* 1. This will only apply within a page view since the content
* must be a subsequent-sibling of the header (~ ion-content).
* 2. This will only apply when that content has a collapse header (ion-header[collapse="condense"])
*
* We use opacity: 0 to avoid a layout shift.
*/
ion-header:not(.header-collapse-main):has(~ ion-content ion-header[collapse="condense"]) {
opacity: 0;
}

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -30,6 +30,7 @@ export interface MenuControllerI {
_setOpen(menu: MenuI, shouldOpen: boolean, animated: boolean): Promise<boolean>;
_register(menu: MenuI): void;
_unregister(menu: MenuI): void;
_setActiveMenu(menu: MenuI): void;
getMenus(): Promise<HTMLIonMenuElement[]>;
getOpenSync(): HTMLIonMenuElement | undefined;

View File

@@ -1,6 +1,7 @@
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Build, Component, Element, Event, Host, Listen, Method, Prop, State, Watch, h } from '@stencil/core';
import { getTimeGivenProgression } from '@utils/animation/cubic-bezier';
import { doc } from '@utils/browser';
import { GESTURE_CONTROLLER } from '@utils/gesture';
import type { Attributes } from '@utils/helpers';
import { inheritAriaAttributes, assert, clamp, isEndSide as isEnd } from '@utils/helpers';
@@ -39,15 +40,6 @@ export class Menu implements ComponentInterface, MenuI {
private blocker = GESTURE_CONTROLLER.createBlocker({ disableScroll: true });
private didLoad = false;
/**
* Flag used to determine if an open/close
* operation was cancelled. For example, if
* an app calls "menu.open" then disables the menu
* part way through the animation, then this would
* be considered a cancelled operation.
*/
private operationCancelled = false;
isAnimating = false;
width!: number;
_isOpen = false;
@@ -440,17 +432,6 @@ export class Menu implements ComponentInterface, MenuI {
await this.loadAnimation();
await this.startAnimation(shouldOpen, animated);
/**
* If the animation was cancelled then
* return false because the operation
* did not succeed.
*/
if (this.operationCancelled) {
this.operationCancelled = false;
return false;
}
this.afterAnimation(shouldOpen);
return true;
@@ -491,24 +472,18 @@ export class Menu implements ComponentInterface, MenuI {
const easingReverse = mode === 'ios' ? iosEasingReverse : mdEasingReverse;
const ani = (this.animation as Animation)!
.direction(isReversed ? 'reverse' : 'normal')
.easing(isReversed ? easingReverse : easing);
.easing(isReversed ? easingReverse : easing)
.onFinish(() => {
if (ani.getDirection() === 'reverse') {
ani.direction('normal');
}
});
if (animated) {
await ani.play();
} else {
ani.play({ sync: true });
}
/**
* We run this after the play invocation
* instead of using ani.onFinish so that
* multiple onFinish callbacks do not get
* run if an animation is played, stopped,
* and then played again.
*/
if (ani.getDirection() === 'reverse') {
ani.direction('normal');
}
}
private _isActive() {
@@ -668,6 +643,8 @@ export class Menu implements ComponentInterface, MenuI {
}
private afterAnimation(isOpen: boolean) {
assert(this.isAnimating, '_before() should be called while animating');
// keep opening/closing the menu disabled for a touch more yet
// only add listeners/css if it's enabled and isOpen
// and only remove listeners/css if it's not open
@@ -736,31 +713,36 @@ export class Menu implements ComponentInterface, MenuI {
this.gesture.enable(isActive && this.swipeGesture);
}
/**
* If the menu is disabled but it is still open
* then we should close the menu immediately.
* Additionally, if the menu is in the process
* of animating {open, close} and the menu is disabled
* then it should still be closed immediately.
*/
if (!isActive) {
/**
* It is possible to disable the menu while
* it is mid-animation. When this happens, we
* need to set the operationCancelled flag
* so that this._setOpen knows to return false
* and not run the "afterAnimation" callback.
*/
if (this.isAnimating) {
this.operationCancelled = true;
}
/**
* If the menu is disabled then we should
* forcibly close the menu even if it is open.
*/
this.afterAnimation(false);
// Close menu immediately
if (!isActive && this._isOpen) {
// close if this menu is open, and should not be enabled
this.forceClosing();
}
if (doc?.contains(this.el)) {
/**
* Only set the active menu if the menu element is
* present in the DOM. Otherwise if it was destructively
* re-hydrated (through Angular Universal), then ignore
* setting the removed node as the active menu.
*/
if (!this.disabled) {
menuController._setActiveMenu(this);
}
}
assert(!this.isAnimating, 'can not be animating');
}
private forceClosing() {
assert(this._isOpen, 'menu cannot be closed');
this.isAnimating = true;
const ani = (this.animation as Animation)!.direction('reverse');
ani.play({ sync: true });
this.afterAnimation(false);
}
render() {

View File

@@ -2,7 +2,7 @@
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Menu - Disable</title>
<title>Menu - Basic</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
@@ -12,30 +12,78 @@
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<script type="module">
import { menuController } from '../../../../dist/ionic/index.esm.js';
window.menuController = menuController;
</script>
</head>
<body>
<ion-app>
<ion-menu side="start" id="start-menu" menu-id="start-menu" content-id="main">
<ion-menu
side="start"
id="start-menu"
menu-id="start-menu"
content-id="main"
>
<ion-header>
<ion-toolbar color="primary">
<ion-title>Menu</ion-title>
<ion-title>Start Menu</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding"> Menu Content </ion-content>
<ion-content>
<ion-list>
<ion-item>
<ion-button id="start-menu-button">Button</ion-button>
</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
</ion-list>
</ion-content>
</ion-menu>
<div class="ion-page" id="main">
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-menu-button></ion-menu-button>
</ion-buttons>
<ion-title>Menu - Disable</ion-title>
<ion-title>Menu - Basic</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">Content</ion-content>
<ion-content class="ion-padding">
<ion-button expand="block" id="open-start" onclick="openStart()">Open Start Menu</ion-button>
<ion-button expand="block" id="open-end" onclick="openEnd()">Open End Menu</ion-button>
<ion-button expand="block" id="open-custom" onclick="openCustom()">Open Custom Menu</ion-button>
</ion-content>
</div>
</ion-app>
<script>
const menu = document.querySelector('ion-menu');
setTimeout(async () => {
menu.open();
setTimeout(() => {
menu.disabled = true;
}, 50);
}, 1000);
</script>
</body>
</html>

View File

@@ -1,66 +1,158 @@
import type { Locator } from '@playwright/test';
import { expect } from '@playwright/test';
import type { E2EPage, ScreenshotFn } from '@utils/test/playwright';
import { configs, test } from '@utils/test/playwright';
configs().forEach(({ title, config, screenshot }) => {
test.describe(title('menu: rendering'), () => {
test('should open selected menu by side', async ({ page }) => {
await page.goto(`/src/components/menu/test/basic`, config);
const startMenu = page.locator('[menu-id="start-menu"]');
const customMenu = page.locator('[menu-id="custom-menu"]');
const endMenu = page.locator('[menu-id="end-menu"]');
await testMenu(page, startMenu, 'start', screenshot);
await testMenu(page, customMenu, 'custom', screenshot);
await testMenu(page, endMenu, 'end', screenshot);
});
});
});
/**
* This behavior does not vary across modes/directions
*/
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
test.describe(title('menu: disable'), () => {
test.describe(title('menu: functionality'), () => {
test.beforeEach(async ({ page }) => {
await page.goto(`/src/components/menu/test/disable`, config);
await page.goto(`/src/components/menu/test/basic`, config);
});
test('should disable when menu is fully open', async ({ page }) => {
const logs: string[] = [];
test('should trap focus', async ({ page, skip, browserName }) => {
skip.browser('firefox', 'Firefox incorrectly allows keyboard focus to move to ion-content');
// TODO (FW-2979)
skip.browser('webkit', 'Safari 16 only allows text fields and pop-up menus to be focused.');
const ionDidOpen = await page.spyOnEvent('ionDidOpen');
page.on('console', (msg) => {
if (msg.type() === 'error') {
logs.push(msg.text());
}
});
await page.click('#open-start');
await ionDidOpen.next();
const menu = page.locator('ion-menu');
const button = page.locator('#start-menu-button');
// Should be visible on initial presentation
await menu.evaluate((el: HTMLIonMenuElement) => el.open());
await expect(menu).toBeVisible();
if (browserName === 'webkit') {
await page.keyboard.down('Alt');
}
// Disabling menu should hide it
await menu.evaluate((el: HTMLIonMenuElement) => (el.disabled = true));
await expect(menu).toBeHidden();
await page.keyboard.press('Tab');
// Re-enabling menu and opening it show make it visible
await menu.evaluate((el: HTMLIonMenuElement) => (el.disabled = false));
await menu.evaluate((el: HTMLIonMenuElement) => el.open());
await expect(menu).toBeVisible();
await expect(button).toBeFocused();
expect(logs.length).toBe(0);
await page.keyboard.press('Tab');
if (browserName === 'webkit') {
await page.keyboard.up('Alt');
}
await expect(button).toBeFocused();
});
test('should disable when menu is animating', async ({ page }) => {
const logs: string[] = [];
test('should preserve scroll position', async ({ page, skip }) => {
skip.browser('firefox', 'Firefox does not preserve scroll position');
page.on('console', (msg) => {
if (msg.type() === 'error') {
logs.push(msg.text());
}
const ionDidOpen = await page.spyOnEvent('ionDidOpen');
await page.click('#open-start');
await ionDidOpen.next();
await page.locator('#start-menu ion-content').evaluate(async (el: HTMLIonContentElement) => {
await el.scrollToPoint(0, 200);
});
const menu = page.locator('ion-menu');
// Opening and quickly disabling menu should hide it
menu.evaluate((el: HTMLIonMenuElement) => {
el.open();
setTimeout(() => (el.disabled = true), 0);
await page.locator('#start-menu').evaluate(async (el: HTMLIonMenuElement) => {
await el.close();
});
await expect(menu).toBeHidden();
// Re-enabling menu and opening it show make it visible
await menu.evaluate((el: HTMLIonMenuElement) => (el.disabled = false));
await menu.evaluate((el: HTMLIonMenuElement) => el.open());
await expect(menu).toBeVisible();
await page.click('#open-start');
await ionDidOpen.next();
expect(logs.length).toBe(0);
const scrollTop = await page.locator('#start-menu ion-content').evaluate(async (el: HTMLIonContentElement) => {
const contentScrollEl = await el.getScrollElement();
return contentScrollEl.scrollTop;
});
await expect(scrollTop).toBe(200);
});
});
});
/**
* This behavior does not vary across modes/directions.
*/
configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
test.describe(title('menu: reactive side'), () => {
test.beforeEach(async ({ page }) => {
await page.goto(`/src/components/menu/test/basic`, config);
});
test('should render on the correct side when side is changed dynamically', async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/25601',
});
const ionDidOpen = await page.spyOnEvent('ionDidOpen');
const ionDidClose = await page.spyOnEvent('ionDidClose');
await page.locator('[menu-id="start-menu"]').evaluate(async (el: HTMLIonMenuElement) => {
el.side = 'end';
});
await page.click('#open-start');
await ionDidOpen.next();
await expect(page).toHaveScreenshot(screenshot(`menu-basic-side-toggled`));
await page.locator('[menu-id="start-menu"]').evaluate(async (el: HTMLIonMenuElement) => {
await el.close();
});
await ionDidClose.next();
});
test('should render on the correct side when document direction is changed dynamically', async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/25601',
});
const ionDidOpen = await page.spyOnEvent('ionDidOpen');
const ionDidClose = await page.spyOnEvent('ionDidClose');
await page.evaluate(() => {
document.dir = 'rtl';
});
await page.click('#open-start');
await ionDidOpen.next();
await expect(page).toHaveScreenshot(screenshot(`menu-basic-doc-dir-toggled`));
await page.locator('[menu-id="start-menu"]').evaluate(async (el: HTMLIonMenuElement) => {
await el.close();
});
await ionDidClose.next();
});
});
});
async function testMenu(page: E2EPage, menu: Locator, menuId: string, screenshot: ScreenshotFn) {
const ionDidOpen = await page.spyOnEvent('ionDidOpen');
const ionDidClose = await page.spyOnEvent('ionDidClose');
await page.click(`#open-${menuId}`);
await ionDidOpen.next();
await expect(menu).toHaveClass(/show-menu/);
await expect(page).toHaveScreenshot(screenshot(`menu-basic-${menuId}`));
await menu.evaluate(async (el: HTMLIonMenuElement) => {
await el.close();
});
await ionDidClose.next();
}

View File

@@ -1,74 +0,0 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Menu - Multiple</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
/>
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<script type="module">
import { menuController } from '../../../../dist/ionic/index.esm.js';
window.menuController = menuController;
</script>
</head>
<body>
<ion-app>
<ion-menu side="start" menu-id="primary-menu" id="primary-menu" content-id="main">
<ion-header>
<ion-toolbar color="primary">
<ion-title>Primary</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding"> Menu Content </ion-content>
</ion-menu>
<ion-menu side="start" menu-id="secondary-menu" id="secondary-menu" content-id="main">
<ion-header>
<ion-toolbar color="secondary">
<ion-title>Secondary</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding"> Menu Content </ion-content>
</ion-menu>
<ion-menu disabled="true" side="end" menu-id="success-menu" id="success-menu" content-id="main">
<ion-header>
<ion-toolbar color="success">
<ion-title>Success</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding"> Menu Content </ion-content>
</ion-menu>
<ion-menu disabled="true" side="end" menu-id="danger-menu" id="danger-menu" content-id="main">
<ion-header>
<ion-toolbar color="danger">
<ion-title>Danger</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding"> Menu Content </ion-content>
</ion-menu>
<div class="ion-page" id="main">
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-menu-button menu="primary-menu" color="primary"></ion-menu-button>
<ion-menu-button menu="secondary-menu" color="secondary"></ion-menu-button>
</ion-buttons>
<ion-title>Menu - Multiple</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">Content</ion-content>
</div>
</ion-app>
</body>
<scrip
</html>

View File

@@ -1,79 +0,0 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';
/**
* This behavior does not vary across modes/directions
*/
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
test.describe(title('menu: multiple'), () => {
test.beforeEach(async ({ page }, testInfo) => {
testInfo.annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/18974',
});
await page.goto(`/src/components/menu/test/multiple`, config);
});
test('should present each menu on the same side individually', async ({ page }) => {
const primaryMenu = page.locator('ion-menu#primary-menu');
const secondaryMenu = page.locator('ion-menu#secondary-menu');
await primaryMenu.evaluate((el: HTMLIonMenuElement) => el.open());
await expect(primaryMenu).toBeVisible();
await primaryMenu.evaluate((el: HTMLIonMenuElement) => el.close());
await expect(primaryMenu).toBeHidden();
await secondaryMenu.evaluate((el: HTMLIonMenuElement) => el.open());
await expect(secondaryMenu).toBeVisible();
await secondaryMenu.evaluate((el: HTMLIonMenuElement) => el.close());
await expect(secondaryMenu).toBeHidden();
});
test('should close first menu when showing another menu on same side', async ({ page }) => {
const primaryMenu = page.locator('ion-menu#primary-menu');
const secondaryMenu = page.locator('ion-menu#secondary-menu');
await primaryMenu.evaluate((el: HTMLIonMenuElement) => el.open());
await expect(primaryMenu).toBeVisible();
await secondaryMenu.evaluate((el: HTMLIonMenuElement) => el.open());
await expect(primaryMenu).toBeHidden();
await expect(secondaryMenu).toBeVisible();
});
test('passing side to the menuController when multiple menus have that side should result in a warning', async ({
page,
}) => {
const logs: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'warning') {
logs.push(msg.text());
}
});
await page.evaluate(() => (window as any).menuController.open('start'));
expect(logs.length).toBe(1);
});
test('passing side to the menuController when multiple disabled menus have that side should result in a warning', async ({
page,
}) => {
const logs: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'warning') {
logs.push(msg.text());
}
});
await page.evaluate(() => (window as any).menuController.open('end'));
expect(logs.length).toBe(1);
});
});
});

View File

@@ -6,5 +6,6 @@
position: absolute;
contain: layout size style;
overflow: hidden;
z-index: $z-index-page-container;
}

View File

@@ -901,7 +901,6 @@ export class Nav implements NavOutlet {
: undefined;
const mode = getIonMode(this);
const enteringEl = enteringView.element!;
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
const leavingEl = leavingView && leavingView.element!;
const animationOpts: TransitionOptions = {
mode,

View File

@@ -6,5 +6,6 @@
position: absolute;
contain: layout size style;
overflow: hidden;
z-index: $z-index-page-container;
}

View File

@@ -3,7 +3,6 @@ import { Component, Element, Event, Method, Prop, Watch, h } from '@stencil/core
import { getTimeGivenProgression } from '@utils/animation/cubic-bezier';
import { attachComponent, detachComponent } from '@utils/framework-delegate';
import { shallowEqualStringMap, hasLazyBuild } from '@utils/helpers';
import { createLockController } from '@utils/lock-controller';
import { transition } from '@utils/transition';
import { config } from '../../global/config';
@@ -25,11 +24,11 @@ import type { RouteID, RouterDirection, RouteWrite, NavOutlet } from '../router/
shadow: true,
})
export class RouterOutlet implements ComponentInterface, NavOutlet {
private readonly lockController = createLockController();
private activeEl: HTMLElement | undefined;
// TODO(FW-2832): types
private activeComponent: any;
private activeParams: any;
private waitPromise?: Promise<void>;
private gesture?: Gesture;
private ani?: Animation;
private gestureOrAnimationInProgress = false;
@@ -141,7 +140,7 @@ export class RouterOutlet implements ComponentInterface, NavOutlet {
leavingEl: HTMLElement | undefined,
opts?: RouterOutletOptions
): Promise<boolean> {
const unlock = await this.lockController.lock();
const unlock = await this.lock();
let changed = false;
try {
changed = await this.transition(enteringEl, leavingEl, opts);
@@ -286,6 +285,18 @@ export class RouterOutlet implements ComponentInterface, NavOutlet {
return true;
}
// TODO: FW-5048 - Remove this code in favor of using lock controller from utils
private async lock() {
const p = this.waitPromise;
let resolve!: () => void;
this.waitPromise = new Promise((r) => (resolve = r));
if (p !== undefined) {
await p;
}
return resolve;
}
render() {
return <slot></slot>;
}

View File

@@ -312,7 +312,6 @@ export class Router implements ComponentInterface {
const routes = readRoutes(this.el);
const fromChain = findChainForSegments(from, routes);
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
const beforeLeaveHook = fromChain && fromChain[fromChain.length - 1].beforeLeave;
const canLeave = beforeLeaveHook ? await beforeLeaveHook() : true;
@@ -321,7 +320,6 @@ export class Router implements ComponentInterface {
}
const toChain = findChainForSegments(to, routes);
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
const beforeEnterHook = toChain && toChain[toChain.length - 1].beforeEnter;
return beforeEnterHook ? beforeEnterHook() : true;

View File

@@ -48,7 +48,6 @@ export const chainToSegments = (chain: RouteChain): string[] | null => {
for (const route of chain) {
for (const segment of route.segments) {
if (segment[0] === ':') {
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
const param = route.params && route.params[segment.slice(1)];
if (!param) {
return null;

View File

@@ -1,7 +1,6 @@
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Listen, Prop, State, Watch, h, writeTask } from '@stencil/core';
import type { Gesture, GestureDetail } from '@utils/gesture';
import { raf } from '@utils/helpers';
import { isRTL } from '@utils/rtl';
import { createColorClasses, hostContext } from '@utils/theme';
@@ -84,7 +83,31 @@ export class Segment implements ComponentInterface {
* Used by `ion-segment-button` to determine if the button should be checked.
*/
this.ionSelect.emit({ value });
this.scrollActiveButtonIntoView();
if (this.scrollable) {
const buttons = this.getButtons();
const activeButton = buttons.find((button) => button.value === value);
if (activeButton !== undefined) {
/**
* Scrollable segment buttons should be
* centered within the view including
* buttons that are partially offscreen.
*/
activeButton.scrollIntoView({
behavior: 'smooth',
inline: 'center',
/**
* Segment should scroll on the
* horizontal axis. `block: 'nearest'`
* ensures that the vertical axis
* does not scroll if the segment
* as a whole is already in view.
*/
block: 'nearest',
});
}
}
}
/**
@@ -140,14 +163,6 @@ export class Segment implements ComponentInterface {
async componentDidLoad() {
this.setCheckedClasses();
/**
* We need to wait for the buttons to all be rendered
* before we can scroll.
*/
raf(() => {
this.scrollActiveButtonIntoView();
});
this.gesture = (await import('../../utils/gesture')).createGesture({
el: this.el,
gestureName: 'segment',
@@ -305,35 +320,6 @@ export class Segment implements ComponentInterface {
}
}
private scrollActiveButtonIntoView() {
const { scrollable, value } = this;
if (scrollable) {
const buttons = this.getButtons();
const activeButton = buttons.find((button) => button.value === value);
if (activeButton !== undefined) {
/**
* Scrollable segment buttons should be
* centered within the view including
* buttons that are partially offscreen.
*/
activeButton.scrollIntoView({
behavior: 'smooth',
inline: 'center',
/**
* Segment should scroll on the
* horizontal axis. `block: 'nearest'`
* ensures that the vertical axis
* does not scroll if the segment
* as a whole is already in view.
*/
block: 'nearest',
});
}
}
}
private setNextIndex(detail: GestureDetail, isEnd = false) {
const rtl = isRTL(this.el);
const activated = this.activated;

View File

@@ -2,7 +2,7 @@ import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';
configs().forEach(({ title, screenshot, config }) => {
test.describe(title('segment: scrollable (rendering)'), () => {
test.describe(title('segment: scrollable'), () => {
test('should not have visual regressions', async ({ page }) => {
await page.setContent(
`
@@ -45,47 +45,3 @@ configs().forEach(({ title, screenshot, config }) => {
});
});
});
configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
test.describe(title('segment: scrollable (functionality)'), () => {
test('should scroll active button into view when value is already set', async ({ page }) => {
await page.setContent(
`
<ion-segment scrollable="true" value="8">
<ion-segment-button value="1">
<ion-label>First</ion-label>
</ion-segment-button>
<ion-segment-button value="2">
<ion-label>Second</ion-label>
</ion-segment-button>
<ion-segment-button value="3">
<ion-label>Third</ion-label>
</ion-segment-button>
<ion-segment-button value="4">
<ion-label>Fourth</ion-label>
</ion-segment-button>
<ion-segment-button value="5">
<ion-label>Fifth</ion-label>
</ion-segment-button>
<ion-segment-button value="6">
<ion-label>Sixth</ion-label>
</ion-segment-button>
<ion-segment-button value="7">
<ion-label>Seventh</ion-label>
</ion-segment-button>
<ion-segment-button id="activeButton" value="8">
<ion-label>Eighth</ion-label>
</ion-segment-button>
<ion-segment-button value="9">
<ion-label>Ninth</ion-label>
</ion-segment-button>
</ion-segment>
`,
config
);
const activeButton = page.locator('#activeButton');
await expect(activeButton).toBeInViewport();
});
});
});

View File

@@ -1041,7 +1041,7 @@ Developers can use the "legacy" property to continue using the legacy form marku
disabled={disabled}
id={inputId}
aria-label={this.ariaLabel}
aria-haspopup="dialog"
aria-haspopup="listbox"
aria-expanded={`${isExpanded}`}
onFocus={this.onFocus}
onBlur={this.onBlur}

View File

@@ -57,14 +57,6 @@ export class TabBar implements ComponentInterface {
/** @internal */
@Event() ionTabBarChanged!: EventEmitter<TabBarChangedEventDetail>;
/**
* @internal
* This event is used in IonContent to correctly
* calculate the fullscreen content offsets
* when IonTabBar is used.
*/
@Event() ionTabBarLoaded!: EventEmitter<void>;
componentWillLoad() {
this.selectedTabChanged();
}
@@ -90,10 +82,6 @@ export class TabBar implements ComponentInterface {
}
}
componentDidLoad() {
this.ionTabBarLoaded.emit();
}
render() {
const { color, translucent, keyboardVisible } = this;
const mode = getIonMode(this);

View File

@@ -39,40 +39,5 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, screenshot, c
await expect(tabBar).toHaveScreenshot(screenshot(`tab-bar-translucent`));
});
test('should render translucent tab bar even when wrapped in a page container', async ({ page }) => {
await page.setContent(
`
<style>
ion-content {
--background: linear-gradient(to right, orange, yellow, green, cyan, blue, violet);
}
</style>
<ion-tabs>
<div class="ion-page">
<ion-content fullscreen="true">My Content</ion-content>
</div>
<ion-tab-bar slot="bottom" translucent="true" selected-tab="1">
<ion-tab-button tab="1">
<ion-label>Recents</ion-label>
</ion-tab-button>
<ion-tab-button tab="2">
<ion-label>Favorites</ion-label>
<ion-badge>23</ion-badge>
</ion-tab-button>
<ion-tab-button tab="3">
<ion-label>Settings</ion-label>
</ion-tab-button>
</ion-tab-bar>
</ion-tabs>
`,
config
);
const tabBar = page.locator('ion-tab-bar');
await expect(tabBar).toHaveScreenshot(screenshot(`tab-bar-translucent-container`));
});
});
});

View File

@@ -2,19 +2,20 @@ import { createAnimation } from '@utils/animation/animation';
import { getElementRoot } from '@utils/helpers';
import type { Animation } from '../../../interface';
import type { ToastPresentOptions } from '../toast-interface';
/**
* iOS Toast Enter Animation
*/
export const iosEnterAnimation = (baseEl: HTMLElement, opts: ToastPresentOptions): Animation => {
export const iosEnterAnimation = (baseEl: HTMLElement, position: string): Animation => {
const baseAnimation = createAnimation();
const wrapperAnimation = createAnimation();
const { position, top, bottom } = opts;
const root = getElementRoot(baseEl);
const wrapperEl = root.querySelector('.toast-wrapper') as HTMLElement;
const bottom = `calc(-10px - var(--ion-safe-area-bottom, 0px))`;
const top = `calc(10px + var(--ion-safe-area-top, 0px))`;
wrapperAnimation.addElement(wrapperEl);
switch (position) {

View File

@@ -1,19 +1,21 @@
import { createAnimation } from '@utils/animation/animation';
import { getElementRoot } from '@utils/helpers';
import type { Animation, ToastDismissOptions } from '../../../interface';
import type { Animation } from '../../../interface';
/**
* iOS Toast Leave Animation
*/
export const iosLeaveAnimation = (baseEl: HTMLElement, opts: ToastDismissOptions): Animation => {
export const iosLeaveAnimation = (baseEl: HTMLElement, position: string): Animation => {
const baseAnimation = createAnimation();
const wrapperAnimation = createAnimation();
const { position, top, bottom } = opts;
const root = getElementRoot(baseEl);
const wrapperEl = root.querySelector('.toast-wrapper') as HTMLElement;
const bottom = `calc(-10px - var(--ion-safe-area-bottom, 0px))`;
const top = `calc(10px + var(--ion-safe-area-top, 0px))`;
wrapperAnimation.addElement(wrapperEl);
switch (position) {

View File

@@ -2,19 +2,20 @@ import { createAnimation } from '@utils/animation/animation';
import { getElementRoot } from '@utils/helpers';
import type { Animation } from '../../../interface';
import type { ToastPresentOptions } from '../toast-interface';
/**
* MD Toast Enter Animation
*/
export const mdEnterAnimation = (baseEl: HTMLElement, opts: ToastPresentOptions): Animation => {
export const mdEnterAnimation = (baseEl: HTMLElement, position: string): Animation => {
const baseAnimation = createAnimation();
const wrapperAnimation = createAnimation();
const { position, top, bottom } = opts;
const root = getElementRoot(baseEl);
const wrapperEl = root.querySelector('.toast-wrapper') as HTMLElement;
const bottom = `calc(8px + var(--ion-safe-area-bottom, 0px))`;
const top = `calc(8px + var(--ion-safe-area-top, 0px))`;
wrapperAnimation.addElement(wrapperEl);
switch (position) {

View File

@@ -1,95 +0,0 @@
import { win } from '@utils/browser';
import { printIonWarning } from '@utils/logging';
import type { Mode } from 'src/interface';
import type { ToastAnimationPosition, ToastPosition } from '../toast-interface';
/**
* Calculate the CSS top and bottom position of the toast, to be used
* as starting points for the animation keyframes.
*
* Note that MD animates bottom-positioned toasts using style.bottom,
* which calculates from the bottom edge of the screen, while iOS uses
* translateY, which calculates from the top edge of the screen. This
* is why the bottom calculates differ slightly between modes.
*
* @param position The value of the toast's position prop.
* @param positionAnchor The element the toast should be anchored to,
* if applicable.
* @param mode The toast component's mode (md, ios, etc).
* @param toast A reference to the toast element itself.
*/
export function getAnimationPosition(
position: ToastPosition,
positionAnchor: HTMLElement | undefined,
mode: Mode,
toast: HTMLElement
): ToastAnimationPosition {
/**
* Start with a predefined offset from the edge the toast will be
* positioned relative to, whether on the screen or anchor element.
*/
let offset: number;
if (mode === 'md') {
offset = 8;
} else {
offset = position === 'top' ? 10 : -10;
}
/**
* If positionAnchor is defined, add in the distance from the target
* screen edge to the target anchor edge. For position="top", the
* bottom anchor edge is targeted. For position="bottom", the top
* anchor edge is targeted.
*/
if (positionAnchor && win) {
warnIfAnchorIsHidden(positionAnchor, toast);
const box = positionAnchor.getBoundingClientRect();
if (position === 'top') {
offset += box.bottom;
} else if (position === 'bottom') {
/**
* Just box.top is the distance from the top edge of the screen
* to the top edge of the anchor. We want to calculate from the
* bottom edge of the screen instead.
*/
if (mode === 'md') {
offset += win.innerHeight - box.top;
} else {
offset -= win.innerHeight - box.top;
}
}
/**
* We don't include safe area here because that should already be
* accounted for when checking the position of the anchor.
*/
return {
top: `${offset}px`,
bottom: `${offset}px`,
};
} else {
return {
top: `calc(${offset}px + var(--ion-safe-area-top, 0px))`,
bottom:
mode === 'md'
? `calc(${offset}px + var(--ion-safe-area-bottom, 0px))`
: `calc(${offset}px - var(--ion-safe-area-bottom, 0px))`,
};
}
}
/**
* If the anchor element is hidden, getBoundingClientRect()
* will return all 0s for it, which can cause unexpected
* results in the position calculation when animating.
*/
function warnIfAnchorIsHidden(positionAnchor: HTMLElement, toast: HTMLElement) {
if (positionAnchor.offsetParent === null) {
printIonWarning(
'The positionAnchor element for ion-toast was found in the DOM, but appears to be hidden. This may lead to unexpected positioning of the toast.',
toast
);
}
}

Some files were not shown because too many files have changed in this diff Show More